[安全部署] 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 官網註冊帳號即可:
- 前往 https://www.cisecurity.org/cis-benchmarks
- 選擇你需要的技術類別(如 Linux、Docker、AWS)
- 填寫基本資訊後即可下載 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 幾百頁的文件嚇到。先做這三件事,就能大幅降低風險:
- 更新系統和套件:把已知的漏洞修掉
- 關閉不必要的 Port 和服務:減少攻擊面
- 不用 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 的旅程中,我們從安全需求出發,經過安全設計、安全編碼、安全測試,現在來到了部署與維運。你的程式碼已經足夠安全了,現在讓它跑在同樣安全的環境上吧。
下一篇,我們會繼續深入部署與維運階段的另一個重要主題——安全監控與告警:建立你的資安儀表板。系統上線後,怎麼知道有沒有人在敲你的門窗?怎麼第一時間發現異常?我們下次見。
延伸閱讀
- CIS Benchmarks 官方下載 — 免費下載各平台的安全設定建議
- Docker Security Best Practices — OWASP — Docker 安全速查表
- Trivy — Container Security Scanner — 開源容器漏洞掃描工具
- Lynis — Security Auditing Tool — Linux 系統安全審計工具
- AWS Well-Architected Framework — Security Pillar — AWS 安全架構最佳實踐
- NIST SP 800-123 — Guide to General Server Security — 伺服器安全通用指南
- CI/CD 安全整合:在 Pipeline 加入安全關卡 — SSDLC by 飛飛系列文章
- 安全設計原則實戰指南:最小權限、縱深防禦、預設安全 — SSDLC by 飛飛系列文章