# ISO 27001 MVP 10 項措施 - 完整實作指南

## 📝 1. 資訊安全政策（ISO27001_006）

### **實作細節**

#### 1.1 政策架構設計

```
資訊安全政策體系
├── Level 1: 資訊安全總政策（Policy）
│   ├── 高階主管核准
│   ├── 適用全公司
│   └── 每年審查
│
├── Level 2: 資訊安全程序（Procedure）
│   ├── 存取控制程序
│   ├── 變更管理程序
│   ├── 備份程序
│   └── 事件處理程序
│
└── Level 3: 作業指引（Work Instruction）
    ├── 密碼設定指引
    ├── 備份操作指引
    └── 系統強化指引
```

#### 1.2 政策制定步驟

**Step 1: 需求分析（1 週）**
```python
# 需求收集檢查表
requirements = {
    "法規要求": [
        "個人資料保護法",
        "電子簽章法",
        "營業秘密法",
        "產業特定法規（如：金融、醫療）"
    ],
    "客戶要求": [
        "客戶合約的資安條款",
        "供應商稽核要求",
        "國際客戶的合規要求"
    ],
    "業務需求": [
        "遠距工作政策",
        "BYOD（自帶設備）政策",
        "雲端服務使用",
        "第三方存取"
    ],
    "風險考量": [
        "過去發生的資安事件",
        "產業常見威脅",
        "技術環境特性"
    ]
}
```

**Step 2: 起草政策（1 週）**
- 參考 ISO 27001 要求
- 參考業界範本（調整為自己公司的內容）
- 諮詢法務/人資意見

**Step 3: 內部審查（3-5 天）**
```
審查會議參與者：
- 總經理/執行長
- 資訊主管
- 人資主管
- 法務主管
- 各部門代表
```

**Step 4: 核准發布（1 天）**
- 總經理簽核
- 發布通知全員
- 舉辦說明會

---

### **文件範本**

#### 範本 1：資訊安全總政策

```markdown
【公司名稱】資訊安全政策
版本：1.0
發布日期：2025-01-15
核准者：總經理 [簽名]

==========================================

一、目的
本政策旨在保護【公司名稱】之資訊資產，確保資訊的機密性、完整性及可用性，
以支持業務目標達成並符合法規要求。

二、適用範圍
本政策適用於：
1. 【公司名稱】全體員工（含正職、約聘、派遣、實習生）
2. 所有承包商、供應商、顧問及第三方人員
3. 所有資訊系統、設備、網路及資料
4. 所有營運據點（台北總部、台中分公司、雲端環境）

三、資訊安全目標
1. 保護客戶資料及營業秘密，防止未經授權之存取、使用或洩露
2. 確保關鍵業務系統可用性達 99.5% 以上
3. 符合個資法、營業秘密法及客戶合約要求
4. 建立資安事件 2 小時內通報、24 小時內處理之應變機制
5. 每年至少進行 1 次全員資安教育訓練

四、資訊安全原則

4.1 機密性（Confidentiality）
- 資訊僅限授權人員存取
- 依資料敏感度分級管理
- 傳輸及儲存時採用加密保護

4.2 完整性（Integrity）
- 防止資料遭未經授權之竄改或刪除
- 建立變更控制及審查機制
- 定期驗證資料正確性

4.3 可用性（Availability）
- 確保授權使用者可及時存取資訊
- 建立備份及災難復原機制
- 監控系統效能及容量

4.4 法規遵循
- 遵守所有適用之法律、法規及合約義務
- 定期審查法規變更
- 接受外部稽核及檢查

五、角色與責任

5.1 最高管理階層
- 核准資訊安全政策
- 提供資源支持
- 主持年度管理審查

5.2 資訊安全委員會
- 主任委員：總經理
- 委員：各部門主管
- 每季開會檢討資安執行狀況

5.3 資訊安全主管（CISO）
- 統籌資安管理制度
- 監督政策執行
- 處理資安事件

5.4 資訊部門
- 實施技術性控制措施
- 維護系統安全
- 提供技術支援

5.5 全體員工
- 遵守資訊安全政策
- 妥善保管帳號密碼
- 通報可疑事件

六、違反政策之處置
違反本政策者，依情節輕重處置：
- 口頭警告
- 書面警告
- 記過處分
- 停職
- 解僱
- 法律追訴

七、政策審查
本政策每年至少審查一次，或發生下列情況時立即審查：
- 重大資安事件發生
- 法規或業務環境重大變更
- 內外部稽核建議
- 管理階層指示

八、相關文件
- 存取控制程序
- 密碼管理程序
- 備份程序
- 事件處理程序
- 可接受使用政策

九、政策發布與溝通
本政策發布於公司內部網站，全體人員應閱讀並簽署「資訊安全政策
聲明書」。

==========================================
核准簽章：

總經理：_______________ 日期：___________
資訊主管：_____________ 日期：___________
```

#### 範本 2：員工資訊安全聲明書

```markdown
【公司名稱】資訊安全政策聲明書

本人 ____________（姓名），員工編號 ____________，
任職於 ____________ 部門，職稱為 ____________。

茲聲明：

一、本人已詳細閱讀並充分了解【公司名稱】之資訊安全政策。

二、本人承諾遵守下列事項：
   □ 妥善保管帳號密碼，不與他人共用
   □ 僅存取業務所需之資訊系統及資料
   □ 不將公司資料攜出或傳送至未經授權之處
   □ 不安裝未經許可之軟體
   □ 發現可疑事件立即通報
   □ 離職時歸還所有公司資產及刪除個人設備中的公司資料

三、本人了解違反資訊安全政策將受懲處，情節重大者將依法追訴。

四、本聲明書一式兩份，公司與本人各執一份。


員工簽名：_______________ 

日期：_______________ 


人資部門簽收：_______________ 日期：_______________
```

---

### **系統功能設計**

#### 功能模組：文件管理系統

```python
# Django Models 設計

from django.db import models
from django.contrib.auth.models import User

class DocumentCategory(models.Model):
    """文件分類"""
    name = models.CharField(max_length=100)  # 政策、程序、指引、表單
    order = models.IntegerField(default=0)
    
    class Meta:
        ordering = ['order', 'name']

class Document(models.Model):
    """文件主表"""
    STATUS_CHOICES = [
        ('draft', '草稿'),
        ('review', '審查中'),
        ('approved', '已核准'),
        ('published', '已發布'),
        ('archived', '已歸檔'),
    ]
    
    document_number = models.CharField(max_length=50, unique=True)  # 文件編號
    title = models.CharField(max_length=200)
    category = models.ForeignKey(DocumentCategory, on_delete=models.PROTECT)
    version = models.CharField(max_length=20, default='1.0')
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
    
    # 內容
    content = models.TextField()  # 富文本內容
    
    # 變數範本（用於自動替換）
    template_variables = models.JSONField(default=dict, blank=True)
    # 例如: {"company_name": "{{company_name}}", "date": "{{date}}"}
    
    # 生命週期
    created_by = models.ForeignKey(User, related_name='documents_created', on_delete=models.PROTECT)
    created_at = models.DateTimeField(auto_now_add=True)
    
    reviewed_by = models.ForeignKey(User, related_name='documents_reviewed', 
                                   on_delete=models.SET_NULL, null=True, blank=True)
    reviewed_at = models.DateTimeField(null=True, blank=True)
    
    approved_by = models.ForeignKey(User, related_name='documents_approved', 
                                   on_delete=models.SET_NULL, null=True, blank=True)
    approved_at = models.DateTimeField(null=True, blank=True)
    
    published_at = models.DateTimeField(null=True, blank=True)
    
    # 審查週期
    review_cycle_months = models.IntegerField(default=12)  # 預設每年審查
    next_review_date = models.DateField(null=True, blank=True)
    
    # 關聯
    related_controls = models.ManyToManyField('Control', blank=True)  # 對應的 ISO 控制措施
    attachments = models.ManyToManyField('Attachment', blank=True)
    
    class Meta:
        ordering = ['-created_at']

class DocumentVersion(models.Model):
    """文件版本歷史"""
    document = models.ForeignKey(Document, related_name='versions', on_delete=models.CASCADE)
    version = models.CharField(max_length=20)
    content = models.TextField()
    change_summary = models.TextField()  # 變更摘要
    created_by = models.ForeignKey(User, on_delete=models.PROTECT)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['-created_at']

class DocumentAcknowledgment(models.Model):
    """文件簽署記錄"""
    document = models.ForeignKey(Document, on_delete=models.CASCADE)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    acknowledged_at = models.DateTimeField(auto_now_add=True)
    ip_address = models.GenericIPAddressField()
    signature_data = models.TextField(blank=True)  # 電子簽章資料
    
    class Meta:
        unique_together = ['document', 'user']

class DocumentReviewReminder(models.Model):
    """文件審查提醒"""
    document = models.ForeignKey(Document, on_delete=models.CASCADE)
    reminder_date = models.DateField()
    sent = models.BooleanField(default=False)
    sent_at = models.DateTimeField(null=True, blank=True)
```

#### API 設計

```python
# views.py

from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.utils import timezone
from datetime import timedelta

class DocumentViewSet(viewsets.ModelViewSet):
    queryset = Document.objects.all()
    serializer_class = DocumentSerializer
    
    @action(detail=True, methods=['post'])
    def submit_for_review(self, request, pk=None):
        """提交審查"""
        document = self.get_object()
        if document.status != 'draft':
            return Response({'error': '只有草稿可以提交審查'}, 
                          status=status.HTTP_400_BAD_REQUEST)
        
        document.status = 'review'
        document.save()
        
        # 發送通知給審查者
        self.send_review_notification(document)
        
        return Response({'message': '已提交審查'})
    
    @action(detail=True, methods=['post'])
    def approve(self, request, pk=None):
        """核准文件"""
        document = self.get_object()
        if document.status != 'review':
            return Response({'error': '文件狀態錯誤'}, 
                          status=status.HTTP_400_BAD_REQUEST)
        
        document.status = 'approved'
        document.approved_by = request.user
        document.approved_at = timezone.now()
        document.save()
        
        return Response({'message': '文件已核准'})
    
    @action(detail=True, methods=['post'])
    def publish(self, request, pk=None):
        """發布文件"""
        document = self.get_object()
        if document.status != 'approved':
            return Response({'error': '只有已核准的文件可以發布'}, 
                          status=status.HTTP_400_BAD_REQUEST)
        
        # 建立版本記錄
        DocumentVersion.objects.create(
            document=document,
            version=document.version,
            content=document.content,
            change_summary=request.data.get('change_summary', ''),
            created_by=request.user
        )
        
        document.status = 'published'
        document.published_at = timezone.now()
        
        # 計算下次審查日期
        document.next_review_date = timezone.now().date() + timedelta(
            days=document.review_cycle_months * 30
        )
        document.save()
        
        # 通知全員閱讀
        self.notify_all_users(document)
        
        return Response({'message': '文件已發布'})
    
    @action(detail=True, methods=['post'])
    def acknowledge(self, request, pk=None):
        """使用者確認已閱讀"""
        document = self.get_object()
        
        DocumentAcknowledgment.objects.create(
            document=document,
            user=request.user,
            ip_address=self.get_client_ip(request),
            signature_data=request.data.get('signature', '')
        )
        
        return Response({'message': '已記錄閱讀確認'})
    
    @action(detail=False, methods=['get'])
    def pending_acknowledgment(self, request):
        """取得待閱讀文件清單"""
        user = request.user
        
        # 已發布但使用者尚未確認的文件
        acknowledged_ids = DocumentAcknowledgment.objects.filter(
            user=user
        ).values_list('document_id', flat=True)
        
        pending_docs = Document.objects.filter(
            status='published'
        ).exclude(id__in=acknowledged_ids)
        
        serializer = self.get_serializer(pending_docs, many=True)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def review_due(self, request):
        """取得需要審查的文件"""
        today = timezone.now().date()
        
        # 審查日期在 30 天內或已過期
        due_docs = Document.objects.filter(
            status='published',
            next_review_date__lte=today + timedelta(days=30)
        )
        
        serializer = self.get_serializer(due_docs, many=True)
        return Response(serializer.data)
```

#### 前端介面設計（Vue.js）

```vue
<template>
  <div class="document-management">
    <!-- 文件清單 -->
    <div class="document-list">
      <h2>政策文件管理</h2>
      
      <!-- 篩選器 -->
      <div class="filters">
        <select v-model="filterCategory">
          <option value="">所有分類</option>
          <option value="policy">政策</option>
          <option value="procedure">程序</option>
          <option value="instruction">指引</option>
        </select>
        
        <select v-model="filterStatus">
          <option value="">所有狀態</option>
          <option value="draft">草稿</option>
          <option value="review">審查中</option>
          <option value="approved">已核准</option>
          <option value="published">已發布</option>
        </select>
      </div>
      
      <!-- 文件表格 -->
      <table class="document-table">
        <thead>
          <tr>
            <th>文件編號</th>
            <th>標題</th>
            <th>版本</th>
            <th>狀態</th>
            <th>發布日期</th>
            <th>下次審查</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="doc in filteredDocuments" :key="doc.id">
            <td>{{ doc.document_number }}</td>
            <td>{{ doc.title }}</td>
            <td>{{ doc.version }}</td>
            <td>
              <span :class="'status-badge status-' + doc.status">
                {{ getStatusLabel(doc.status) }}
              </span>
            </td>
            <td>{{ formatDate(doc.published_at) }}</td>
            <td>
              <span :class="{'text-danger': isOverdue(doc.next_review_date)}">
                {{ formatDate(doc.next_review_date) }}
              </span>
            </td>
            <td>
              <button @click="viewDocument(doc)" class="btn-sm">查看</button>
              <button v-if="doc.status === 'published' && !doc.acknowledged" 
                      @click="acknowledgeDocument(doc)" 
                      class="btn-sm btn-primary">
                確認已閱讀
              </button>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <!-- 待辦提醒 -->
    <div class="pending-tasks">
      <h3>待辦事項</h3>
      <div v-if="pendingAcknowledgments.length > 0" class="alert alert-warning">
        <i class="icon-warning"></i>
        您有 {{ pendingAcknowledgments.length }} 份文件待閱讀確認
      </div>
      <div v-if="reviewDueDocuments.length > 0" class="alert alert-info">
        <i class="icon-info"></i>
        有 {{ reviewDueDocuments.length }} 份文件需要審查
      </div>
    </div>
    
    <!-- 文件檢視/編輯對話框 -->
    <DocumentModal 
      v-if="showModal" 
      :document="selectedDocument"
      @close="showModal = false"
      @updated="loadDocuments"
    />
  </div>
</template>

<script>
export default {
  name: 'DocumentManagement',
  data() {
    return {
      documents: [],
      filterCategory: '',
      filterStatus: '',
      pendingAcknowledgments: [],
      reviewDueDocuments: [],
      showModal: false,
      selectedDocument: null
    }
  },
  computed: {
    filteredDocuments() {
      return this.documents.filter(doc => {
        if (this.filterCategory && doc.category !== this.filterCategory) return false;
        if (this.filterStatus && doc.status !== this.filterStatus) return false;
        return true;
      });
    }
  },
  methods: {
    async loadDocuments() {
      const response = await this.$api.get('/api/documents/');
      this.documents = response.data;
    },
    
    async loadPendingTasks() {
      const pending = await this.$api.get('/api/documents/pending_acknowledgment/');
      this.pendingAcknowledgments = pending.data;
      
      const reviewDue = await this.$api.get('/api/documents/review_due/');
      this.reviewDueDocuments = reviewDue.data;
    },
    
    async acknowledgeDocument(doc) {
      if (confirm('確認您已詳細閱讀此文件？')) {
        await this.$api.post(`/api/documents/${doc.id}/acknowledge/`);
        this.$message.success('已記錄閱讀確認');
        this.loadDocuments();
        this.loadPendingTasks();
      }
    },
    
    viewDocument(doc) {
      this.selectedDocument = doc;
      this.showModal = true;
    },
    
    getStatusLabel(status) {
      const labels = {
        'draft': '草稿',
        'review': '審查中',
        'approved': '已核准',
        'published': '已發布',
        'archived': '已歸檔'
      };
      return labels[status] || status;
    },
    
    formatDate(dateString) {
      if (!dateString) return '-';
      return new Date(dateString).toLocaleDateString('zh-TW');
    },
    
    isOverdue(dateString) {
      if (!dateString) return false;
      return new Date(dateString) < new Date();
    }
  },
  mounted() {
    this.loadDocuments();
    this.loadPendingTasks();
  }
}
</script>

<style scoped>
.status-badge {
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
}
.status-draft { background: #e0e0e0; }
.status-review { background: #fff3cd; }
.status-approved { background: #d1ecf1; }
.status-published { background: #d4edda; }

.text-danger { color: #dc3545; }
</style>
```

---

### **技術實現方案**

#### 1. 文件範本變數替換引擎

```python
# utils/template_engine.py

from jinja2 import Template
from datetime import datetime

class DocumentTemplateEngine:
    """文件範本變數替換引擎"""
    
    def __init__(self, organization):
        self.organization = organization
        self.default_variables = self._get_default_variables()
    
    def _get_default_variables(self):
        """取得預設變數"""
        return {
            'company_name': self.organization.name,
            'company_address': self.organization.address,
            'company_phone': self.organization.phone,
            'company_email': self.organization.email,
            'current_date': datetime.now().strftime('%Y年%m月%d日'),
            'current_year': datetime.now().year,
        }
    
    def render(self, template_content, custom_variables=None):
        """渲染範本"""
        variables = self.default_variables.copy()
        if custom_variables:
            variables.update(custom_variables)
        
        template = Template(template_content)
        return template.render(**variables)
    
    def get_available_variables(self):
        """取得可用變數清單"""
        return list(self.default_variables.keys())

# 使用範例
engine = DocumentTemplateEngine(organization=my_org)

template_content = """
【{{company_name}}】資訊安全政策

發布日期：{{current_date}}

本政策由 {{company_name}} 制定，適用於本公司所有員工...
"""

rendered_content = engine.render(template_content)
```

#### 2. 文件審查提醒排程

```python
# tasks.py (Celery)

from celery import shared_task
from django.core.mail import send_mail
from django.utils import timezone
from datetime import timedelta

@shared_task
def send_document_review_reminders():
    """發送文件審查提醒"""
    today = timezone.now().date()
    
    # 找出 30 天內需要審查的文件
    documents = Document.objects.filter(
        status='published',
        next_review_date__lte=today + timedelta(days=30),
        next_review_date__gte=today
    )
    
    for doc in documents:
        days_until_review = (doc.next_review_date - today).days
        
        # 發送提醒給文件擁有者
        send_mail(
            subject=f'[提醒] 文件審查到期通知 - {doc.title}',
            message=f"""
            您好，
            
            文件「{doc.title}」（編號：{doc.document_number}）
            將於 {days_until_review} 天後到期需要審查。
            
            請於 {doc.next_review_date} 前完成審查。
            
            文件連結：https://yourdomain.com/documents/{doc.id}/
            
            此為系統自動發送郵件，請勿直接回覆。
            """,
            from_email='system@yourcompany.com',
            recipient_list=[doc.created_by.email],
        )
        
        # 記錄提醒
        DocumentReviewReminder.objects.create(
            document=doc,
            reminder_date=today,
            sent=True,
            sent_at=timezone.now()
        )

# Celery Beat 排程設定
from celery.schedules import crontab

CELERY_BEAT_SCHEDULE = {
    'send-document-review-reminders': {
        'task': 'tasks.send_document_review_reminders',
        'schedule': crontab(hour=9, minute=0),  # 每天早上 9 點執行
    },
}
```
