[安全部署] 001 系統強化 Hardening 入門指南:關閉不必要的門窗|CIS Benchmark 應用與容器、雲端環境強化實戰

「房子蓋好了、驗收也過了,但你有檢查過哪些窗戶還開著嗎?系統強化,就是交屋後的最後一道安全流程。」
— SSDLC by 飛飛


為什麼你需要系統強化?蓋好房子不等於住進去就安全

在 SSDLC 的學習旅程中,我們已經走過了安全需求定義、安全設計、安全編碼,也做了 SAST、DAST、CI/CD 安全整合,甚至請人做了滲透測試。現在,系統終於要上線了——恭喜你來到了階段五:部署

但先等一下。你的程式碼再安全、測試再完整,如果跑在一台設定不當的伺服器上,那一切都白費了

延續蓋房子的比喻:你花了大錢請建築師畫防震設計圖、用了最好的防火建材、裝了頂級保全系統。但交屋那天,你發現——後門沒鎖、地下室的窗戶大開、備用鑰匙還掛在門口、Wi-Fi 密碼是 <code>12345678</code>。

這就是「系統強化(System Hardening)」要解決的問題:把所有不必要的門窗關上、把預設的弱設定改掉、把用不到的服務停掉

一個真實的痛點

2021 年,台灣某知名論壇因為伺服器的 Apache Struts 未即時更新,被駭客利用已知漏洞入侵,導致數百萬筆會員資料外洩。問題不在程式碼,而在運行環境的設定與維護

類似的案例層出不窮:AWS S3 Bucket 設成公開、Docker 容器用 root 跑服務、資料庫預設帳號密碼沒改、SSH 允許 root 遠端登入⋯⋯這些都不是程式邏輯的漏洞,而是系統設定的疏忽

飛飛觀點:
很多開發者覺得「部署是 DevOps 的事、設定是 SRE 的事」。但在 SSDLC 的理念中,安全是每個人的責任。你寫的程式跑在什麼環境上、那個環境夠不夠安全,這直接影響你的程式碼能不能真正保護使用者。


什麼是系統強化(Hardening)?把「預設不安全」變成「預設安全」

系統強化,簡單來說就是:減少系統的攻擊面(Attack Surface)

每一台伺服器、每一個容器、每一個雲端服務,出廠時都會帶著一堆預設設定。這些預設設定的設計目標是「讓你能快速上手」,而不是「讓你最安全」。所以你會看到:

  • 作業系統預裝了一堆你用不到的服務(FTP、Telnet、SNMP)
  • Web 伺服器預設開啟目錄列表(Directory Listing)
  • 資料庫預設允許遠端連線且帳號密碼是 <code>admin/admin</code>
  • Docker 容器預設用 root 執行
  • 雲端服務的 Security Group 預設允許 0.0.0.0/0 入站

系統強化就是有系統性地把這些「門窗」一扇扇關上,只留下你真正需要的。

系統強化 vs. 安全設計原則的關係

還記得我們在安全設計階段學的七大原則嗎?系統強化其實就是這些原則在維運階段的具體實踐:

安全設計原則 在系統強化中的應用
最小權限 服務帳號只給需要的權限、容器不用 root 跑
預設安全 關閉不必要的服務和 Port、移除預設帳號
縱深防禦 網路層 + 主機層 + 應用層多層設定
最小共享 每個服務獨立隔離、不共用帳號和憑證
失敗安全 防火牆預設拒絕、只開白名單

CIS Benchmark 是什麼?你的系統強化教科書

自己從零開始想「該關哪些門窗」太費時了,而且很容易漏掉。好在,有人已經幫你整理好了——這就是 CIS Benchmark

CIS(Center for Internet Security,網際網路安全中心) 是一個非營利組織,集結了全球數千位資安專家,針對各種作業系統、雲端平台、容器、資料庫、網路設備,制定了詳細的安全設定建議

用蓋房子比喻:CIS Benchmark 就像「建築安全規範手冊」。你不需要自己去研究鋼筋要多粗、消防通道要多寬——規範手冊都寫好了,你照著做就好。

CIS Benchmark 的七大類別

類別 涵蓋範圍 常見範例
作業系統 Windows、Linux、macOS Ubuntu 24.04、Windows Server 2022、RHEL 9
伺服器軟體 Web 伺服器、資料庫 Nginx、Apache、PostgreSQL、MySQL
雲端平台 三大雲的基礎設定 AWS Foundations、Azure Foundations、GCP
容器與編排 容器化環境 Docker、Kubernetes
網路設備 防火牆、交換器 Cisco、Palo Alto、Fortinet
桌面軟體 瀏覽器、辦公軟體 Chrome、Firefox、Microsoft 365
行動裝置 手機作業系統 iOS、Android

兩個安全等級:Level 1 與 Level 2

CIS Benchmark 把建議分成兩個等級,你可以根據自己的風險承受度來選擇:

等級 說明 適合對象 對系統的影響
Level 1 基本安全建議,適用於所有環境 所有系統,尤其是剛開始做強化的團隊 低,幾乎不影響功能
Level 2 進階安全建議,提供更高的防護 處理敏感資料的系統(如金融、醫療) 中,可能需要調整部分功能

飛飛觀點:
如果你的團隊是第一次做系統強化,先從 Level 1 開始就好。不要一開始就追求 Level 2 全部滿分——那就像新手健身第一天就舉 100 公斤,只會受傷。先把 Level 1 做扎實,再逐步升級。

如何取得 CIS Benchmark?

CIS Benchmark 是免費下載的(PDF 格式),你只需要到 CIS 官網註冊帳號即可:

  1. 前往 https://www.cisecurity.org/cis-benchmarks
  2. 選擇你需要的技術類別(如 Linux、Docker、AWS)
  3. 填寫基本資訊後即可下載 PDF

實戰:Node.js 應用常見的系統強化項目

讓我們回到 SSDLC by 飛飛系列一直使用的技術棧——Node.js + Express,看看在部署時有哪些系統強化的工作要做。

一、作業系統層級強化(以 Ubuntu 為例)

# ═══════════════════════════════════════
# 1. 更新系統,修補已知漏洞
# ═══════════════════════════════════════
sudo apt update && sudo apt upgrade -y

# 啟用自動安全更新(就像設定自動繳管理費,不會忘記)
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

# ═══════════════════════════════════════
# 2. 移除不必要的服務(關閉不需要的門窗)
# ═══════════════════════════════════════
# 列出目前啟用的服務
systemctl list-unit-files --type=service --state=enabled

# 停用並關閉不需要的服務
sudo systemctl stop avahi-daemon && sudo systemctl disable avahi-daemon   # mDNS 服務
sudo systemctl stop cups && sudo systemctl disable cups                     # 列印服務
sudo systemctl stop rpcbind && sudo systemctl disable rpcbind               # NFS 相關

# ═══════════════════════════════════════
# 3. 設定防火牆(只開需要的 Port)
# ═══════════════════════════════════════
# 使用 UFW(Uncomplicated Firewall)
sudo ufw default deny incoming    # 預設拒絕所有入站(失敗安全原則)
sudo ufw default allow outgoing   # 允許出站
sudo ufw allow 22/tcp             # SSH(建議改用非標準 Port)
sudo ufw allow 443/tcp            # HTTPS
sudo ufw allow 80/tcp             # HTTP(建議只用於重導到 HTTPS)
sudo ufw enable

# ═══════════════════════════════════════
# 4. SSH 強化(鎖好遠端登入的大門)
# ═══════════════════════════════════════
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
# /etc/ssh/sshd_config 安全設定
# 參考 CIS Benchmark for Ubuntu — SSH Server Configuration

Port 2222                          # 改用非標準 Port,減少自動化掃描
PermitRootLogin no                 # 禁止 root 直接登入
PasswordAuthentication no          # 禁用密碼登入,只允許 SSH Key
PubkeyAuthentication yes           # 啟用公鑰認證
MaxAuthTries 3                     # 最多嘗試 3 次
ClientAliveInterval 300            # 5 分鐘無活動斷開
ClientAliveCountMax 2              # 最多容忍 2 次無回應
AllowUsers deploy                  # 只允許特定使用者登入
Protocol 2                         # 只用 SSH Protocol 2
X11Forwarding no                   # 關閉 X11 轉發
# 重啟 SSH 服務讓設定生效
sudo systemctl restart sshd

# ═══════════════════════════════════════
# 5. 建立專用服務帳號(最小權限原則)
# ═══════════════════════════════════════
# 建立一個沒有 shell 的專用帳號跑 Node.js
sudo useradd --system --no-create-home --shell /usr/sbin/nologin nodeapp

二、Node.js 與 Express 應用層級強化

// app.js — 生產環境的安全設定
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

const app = express();

// ═══════════════════════════════════════
// 1. 使用 Helmet 設定安全標頭
// ═══════════════════════════════════════
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'"],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"],
    },
  },
  crossOriginEmbedderPolicy: true,
  crossOriginOpenerPolicy: true,
  crossOriginResourcePolicy: { policy: "same-site" },
  dnsPrefetchControl: true,
  frameguard: { action: 'deny' },
  hidePoweredBy: true,                     // ✅ 移除 X-Powered-By
  hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
  ieNoOpen: true,
  noSniff: true,
  referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
  xssFilter: true,
}));

// ═══════════════════════════════════════
// 2. 速率限制(防止暴力攻擊與 DDoS)
// ═══════════════════════════════════════
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,   // 15 分鐘
  max: 100,                     // 每個 IP 最多 100 次請求
  standardHeaders: true,
  legacyHeaders: false,
  message: { error: '請求過於頻繁,請稍後再試' }
});
app.use('/api/', limiter);

// ═══════════════════════════════════════
// 3. 關閉不必要的功能
// ═══════════════════════════════════════
app.disable('x-powered-by');           // 不洩露技術棧
app.disable('etag');                   // 視情況關閉(防止資訊洩露)

// ═══════════════════════════════════════
// 4. 安全的錯誤處理(不洩露內部資訊)
// ═══════════════════════════════════════
if (process.env.NODE_ENV === 'production') {
  app.use((err, req, res, next) => {
    console.error(<code>[${new Date().toISOString()}] Error:</code>, {
      message: err.message,
      stack: err.stack,        // 只記到伺服器日誌
      url: req.originalUrl,
      method: req.method,
      ip: req.ip
    });

    res.status(err.status || 500).json({
      error: '系統處理時發生錯誤,請稍後再試'
      // ❌ 絕不回傳 err.stack 或 err.message
    });
  });
}

// ═══════════════════════════════════════
// 5. 只監聽必要的介面
// ═══════════════════════════════════════
const PORT = process.env.PORT || 3000;
const HOST = process.env.HOST || '127.0.0.1';  // ✅ 只監聽 localhost
// 外部流量由 Nginx 反向代理進來,Node.js 不直接暴露

app.listen(PORT, HOST, () => {
  console.log(<code class="kb-btn">Server running on ${HOST}:${PORT}</code>);
});

三、Nginx 反向代理強化

# /etc/nginx/conf.d/security.conf — Nginx 安全設定
# 參考 CIS Benchmark for Nginx

# 隱藏版本號(不讓攻擊者知道你用什麼版本)
server_tokens off;

# 限制請求大小(防止大檔案攻擊)
client_max_body_size 10M;
client_body_buffer_size 1K;
client_header_buffer_size 1K;
large_client_header_buffers 2 1K;

# 超時設定(防止慢速攻擊 Slowloris)
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 15;
send_timeout 10;

# 安全標頭(與 Helmet 互補)
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

# 只允許安全的 TLS 版本和加密套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

# 禁用不安全的 HTTP 方法
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|PATCH)$ ) {
    return 405;
}
# /etc/nginx/sites-available/myapp.conf
server {
    listen 80;
    server_name myapp.com.tw;
    # 強制導向 HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name myapp.com.tw;

    ssl_certificate /etc/letsencrypt/live/myapp.com.tw/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapp.com.tw/privkey.pem;

    # HSTS — 告訴瀏覽器永遠用 HTTPS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # 隱藏後端的 Header
        proxy_hide_header X-Powered-By;
    }

    # 禁止存取隱藏檔案(.env, .git 等)
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
}

容器環境強化:Docker 不是天生安全的

容器化部署是現代應用的標配,但 Docker 的預設設定有不少安全隱患。很多開發者以為「放進容器就是隔離了」,但如果容器裡跑的是 root、映像裡藏著漏洞、網路沒有隔離——那容器只是一層薄薄的保鮮膜,不是防彈衣。

Dockerfile 安全最佳實踐

# ═══════════════════════════════════════
# 安全的 Node.js Dockerfile
# 參考 CIS Docker Benchmark
# ═══════════════════════════════════════

# 1. 使用特定版本的精簡映像(不用 latest,不用完整版)
FROM node:20.11-alpine AS builder

WORKDIR /app

# 2. 只複製 package 檔案,利用 Docker 快取
COPY package.json package-lock.json ./

# 3. 使用 npm ci(確保依賴與 lock 檔一致)
RUN npm ci --only=production && npm cache clean --force

# ─── 第二階段:只帶需要的東西 ───
FROM node:20.11-alpine

# 4. 加入安全更新
RUN apk update && apk upgrade --no-cache && rm -rf /var/cache/apk/*

# 5. 建立非 root 使用者(最小權限原則)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# 6. 從 builder 階段複製依賴
COPY --from=builder /app/node_modules ./node_modules
COPY . .

# 7. 設定檔案權限
RUN chown -R appuser:appgroup /app

# 8. 切換到非 root 使用者
USER appuser

# 9. 只暴露需要的 Port
EXPOSE 3000

# 10. 使用 dumb-init 或 tini 處理信號(避免殭屍程序)
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]

# 11. 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "server.js"]

Docker Compose 安全設定

# docker-compose.yml — 安全設定範例
version: '3.8'

services:
  app:
    build: .
    ports:
      - "127.0.0.1:3000:3000"   # ✅ 只綁定 localhost,不暴露到外網
    environment:
      - NODE_ENV=production
    # ═══ 安全設定 ═══
    read_only: true               # ✅ 唯讀檔案系統
    tmpfs:
      - /tmp                      # 只有 /tmp 可寫入
    security_opt:
      - no-new-privileges:true    # ✅ 禁止提權
    cap_drop:
      - ALL                       # ✅ 移除所有 Linux Capabilities
    cap_add:
      - NET_BIND_SERVICE          # 只加回需要的(如果要綁 Port < 1024)
    deploy:
      resources:
        limits:
          cpus: '1.0'             # ✅ 限制 CPU 使用量
          memory: 512M            # ✅ 限制記憶體
    networks:
      - frontend                  # ✅ 放在專屬網路

  db:
    image: postgres:16-alpine
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password  # ✅ 用 Secret 管理密碼
    networks:
      - backend                   # ✅ 資料庫在獨立網路

# ═══ 網路隔離(最小共享原則)═══
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true                # ✅ 後端網路不連外網

volumes:
  db-data:

secrets:
  db_password:
    file: ./secrets/db_password.txt

容器映像掃描:上線前的健康檢查

# 使用 Trivy 掃描容器映像中的漏洞
# 安裝 Trivy
# macOS
brew install trivy

# Linux
sudo apt-get install trivy

# 掃描你的映像
trivy image my-app:latest

# 只顯示高風險和嚴重漏洞
trivy image --severity HIGH,CRITICAL my-app:latest

# 整合進 CI/CD(發現嚴重漏洞就失敗)
trivy image --exit-code 1 --severity CRITICAL my-app:latest

飛飛觀點:
容器安全最常被忽略的三件事:用 root 跑、用 latest 標籤、不掃描映像。只要解決這三個問題,你的容器安全就已經贏過大多數人了。


雲端環境強化:AWS / GCP / Azure 的安全設定

如果你的應用部署在雲端,除了伺服器本身,還有一整層「雲端服務設定」需要強化。CIS 針對三大雲端平台都有專門的 Benchmark,以下用 AWS 為例說明最關鍵的設定。

AWS 常見的安全設定問題

問題 風險等級 正確做法
S3 Bucket 設為公開 🔴 嚴重 預設私有,需要公開的用 CloudFront 分發
IAM 使用者用 root 帳號操作 🔴 嚴重 建立 IAM 使用者,root 只用於帳號管理
Security Group 允許 0.0.0.0/0 SSH 🔴 嚴重 只允許特定 IP 或 VPN 的 CIDR 範圍
未啟用 CloudTrail 稽核日誌 🟡 高風險 啟用 CloudTrail 並送到獨立的 S3 Bucket
未啟用 MFA 🟡 高風險 所有 IAM 使用者強制 MFA
EBS 未加密 🟡 高風險 預設啟用 EBS 加密
RDS 可公開存取 🔴 嚴重 放在 Private Subnet,不開放公開存取

以 Terraform 實作安全的 AWS 基礎設施

# main.tf — 安全的 AWS 基礎設施設定
# 參考 CIS AWS Foundations Benchmark

# ═══════════════════════════════════════
# 1. S3 Bucket 安全設定
# ═══════════════════════════════════════
resource "aws_s3_bucket" "app_data" {
  bucket = "myapp-data-tw"
}

# ✅ 封鎖所有公開存取
resource "aws_s3_bucket_public_access_block" "app_data" {
  bucket = aws_s3_bucket.app_data.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# ✅ 啟用伺服器端加密
resource "aws_s3_bucket_server_side_encryption_configuration" "app_data" {
  bucket = aws_s3_bucket.app_data.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "aws:kms"
    }
    bucket_key_enabled = true
  }
}

# ✅ 啟用版本控制(防止意外刪除)
resource "aws_s3_bucket_versioning" "app_data" {
  bucket = aws_s3_bucket.app_data.id
  versioning_configuration {
    status = "Enabled"
  }
}

# ═══════════════════════════════════════
# 2. Security Group — 最小開放原則
# ═══════════════════════════════════════
resource "aws_security_group" "app_sg" {
  name        = "app-security-group"
  description = "Application security group"
  vpc_id      = aws_vpc.main.id

  # ✅ 只允許 HTTPS 入站
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow HTTPS from anywhere"
  }

  # ✅ SSH 只允許公司 VPN IP
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["203.0.113.0/24"]   # 公司 VPN IP 範圍
    description = "Allow SSH from company VPN only"
  }

  # ✅ 出站只允許必要的
  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow HTTPS outbound"
  }

  egress {
    from_port   = 5432
    to_port     = 5432
    protocol    = "tcp"
    cidr_blocks = [aws_subnet.private.cidr_block]
    description = "Allow PostgreSQL to private subnet"
  }

  tags = {
    Name = "app-sg"
  }
}

# ═══════════════════════════════════════
# 3. RDS — 資料庫安全設定
# ═══════════════════════════════════════
resource "aws_db_instance" "main" {
  identifier     = "myapp-db"
  engine         = "postgres"
  engine_version = "16.1"
  instance_class = "db.t3.medium"

  # ✅ 不可公開存取
  publicly_accessible = false

  # ✅ 啟用加密
  storage_encrypted = true

  # ✅ 啟用自動備份
  backup_retention_period = 7

  # ✅ 啟用刪除保護
  deletion_protection = true

  # ✅ 放在 Private Subnet
  db_subnet_group_name = aws_db_subnet_group.private.name

  # ✅ 啟用效能監控
  monitoring_interval = 60
}

系統強化 Checklist:你的部署前檢查清單

每次部署前,用這份 Checklist 確認系統強化的項目是否都有照顧到:

## 系統強化部署前 Checklist

### 作業系統層級
- [ ] 系統已更新至最新安全版本?
- [ ] 自動安全更新已啟用?
- [ ] 不必要的服務已停用?(FTP、Telnet、SNMP、CUPS 等)
- [ ] 防火牆已設定,預設拒絕入站?
- [ ] SSH 已禁用 root 登入、禁用密碼認證?
- [ ] 應用程式使用專用的非 root 帳號執行?
- [ ] 檔案系統權限已適當設定?(敏感設定檔權限 600/640)

### 應用程式層級
- [ ] X-Powered-By Header 已移除?
- [ ] Debug 模式已關閉?
- [ ] 錯誤訊息不洩露內部資訊?
- [ ] HTTP 安全標頭已設定?(CSP、HSTS、X-Frame-Options 等)
- [ ] CORS 只允許白名單域名?
- [ ] 速率限制已啟用?
- [ ] TLS 1.2 以上且使用安全的加密套件?

### 容器環境
- [ ] 容器不以 root 使用者執行?
- [ ] 使用特定版本的映像(非 latest)?
- [ ] 映像已通過漏洞掃描(Trivy / Snyk)?
- [ ] 使用多階段建置減少映像大小?
- [ ] 檔案系統設為唯讀?(read_only: true)
- [ ] Linux Capabilities 已最小化?(cap_drop: ALL)
- [ ] 容器間的網路已適當隔離?
- [ ] 健康檢查已設定?

### 雲端環境
- [ ] S3 / Storage 封鎖公開存取?
- [ ] IAM 權限遵循最小權限原則?
- [ ] Security Group 不允許 0.0.0.0/0 SSH?
- [ ] 資料庫不可公開存取?
- [ ] CloudTrail / 稽核日誌已啟用?
- [ ] 所有儲存已啟用加密?
- [ ] MFA 已對所有使用者啟用?
- [ ] 定期審查 IAM 權限和安全設定?

自動化強化:讓工具幫你檢查

手動逐條檢查 CIS Benchmark 太費時了。好消息是,有很多工具可以幫你自動化這個流程:

工具 用途 費用 適合場景
CIS-CAT Pro 依據 CIS Benchmark 自動評估 會員制 企業級合規需求
Trivy 容器映像 + IaC 漏洞掃描 免費開源 容器和 Kubernetes
Lynis Linux 系統安全審計 免費(社群版) 伺服器強化檢查
ScoutSuite 多雲安全設定審計 免費開源 AWS / Azure / GCP
Docker Bench Docker 安全最佳實踐檢查 免費開源 Docker 環境
kube-bench Kubernetes CIS Benchmark 檢查 免費開源 Kubernetes 環境
Prowler AWS 安全最佳實踐檢查 免費開源 AWS 環境

快速使用範例

# ═══ Lynis:Linux 系統安全審計 ═══
# 安裝
sudo apt install lynis -y

# 執行完整系統審計
sudo lynis audit system

# 結果會顯示:
# - 安全評分(Hardening Index)
# - 需要修復的項目
# - 建議的改善措施

# ═══ Docker Bench:Docker 安全檢查 ═══
# 直接用 Docker 跑
docker run --rm --net host --pid host \
  --userns host --cap-add audit_control \
  -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v /etc:/etc:ro \
  docker/docker-bench-security

# ═══ Prowler:AWS 安全檢查 ═══
# 安裝
pip install prowler --break-system-packages

# 執行 CIS AWS Foundations Benchmark 檢查
prowler aws --compliance cis_2.0_aws

整合進 CI/CD Pipeline

還記得我們在前幾篇設定的安全 Pipeline 嗎?現在可以把系統強化的檢查也加進去:

# .github/workflows/security.yml — 加入容器和 IaC 掃描
  # ═══════════════════════════════════════
  # 容器映像安全掃描
  # ═══════════════════════════════════════
  container-hardening:
    name: 🐳 容器安全強化檢查
    runs-on: ubuntu-latest
    if: hashFiles('Dockerfile') != ''
    steps:
      - uses: actions/checkout@v4

      - name: 建置 Docker 映像
        run: docker build -t my-app:scan .

      - name: Trivy 容器漏洞掃描
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'my-app:scan'
          format: 'table'
          exit-code: '1'
          severity: 'HIGH,CRITICAL'
          ignore-unfixed: true

      - name: Dockerfile 最佳實踐檢查(Hadolint)
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile

  # ═══════════════════════════════════════
  # 基礎設施即程式碼(IaC)安全掃描
  # ═══════════════════════════════════════
  iac-scan:
    name: 🏗️ 基礎設施安全掃描
    runs-on: ubuntu-latest
    if: hashFiles('**/*.tf') != '' || hashFiles('docker-compose*.yml') != ''
    steps:
      - uses: actions/checkout@v4

      - name: Trivy IaC 掃描
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          scan-ref: '.'
          format: 'table'
          exit-code: '1'
          severity: 'HIGH,CRITICAL'

團隊落地建議:不要想一次做完,分階段來

建議一:先做風險最高的三件事

如果你的團隊是第一次接觸系統強化,不要被 CIS Benchmark 幾百頁的文件嚇到。先做這三件事,就能大幅降低風險:

  1. 更新系統和套件:把已知的漏洞修掉
  2. 關閉不必要的 Port 和服務:減少攻擊面
  3. 不用 root 執行應用程式:限制入侵後的影響範圍

建議二:建立「黃金映像(Golden Image)」

不要每次部署都從零開始強化。把已經強化好的系統設定打包成映像,之後的新伺服器都從這個映像開始:

強化好的 Ubuntu + Node.js + 安全設定
  │
  ├─ 打包成 AMI(AWS)或 Docker Base Image
  │
  ├─ 所有新環境都從這個映像起步
  │
  └─ 定期更新映像(每月一次)

建議三:定期複檢,不是做一次就好

系統強化不是一次性的工作。新的漏洞會被發現、CIS Benchmark 會更新、你的系統設定可能在維護過程中被改動。建議每季做一次全面的安全設定複檢。

## 定期複檢排程建議

| 頻率 | 檢查項目 | 工具 |
|------|---------|------|
| 每天 | 容器映像漏洞掃描 | Trivy(CI/CD 自動化) |
| 每週 | 系統安全更新 | unattended-upgrades |
| 每月 | 完整安全審計 | Lynis / Docker Bench |
| 每季 | CIS Benchmark 合規檢查 | CIS-CAT / Prowler |
| 每年 | 全面安全架構審查 | 搭配滲透測試結果 |

常見問題 FAQ

Q1:系統強化跟滲透測試有什麼差別?

系統強化是「主動防禦」——你照著最佳實踐把設定調好,把門窗關上。滲透測試是「被動驗證」——你請人假裝小偷來攻擊,看還有哪裡沒關好。兩者是互補的:先做強化(關門窗),再做滲透測試(請人來試開門),最後根據測試結果再強化。

Q2:我們用的是 PaaS(如 Heroku、Render),還需要做系統強化嗎?

PaaS 幫你處理了作業系統和基礎設施的強化,但應用程式層級的強化還是你的責任。HTTP 安全標頭、速率限制、錯誤處理、環境變數管理——這些不管你部署在哪裡都要做。另外,PaaS 的設定本身也有安全選項需要檢查,例如:是否強制 HTTPS、是否限制 IP 白名單、日誌是否保留足夠天數。

Q3:CIS Benchmark 要做到 100% 合規嗎?

不需要,也不建議追求 100%。CIS Benchmark 是建議,不是法律。有些建議可能跟你的業務需求衝突(例如你的系統就是需要允許某些 Port)。重點是:你清楚知道每一條你沒有遵守的建議,並且有合理的理由。把這些「例外」記錄下來,作為安全決策的依據。

Q4:團隊沒有專職的 DevOps 或 SRE,誰來做系統強化?

在小團隊裡,系統強化可以是後端工程師的責任之一。用自動化工具(如 Lynis、Docker Bench)可以大幅降低門檻。建議指定一位 Security Champion 負責定期執行掃描、追蹤修復進度。如果預算允許,也可以考慮導入 CIS Hardened Images(已經預先強化好的雲端映像),直接省下大部分手動設定的工作。


結語:安全的最後一哩路,往往藏在設定裡

回到蓋房子的比喻。你花了幾年設計、施工、裝修,終於蓋好了一棟漂亮的房子。但如果交屋那天你不去檢查每一扇窗戶是否能鎖好、每一道門的門鎖是否正常、火災警報器的電池有沒有裝——那所有的努力都可能功虧一簣。

系統強化就是那個「交屋驗收」的步驟。它不華麗、不刺激、不像寫程式那麼有成就感,但它是安全的最後一哩路

在 SSDLC 的旅程中,我們從安全需求出發,經過安全設計、安全編碼、安全測試,現在來到了部署與維運。你的程式碼已經足夠安全了,現在讓它跑在同樣安全的環境上吧。

下一篇,我們會繼續深入部署與維運階段的另一個重要主題——安全監控與告警:建立你的資安儀表板。系統上線後,怎麼知道有沒有人在敲你的門窗?怎麼第一時間發現異常?我們下次見。


延伸閱讀