[安全維運] 002 事件回應 SOP 完整指南:發生資安事件怎麼辦?NIST 四階段流程與 Node.js 實戰範例

「房子住進去之後,最怕的不是牆壁裂開,而是半夜火災時不知道滅火器在哪裡。
系統上線之後也一樣——你不怕出事,怕的是出事之後手忙腳亂、沒有人知道該做什麼。
事件回應不是『希望用不到』的東西,而是『用到時能救命』的東西。」
— SSDLC by 飛飛


一、事件回應是什麼?為什麼每個開發團隊都需要一份 SOP?

想像你住在一棟新蓋好的公寓裡。建商用了防火建材、裝了煙霧偵測器、也有消防灑水系統。這些都是之前在設計和施工階段做好的安全措施。

但有一天,三樓真的冒煙了。

這時候你需要的不是再去研究防火建材的規格,而是:

  • 誰負責打 119?
  • 住戶要從哪個樓梯逃生?
  • 消防栓在幾樓?
  • 怎麼確認所有人都安全撤離了?
  • 火滅了之後,要怎麼評估損失、修復房屋?

這就是事件回應(Incident Response, IR)——當資安事件真的發生時,你的團隊要按照什麼流程來處理,才能把傷害降到最低、盡快恢復正常。

在 SSDLC 的旅程中,我們已經走過了安全需求(確認要防火)、安全設計(畫好消防通道)、安全實作(裝好偵測器)、安全驗證(測試灑水系統能不能動)。現在我們來到了階段五:部署與維運——房子住進去之後,真的發生狀況要怎麼辦?

為什麼這件事這麼重要?

因為不管你的防禦做得多好,沒有任何系統是 100% 不會被入侵的。就像不管防火建材多好,你還是需要滅火器和逃生計畫。資安界有句名言:

「被入侵不是『會不會』的問題,而是『什麼時候』的問題。」

根據 IBM 的統計,擁有事件回應計畫並定期演練的組織,平均每次資料外洩事件可以節省超過 NT$5,000 萬的損失。不是因為他們不會被攻擊,而是因為他們知道被攻擊後該怎麼做


二、事件回應的四大階段:用火災應變來理解

事件回應有一個經典的框架,來自 NIST SP 800-61(Computer Security Incident Handling Guide)。它把事件回應分成四個階段,我們繼續用公寓火災的比喻來理解:

┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  🛡️ 階段一   │───▶│  🔍 階段二   │───▶│  🔧 階段三   │───▶│  📝 階段四   │
│  準備        │    │  偵測與分析  │    │  封鎖、消除  │    │  事後活動    │
│  Preparation │    │  Detection & │    │  與復原      │    │  Post-       │
│              │    │  Analysis    │    │  Containment │    │  Incident    │
│              │    │              │    │  Eradication │    │  Activity    │
│              │    │              │    │  & Recovery  │    │              │
│ 🏠 準備好   │    │ 🏠 發現冒煙 │    │ 🏠 滅火+   │    │ 🏠 檢討+   │
│ 滅火器和     │    │ 確認是火災   │    │ 修復+      │    │ 改進         │
│ 逃生計畫     │    │ 還是燒焦味   │    │ 重新入住     │    │ 防災措施     │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
        │                                                          │
        └──────────────────────────────────────────────────────────┘
                            持續改善的循環

這四個階段形成一個循環,每次處理完事件的經驗,都會回饋到準備階段,讓下一次的應變更好。


階段一:準備(Preparation)——滅火器要在火災前就買好

公寓比喻:在火災發生之前,你要做的事:

  • 買滅火器放在走廊
  • 畫好逃生路線圖貼在電梯旁
  • 確認所有住戶都知道逃生出口在哪
  • 跟管委會確認消防公司的電話
  • 每半年做一次消防演練

在資安領域,準備階段要做這些事:

1. 組建事件回應團隊(CSIRT)

CSIRT(Computer Security Incident Response Team)就是你的「消防隊」。不需要是專職的資安團隊,但每個角色必須明確:

角色 職責 公寓比喻
事件指揮官(Incident Commander) 統籌整個事件處理流程,做最終決策 管委會主委
技術負責人(Technical Lead) 帶領技術調查與修復 水電師傅
溝通聯絡人(Communications Lead) 對內對外溝通,包含通知客戶、主管機關 總幹事
法務/合規(Legal/Compliance) 確認法規通報義務、保存證據 社區法律顧問
管理層代表(Management) 核准重大決策(如關閉系統) 建設公司代表

飛飛觀點:
對於 5-10 人的小團隊,不需要每個角色都是不同的人。CTO 可以同時是事件指揮官和技術負責人,PM 可以兼溝通聯絡人。重點是事先指定,而不是事發後才在群組裡問「誰來處理?」

2. 準備工具與資源

就像滅火器要在火災前就放好,以下工具要在事件發生前就準備好:

□ 日誌集中管理系統(如 ELK Stack、Graylog)
□ 系統監控與告警工具(如 Grafana、CloudWatch)
□ 事件追蹤系統(如 Jira、PagerDuty)
□ 安全的溝通管道(不要用可能被入侵的系統來討論事件!)
□ 數位鑑識工具(如 Volatility、Autopsy)
□ 離線備份(確認備份真的可以還原)
□ 聯絡清單(團隊成員手機、外部資安廠商、主管機關聯繫方式)

3. 建立事件分類與分級標準

不是每個告警都是世界末日。你需要一套分級標準,讓團隊知道什麼時候該全員召集、什麼時候看一下就好:

等級 名稱 說明 回應時效 範例
P1 緊急(Critical) 系統核心功能受損或大規模資料外洩 15 分鐘內回應 資料庫被拖走、勒索軟體感染
P2 高(High) 部分功能受影響或有確認的入侵跡象 1 小時內回應 管理後台被未授權存取、異常大量 API 呼叫
P3 中(Medium) 可疑活動但尚未確認影響 4 小時內回應 多次登入失敗、可疑的掃描行為
P4 低(Low) 資訊性事件,需要記錄但不需緊急處理 下一個工作日 弱點掃描發現低風險問題

4. 定期演練

有逃生計畫但從來不演練,等真的火災來了,大家還是會慌。建議每季做一次桌上演練(Tabletop Exercise)——不需要真的動系統,團隊坐下來,模擬一個資安事件情境,走一遍流程。

演練情境範例(台灣電商場景):

情境:你們的電商平台在週五晚上 10 點收到客戶投訴,
說他的帳號被用來下了一筆 NT$50,000 的訂單,但他本人沒有操作。

同時,監控系統顯示過去 1 小時有超過 500 個帳號從同一個 IP 段嘗試登入,
其中 30 個帳號登入成功。

請討論:
1. 這是什麼等級的事件?
2. 誰應該被通知?
3. 第一步要做什麼?
4. 需要通報主管機關嗎?(提示:個資法)
5. 要不要暫時關閉登入功能?

階段二:偵測與分析(Detection & Analysis)——確認是真的火災,還是只是鄰居在烤肉

公寓比喻:煙霧偵測器響了,你要先搞清楚:

  • 是真的失火了嗎?還是有人在廚房燒焦了東西?
  • 如果是火災,火源在哪?幾樓?
  • 範圍多大?有擴散嗎?
  • 有沒有人受傷?

在資安領域,偵測與分析要回答這些問題:

1. 事件來源有哪些?

資安事件的「煙霧偵測器」有很多種:

偵測來源 說明 範例
監控系統告警 SIEM、IDS/IPS 發出的自動告警 「同一 IP 在 5 分鐘內嘗試登入 200 次」
使用者回報 客戶或內部員工通報 「我的帳號被盜了」「網站出現奇怪的頁面」
第三方通知 外部資安研究者或合作夥伴告知 「在暗網看到你們的客戶資料在賣」
例行檢查 定期的日誌審查或掃描 「弱點掃描發現新的 Critical 弱點」
執法機關通知 警察或調查局告知 「你們的系統IP出現在犯罪調查中」

2. 初步分析:確認事件的真實性與範圍

收到告警後,不要馬上恐慌。先做初步分析:

// 事件初步分析 Checklist(Node.js 日誌查詢範例)
const analyzeIncident = async (alertData) => {
  console.log('=== 事件初步分析 ===');

  // 1. 確認告警來源是否可信
  console.log('📌 告警來源:', alertData.source);
  console.log('📌 告警時間:', alertData.timestamp);

  // 2. 查詢相關日誌
  const relatedLogs = await queryLogs({
    timeRange: {
      start: new Date(alertData.timestamp - 3600000), // 往前看 1 小時
      end: new Date(alertData.timestamp + 1800000),   // 往後看 30 分鐘
    },
    filters: {
      sourceIP: alertData.sourceIP,
      userId: alertData.userId,
    }
  });

  // 3. 初步判斷
  const analysis = {
    isConfirmed: false,          // 是否確認為真實事件
    severity: 'UNKNOWN',         // 嚴重等級
    affectedSystems: [],         // 受影響的系統
    affectedUsers: 0,            // 受影響的使用者數量
    dataExposure: 'UNKNOWN',     // 是否有資料外洩
    isOngoing: true,             // 攻擊是否仍在進行
  };

  // 4. 記錄分析結果(保留證據!)
  await saveToIncidentLog({
    incidentId: generateIncidentId(), // IR-2026-001
    analyst: 'on-call-engineer',
    timestamp: new Date(),
    findings: analysis,
    rawLogs: relatedLogs,
  });

  return analysis;
};

3. 事件時間線建立

確認是真實事件後,最重要的事情之一就是建立事件時間線(Timeline)。這就像火災調查員會重建火災的發展過程:

📅 事件時間線範例:帳號大規模盜用事件

2026-02-27 21:30  監控系統偵測到異常登入行為
                  (同一 IP 段 103.xx.xx.0/24 大量登入嘗試)

2026-02-27 21:45  告警發送至 on-call 工程師

2026-02-27 21:50  工程師確認為 Credential Stuffing 攻擊
                  (撞庫攻擊,使用外洩的帳密清單嘗試登入)

2026-02-27 22:00  第一位客戶來電投訴帳號被盜

2026-02-27 22:10  初步統計:30 個帳號被成功登入
                  其中 5 個帳號已被用來下單

2026-02-27 22:15  升級為 P1 事件,通知事件指揮官

2026-02-27 22:20  ▶ 進入階段三:封鎖與消除

飛飛觀點:
時間線是事件回應中最有價值的產出之一。它不只是事後寫報告用的,在事件處理過程中,它能幫助你的團隊保持頭腦清醒——在壓力下,人很容易忘記自己做了什麼、什麼時間做的。養成習慣,每做一個動作就記一筆。


階段三:封鎖、消除與復原(Containment, Eradication & Recovery)——滅火、清理現場、重新入住

這是事件回應中最「動手」的階段。公寓比喻:

  • 封鎖(Containment):先阻止火勢蔓延——關閉門窗、隔離起火樓層
  • 消除(Eradication):把火撲滅、找到火源並確認完全熄滅
  • 復原(Recovery):修復受損區域、確認安全後讓住戶回來

封鎖(Containment):先止血

封鎖的目標是阻止事件繼續擴大。分為短期封鎖和長期封鎖:

類型 目的 時機 範例
短期封鎖 立即止血,減少損害 確認事件後立即執行 封鎖攻擊來源 IP、停用被入侵帳號
長期封鎖 建立臨時防線,等待根本修復 短期封鎖後 啟用額外的認證機制、部署 WAF 規則

實戰範例:處理 Credential Stuffing 攻擊的封鎖措施

// 短期封鎖措施
const shortTermContainment = async (incidentData) => {
  // 1. 封鎖攻擊來源 IP 段
  await firewall.blockIPRange('103.xx.xx.0/24');
  logAction('封鎖 IP 段 103.xx.xx.0/24');

  // 2. 強制登出所有被入侵的帳號
  for (const userId of incidentData.compromisedUsers) {
    await session.revokeAllSessions(userId);
    logAction(<code>強制登出使用者 ${userId} 所有 Session</code>);
  }

  // 3. 暫時提高登入的 Rate Limit
  await rateLimiter.update({
    endpoint: '/api/auth/login',
    maxAttempts: 3,        // 從 10 次降到 3 次
    windowMinutes: 15,     // 15 分鐘內
    blockDuration: 60,     // 超過就封鎖 60 分鐘
  });
  logAction('提高登入 Rate Limit 限制');

  // 4. 凍結可疑訂單
  for (const orderId of incidentData.suspiciousOrders) {
    await orders.freeze(orderId);
    logAction(<code class="kb-btn">凍結可疑訂單 ${orderId}</code>);
  }
};
// 長期封鎖措施
const longTermContainment = async () => {
  // 1. 對所有被入侵帳號強制重設密碼
  for (const userId of incidentData.compromisedUsers) {
    await auth.forcePasswordReset(userId);
    await notification.send(userId, {
      subject: '【重要】您的帳號安全通知',
      template: 'account-compromised',
    });
    logAction(<code>強制重設使用者 ${userId} 密碼並發送通知</code>);
  }

  // 2. 部署 WAF 規則阻擋自動化攻擊
  await waf.addRule({
    name: 'block-credential-stuffing',
    condition: 'login_failure_rate > 5/min per IP',
    action: 'CAPTCHA_CHALLENGE',
  });
  logAction('部署 WAF Credential Stuffing 防護規則');
};

消除(Eradication):把火源徹底清除

封鎖只是「止血」,消除才是「治療」。你要找到根本原因並清除:

消除階段 Checklist:

□ 根本原因已確認
  → 此案例:使用者密碼來自其他平台的外洩資料,且未啟用 MFA

□ 攻擊者的存取路徑已關閉
  → 所有被入侵帳號已重設密碼

□ 攻擊者植入的後門已清除(如有)
  → 檢查是否有新增的 API Key、OAuth App、Webhook

□ 受影響的系統已修補
  → 新增 Credential Stuffing 防護機制

□ 相關的 IOC(Indicators of Compromise)已記錄
  → 攻擊 IP 段、攻擊模式特徵

復原(Recovery):確認安全後重新開放

復原不是「打開開關就好」,而是要逐步恢復、持續監控

復原步驟:

1. ✅ 確認封鎖和消除措施都已到位
2. ✅ 在測試環境驗證修復方案
3. ✅ 逐步恢復服務(先灰度發布、再全量開放)
4. ✅ 加強監控(至少 72 小時高頻監控)
5. ✅ 確認被入侵帳號的使用者已收到通知
6. ✅ 確認可疑訂單已被正確處理(退款或確認)

飛飛觀點:
復原階段最常犯的錯誤就是「太急著恢復正常」。我見過有團隊在攻擊還沒完全消除的情況下就急著把系統打開,結果攻擊者馬上又進來了。寧可多花幾個小時確認,也不要讓同一個事件發生兩次。


階段四:事後活動(Post-Incident Activity)——火災之後的檢討與改善

火滅了、房子修好了、住戶回來了。但最重要的一步還沒做——從這次事件中學到什麼?

這個階段的核心就是無咎事後檢討會議(Blameless Postmortem)

事後檢討會議的結構

建議在事件結束後 3-5 個工作天內召開(太早大家還在善後,太晚就忘記細節了):

# 事件回顧報告
## IR-2026-001:電商平台 Credential Stuffing 攻擊事件

### 事件摘要
- 事件類型:Credential Stuffing(撞庫攻擊)
- 發生時間:2026-02-27 21:30 ~ 2026-02-28 02:00
- 持續時間:約 4.5 小時
- 嚴重等級:P1
- 影響範圍:30 個帳號被入侵,5 筆可疑訂單(總金額 NT$127,000)

### 時間線
(從偵測到復原的完整時間線,略)

### 根本原因
使用者在其他平台使用相同的帳號密碼,該平台資料外洩後,
攻擊者利用外洩的帳密清單對我們的登入 API 進行撞庫攻擊。

我們的系統缺乏:
1. 登入行為異常偵測(同 IP 大量登入嘗試未觸發告警)
2. 強制或鼓勵 MFA
3. 已知外洩密碼的比對機制

### 做得好的地方 ✅
- 監控系統在攻擊開始後 15 分鐘內發出告警
- On-call 工程師在 5 分鐘內回應
- 事件時間線記錄完整
- 客服團隊快速回應客戶詢問

### 需要改進的地方 🔧
- 告警規則的閾值太高,應該更早觸發
- 沒有預先準備好的 Credential Stuffing 應變 Playbook
- 封鎖 IP 的流程需要手動操作 AWS Console,耗時太久
- 客戶通知信的範本沒有事先準備好

### 改善行動項目
| 項目 | 負責人 | 預計完成日 | 優先順序 |
|------|--------|-----------|---------|
| 導入 MFA 並在登入頁面推廣 | 前端組 Alice | 2026-03-15 | P1 |
| 建立自動化 IP 封鎖機制 | 後端組 Bob | 2026-03-10 | P1 |
| 整合 HaveIBeenPwned API 檢查密碼是否外洩 | 後端組 Charlie | 2026-03-20 | P2 |
| 調整登入異常偵測的告警閾值 | SRE Diana | 2026-03-08 | P1 |
| 準備客戶通知信範本 | PM Eve | 2026-03-05 | P2 |
| 撰寫 Credential Stuffing Playbook | 全隊 | 2026-03-31 | P2 |

飛飛觀點:
事後檢討會議的「無咎」原則非常重要。不要問「是誰的錯」,要問「是什麼讓這件事發生了」。如果工程師怕被責備,下次發現異常就不敢回報,那才是最大的損失。


三、建立你的事件處理流程:從零到一的實戰指南

了解了四個階段之後,讓我們來建立一份實際可用的事件回應 SOP。

3.1 事件回應流程圖

                    ┌──────────────┐
                    │  偵測到告警  │
                    │  或接到通報  │
                    └──────┬───────┘
                           │
                    ┌──────▼───────┐
                    │  初步評估    │
                    │  是否為真實  │
                    │  安全事件?  │
                    └──────┬───────┘
                           │
                  ┌────────┴────────┐
                  │                 │
            ┌─────▼─────┐    ┌─────▼─────┐
            │  是:分級  │    │ 否:記錄  │
            │  並啟動    │    │ 並關閉    │
            │  回應流程  │    │           │
            └─────┬─────┘    └───────────┘
                  │
         ┌────────┼────────┐
         │        │        │
    ┌────▼───┐ ┌──▼──┐ ┌──▼────┐
    │ P1/P2  │ │ P3  │ │  P4   │
    │ 立即   │ │ 4hr │ │ 下個   │
    │ 召集   │ │ 內   │ │ 工作日 │
    │ 團隊   │ │ 回應 │ │ 處理   │
    └────┬───┘ └──┬──┘ └──┬────┘
         │        │       │
    ┌────▼────────▼───────▼────┐
    │        封鎖與止血        │
    │  • 隔離受影響系統        │
    │  • 封鎖攻擊來源          │
    │  • 保存證據              │
    └────────────┬─────────────┘
                 │
    ┌────────────▼─────────────┐
    │        消除與修復        │
    │  • 找到根本原因          │
    │  • 清除攻擊者存取        │
    │  • 修補弱點              │
    └────────────┬─────────────┘
                 │
    ┌────────────▼─────────────┐
    │        復原與監控        │
    │  • 逐步恢復服務          │
    │  • 加強監控 72 小時      │
    │  • 通知受影響使用者      │
    └────────────┬─────────────┘
                 │
    ┌────────────▼─────────────┐
    │        事後活動          │
    │  • 撰寫事件報告          │
    │  • 召開檢討會議          │
    │  • 執行改善項目          │
    │  • 法規通報(如需要)    │
    └──────────────────────────┘

3.2 事件回應 Playbook 模板

Playbook 就像是針對特定類型事件的「標準作業程序」。與其在事件發生時才想該做什麼,不如先為常見的攻擊類型寫好劇本。

以下是一個可以直接使用的 Playbook 模板:

# Playbook:[事件類型名稱]

## 適用情境
- 什麼情況下要啟動這份 Playbook

## 嚴重等級判斷
- P1 條件:...
- P2 條件:...

## 初步確認步驟
1. 檢查 [具體的日誌/監控]
2. 確認 [具體的指標]
3. 判斷 [真實性條件]

## 封鎖措施
### 立即執行(前 15 分鐘)
- [ ] 動作一
- [ ] 動作二

### 短期措施(前 1 小時)
- [ ] 動作一
- [ ] 動作二

## 消除步驟
- [ ] 找出根本原因
- [ ] 清除 [具體項目]

## 復原步驟
- [ ] 驗證修復
- [ ] 恢復服務
- [ ] 加強監控

## 通知清單
- [ ] 內部:[誰]
- [ ] 外部:[誰]
- [ ] 主管機關(如適用)

## 證據保存
- [ ] 需要保存的日誌
- [ ] 需要保存的系統快照

3.3 台灣法規通報要求

在台灣,資安事件發生後可能需要通報主管機關。這不是「做好人」,而是法律義務

法規 通報對象 通報時限 適用情況
個人資料保護法(2025 年修正) 個資保護委員會 + 當事人 發現後 72 小時內通報機關;查明後 30 日內通知當事人 個資外洩事件
資通安全管理法 主管機關 + 上級機關 1 小時內通報(依嚴重等級) 公務機關與特定非公務機關
上市櫃公司資通安全管控指引 證交所/櫃買中心 發生後儘速通報 上市櫃公司

飛飛觀點:
2025 年個資法修正後,個資外洩的通報義務變得更加嚴格。通報不是「示弱」,拖延通報才是真正的風險——不只有行政裁罰,還可能面臨民事求償。及時通報反而能展現組織的負責態度,降低後續的法律風險。


四、事件回應工具箱:實用的 Node.js 輔助工具

以下是幾個你可以整合進系統中的事件回應輔助工具範例:

4.1 安全事件自動告警

// middleware/securityMonitor.js
// 簡易的安全事件偵測中介軟體

const Redis = require('ioredis');
const redis = new Redis();

const ALERT_THRESHOLDS = {
  LOGIN_FAILURE: { count: 10, windowSeconds: 300 },   // 5 分鐘內 10 次登入失敗
  API_RATE: { count: 100, windowSeconds: 60 },         // 1 分鐘內 100 次 API 請求
  SENSITIVE_ACCESS: { count: 5, windowSeconds: 600 },  // 10 分鐘內 5 次敏感資料存取
};

async function trackSecurityEvent(eventType, identifier) {
  const key = <code class="kb-btn">security:${eventType}:${identifier}</code>;
  const threshold = ALERT_THRESHOLDS[eventType];

  if (!threshold) return;

  const count = await redis.incr(key);
  if (count === 1) {
    await redis.expire(key, threshold.windowSeconds);
  }

  if (count >= threshold.count) {
    await triggerAlert({
      type: eventType,
      identifier,
      count,
      window: threshold.windowSeconds,
      timestamp: new Date().toISOString(),
    });
  }
}

async function triggerAlert(alertData) {
  console.error('[SECURITY ALERT]', JSON.stringify(alertData));

  // 發送到 Slack
  await fetch(process.env.SLACK_WEBHOOK_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: <code>🚨 安全告警:${alertData.type}\n</code> +
            <code>來源:${alertData.identifier}\n</code> +
            <code>次數:${alertData.count} 次(${alertData.window} 秒內)\n</code> +
            <code class="kb-btn">時間:${alertData.timestamp}</code>,
    }),
  });

  // 記錄到事件追蹤系統
  await saveIncidentAlert(alertData);
}

module.exports = { trackSecurityEvent };

4.2 證據保存腳本

事件發生時,第一件事就是保存證據。以下腳本可以快速收集關鍵資訊:

// scripts/collectEvidence.js
// 資安事件證據收集腳本

const fs = require('fs');
const { execSync } = require('child_process');
const path = require('path');

async function collectEvidence(incidentId) {
  const evidenceDir = path.join('/secure-evidence', incidentId);
  fs.mkdirSync(evidenceDir, { recursive: true });

  console.log(<code class="kb-btn">📁 證據收集開始:${incidentId}</code>);
  console.log(<code class="kb-btn">📂 儲存目錄:${evidenceDir}</code>);

  // 1. 收集系統日誌
  console.log('📋 收集系統日誌...');
  execSync(<code>journalctl --since "24 hours ago" > ${evidenceDir}/system.log</code>);

  // 2. 收集應用程式日誌
  console.log('📋 收集應用程式日誌...');
  execSync(<code>cp /var/log/app/*.log ${evidenceDir}/</code>);

  // 3. 收集網路連線狀態
  console.log('🌐 收集網路連線狀態...');
  execSync(<code>netstat -tlnp > ${evidenceDir}/network-connections.txt</code>);
  execSync(<code>ss -tlnp >> ${evidenceDir}/network-connections.txt</code>);

  // 4. 收集執行中的程序
  console.log('⚙️  收集程序清單...');
  execSync(<code>ps auxf > ${evidenceDir}/processes.txt</code>);

  // 5. 收集最近登入記錄
  console.log('👤 收集登入記錄...');
  execSync(<code>last -50 > ${evidenceDir}/login-history.txt</code>);
  execSync(<code>lastb -50 > ${evidenceDir}/failed-logins.txt 2>/dev/null || true</code>);

  // 6. 計算所有證據檔案的 Hash(確保證據完整性)
  console.log('🔏 計算證據 Hash...');
  const files = fs.readdirSync(evidenceDir);
  const hashes = {};
  for (const file of files) {
    const filePath = path.join(evidenceDir, file);
    const hash = execSync(<code class="kb-btn">sha256sum ${filePath}</code>).toString().trim();
    hashes[file] = hash.split(' ')[0];
  }
  fs.writeFileSync(
    path.join(evidenceDir, 'evidence-hashes.json'),
    JSON.stringify(hashes, null, 2)
  );

  console.log(<code>\n✅ 證據收集完成,共 ${files.length} 個檔案</code>);
  console.log(<code class="kb-btn">📂 存放位置:${evidenceDir}</code>);

  return evidenceDir;
}

// 使用方式
// node scripts/collectEvidence.js IR-2026-001
const incidentId = process.argv[2] || <code class="kb-btn">IR-${new Date().getFullYear()}-${Date.now()}</code>;
collectEvidence(incidentId);

五、事件回應 SOP 完整模板

以下是一份可以直接用在你的團隊的事件回應 SOP 模板:

# [公司名稱] 資安事件回應標準作業程序(SOP)

## 版本紀錄
| 版本 | 日期 | 修改人 | 修改內容 |
|------|------|--------|---------|
| 1.0 | YYYY-MM-DD | OOO | 初版制定 |

## 一、目的
定義資安事件的處理流程與責任分工,確保事件發生時能迅速、
有效地回應,將損害降到最低。

## 二、適用範圍
適用於本組織所有資訊系統、網路設備與資料資產相關的資安事件。

## 三、事件回應團隊(CSIRT)
| 角色 | 姓名 | 聯繫方式 | 備援人員 |
|------|------|---------|---------|
| 事件指揮官 | | | |
| 技術負責人 | | | |
| 溝通聯絡人 | | | |
| 法務/合規 | | | |

## 四、事件分級
(參考前文的 P1-P4 分級表)

## 五、處理流程
### 5.1 偵測與通報
- 收到告警或通報後,由 on-call 人員進行初步評估
- 判定為真實事件後,依據分級標準通知對應人員
- 建立事件追蹤單(IR-YYYY-NNN)

### 5.2 封鎖與止血
- 依據對應的 Playbook 執行封鎖措施
- 保存所有相關證據
- 記錄所有操作於事件時間線

### 5.3 消除與修復
- 找出根本原因
- 清除攻擊者存取路徑
- 修補相關弱點

### 5.4 復原
- 在測試環境驗證修復方案
- 逐步恢復服務
- 加強監控至少 72 小時

### 5.5 事後活動
- 事件結束後 3-5 個工作天內召開檢討會議
- 撰寫事件報告
- 追蹤改善項目

## 六、法規通報
- 個資外洩:72 小時內通報個資保護委員會
- 資安事件(受資安法管轄者):依等級 1 小時內通報

## 七、文件附錄
- 附錄 A:Playbook 清單
- 附錄 B:聯絡清單
- 附錄 C:證據收集指引
- 附錄 D:事件報告模板

六、團隊落地的務實建議

第一步:先有,再完善

不要試圖一次寫出完美的 SOP。先寫一份「能用」的版本,可能只有一頁——誰是 on-call、出事打什麼電話、最基本的封鎖步驟。有了這個起點,每次處理完事件後再逐步補充。

第二步:從常見攻擊開始寫 Playbook

不用一次寫完所有攻擊類型的 Playbook。建議從以下五個最常見的開始:

□ Playbook 1:帳號被入侵 / Credential Stuffing
□ Playbook 2:DDoS 攻擊
□ Playbook 3:Web 應用程式弱點被利用(SQL Injection、XSS 等)
□ Playbook 4:惡意軟體 / 勒索軟體感染
□ Playbook 5:個資外洩

第三步:每季做一次桌上演練

演練不需要動到真實系統。把團隊拉到一個會議室(或視訊),丟一個情境,走一遍流程。每次演練後記錄發現的問題,更新 SOP。

第四步:把事件回應整合進 on-call 制度

如果你的團隊有 on-call 輪值,把安全事件的回應也納入。on-call 工程師不需要會處理所有事件,但至少要會做初步評估和分級,然後知道該 call 誰。


七、常見問題 FAQ

Q1:我們是小團隊,只有 5 個人,也需要事件回應 SOP 嗎?

A:尤其需要。大公司可能有專職的資安團隊來處理事件,但小團隊發生資安事件時,影響的就是所有人。一份簡單的 SOP(即使只有一頁)可以在壓力最大的時候,幫你的團隊保持冷靜和有序。從一份簡單的聯絡清單和分級標準開始就好。

Q2:我們沒有 SIEM 或 SOC,怎麼偵測事件?

A:不需要昂貴的工具才能開始。你可以從以下低成本方案開始:

  • 應用程式日誌:確保你的 Node.js 應用程式記錄了關鍵的安全事件(登入失敗、權限錯誤等)
  • 雲端服務內建告警:AWS CloudWatch、GCP Cloud Monitoring、Azure Monitor 都有免費額度
  • 開源工具:Grafana + Loki 做日誌監控、Wazuh 做入侵偵測
  • 第三方服務:Uptime Robot(免費版本)監控網站可用性

重點不是工具有多厲害,而是你有沒有在看那些日誌。

Q3:事件發生時,要不要先關閉系統?

A:這取決於事件的性質和嚴重程度,沒有標準答案。一般原則:

  • 應該關閉:確認有持續的資料外洩、勒索軟體正在擴散、攻擊者仍在系統中且無法即時阻斷
  • 不應輕易關閉:關閉會導致證據消失(記憶體中的攻擊痕跡)、影響範圍可以透過隔離控制、關閉的損失大於事件本身的損失

這個決策應該由事件指揮官根據當下情況判斷,不要由工程師獨自決定。

Q4:怎麼跟客戶溝通資安事件?

A:透明、及時、負責任。以下是溝通的三個原則:

  • 說你知道的事實:「我們在某月某日偵測到未授權的系統存取」
  • 說你正在做什麼:「我們已經封鎖了攻擊來源,並正在全面調查影響範圍」
  • 說使用者應該做什麼:「建議您立即更改密碼,並開啟雙因素認證」
  • 不要猜測:還沒查清楚的事情不要亂講,「調查仍在進行中」是完全可以接受的說法

八、結語:最好的事件回應,是讓團隊不再害怕事件

很多團隊害怕資安事件,就像很多人害怕火災。但消防隊員不怕火災,不是因為他們覺得火燒不到自己,而是因為他們知道該怎麼做

事件回應 SOP 的真正價值,不是那份文件本身,而是準備的過程。當你的團隊一起討論「如果 XXX 發生了怎麼辦」,大家就會開始思考如何預防、如何偵測、如何回應。這個過程本身,就是在強化你的安全文化。

記住 NIST SP 800-61 的核心理念:

「事件回應不是在事件發生時才開始,而是在事件發生之前就已經在進行了。」

從今天開始,花 30 分鐘跟你的團隊坐下來,回答這三個問題:

  1. 如果我們的系統現在被入侵了,第一個被通知的人是誰?
  2. 他/她知道接下來該做什麼嗎?
  3. 我們有沒有一份寫下來的流程可以參考?

如果有任何一個答案是「不知道」或「沒有」,那就是你開始建立事件回應 SOP 的最好時機。

安全不是恐懼,而是創造的基礎。 一份好的事件回應計畫,讓你的團隊在面對資安事件時,從「恐慌」變成「有序處理」——這就是專業。


延伸閱讀

官方資源:

台灣法規:

系列文章: