
安全掃描:你的映像檔和依賴套件藏了什麼漏洞?
2020 年 SolarWinds 供應鏈攻擊影響了上萬個組織,2021 年 Log4Shell(CVE-2021-44228)讓全球 Java 應用暴露在遠端程式碼執行風險之下,2024 年 xz-utils 後門事件差點讓整個 Linux 生態系統淪陷。這些事件的共同點:問題不在你自己寫的程式碼,而在你「引用」的東西——Base Image 裡的系統套件、npm/pip 裡的第三方依賴、甚至是建置工具本身。當你的 Dockerfile 寫 FROM node:20 時,你繼承了那個 image 裡所有已知和未知的漏洞。當你 npm install 時,你把整棵依賴樹的風險都拉進了專案。
安全掃描的目標很簡單:在漏洞被攻擊者利用之前,先找到它。不是部署到 production 之後才去掃,而是在 CI/CD pipeline 的每個階段都掃——程式碼寫完掃一次(SAST),映像檔 build 完掃一次(Image Scan),跑起來之後再掃一次(DAST)。這就是「Shift-left Security」的核心思想:安全檢查越早做,修復成本越低。這篇文章會完整走過 Container Image 掃描、Dependency 掃描、SAST/DAST 的概念和實作,重點放在 Trivy(目前最流行的開源掃描工具)以及如何把掃描整合進 CD pipeline 裡。
架構概覽
flowchart LR Code["原始碼\nSource Code"] --> SAST["SAST\n靜態分析"] SAST --> DepScan["Dependency Scan\n依賴套件掃描"] DepScan --> Build["Docker Build\n映像檔建置"] Build --> ImageScan["Image Scan\nTrivy / Snyk"] ImageScan --> DAST["DAST\n動態掃描"] DAST --> Report["安全報告\nVulnerability Report"] Report -->|PASS| Deploy["部署 Production"] Report -->|FAIL| Block["阻擋部署\n通知開發者"]
架構概覽
flowchart LR subgraph Dev["開發階段"] Code[程式碼撰寫] -->|git push| Repo[Git Repository] end subgraph CI["CI Pipeline"] direction TB SAST[SAST\n靜態原始碼掃描] -->|pass| DepScan[Dependency Scan\nnpm audit / pip-audit] DepScan -->|pass| Build[Docker Build] Build -->|image| ImgScan[Image Scan\nTrivy / Clair] ImgScan -->|pass| Push[Push to Registry] end subgraph Registry["映像庫"] Harbor[Harbor\n自動掃描] end subgraph Deploy["部署階段"] Staging[Staging 環境] DAST[DAST\n動態應用掃描] Prod[Production] end Repo --> CI Push --> Harbor Harbor -->|pull| Staging Staging --> DAST DAST -->|pass| Prod style SAST fill:#4a90d9,color:#fff style DepScan fill:#4a90d9,color:#fff style ImgScan fill:#e74c3c,color:#fff style DAST fill:#f39c12,color:#fff
整個安全掃描策略覆蓋四個階段:SAST 在程式碼階段抓出潛在的安全缺陷(SQL Injection、硬編碼密碼、不安全的函式呼叫);Dependency Scan 檢查第三方套件是否有已知 CVE;Image Scan 在映像檔建置後掃描 OS 層和應用層的漏洞;DAST 在 staging 環境對運行中的應用做黑箱測試。每一層都是一道閘門,通過才能進入下一階段。
核心概念
-
Container Image Scanning(容器映像檔掃描):映像檔掃描會解析 image 的每一層 layer,識別出安裝了哪些 OS 套件(apt/apk/yum)和應用依賴(npm/pip/maven),然後比對 CVE 資料庫(NVD、Alpine SecDB、Debian Security Tracker 等)找出已知漏洞。常見工具:Trivy(Aqua Security 開源,掃描速度快、支援格式多)、Clair(CoreOS 開發,需要搭配 PostgreSQL)、Harbor 內建掃描器(其實就是 Trivy adapter)。掃描結果會列出每個漏洞的 CVE ID、嚴重程度(Critical/High/Medium/Low)、受影響的套件版本、以及修復版本。重點不是「零漏洞」(幾乎不可能),而是「沒有 Critical 和 High 等級的可修復漏洞」。
-
Dependency Scanning(依賴套件掃描):和 Image Scanning 不同,Dependency Scanning 專注在應用層的第三方套件。它讀取
package-lock.json、requirements.txt、pom.xml、go.sum等 lock file,比對已知漏洞資料庫。每個語言生態系都有自己的工具:Node.js 用npm audit、Python 用pip-audit、Java 用 OWASP Dependency-Check、Go 用govulncheck。這類掃描應該在 CI 的最早期執行——甚至在 build 之前——因為如果依賴有嚴重漏洞,整個 build 就不應該繼續。 -
SAST(Static Application Security Testing,靜態應用安全測試):SAST 分析原始碼本身,不需要編譯或執行。它能抓出的問題包括:SQL Injection(字串拼接 SQL 查詢)、XSS(未經轉義的使用者輸入直接輸出到 HTML)、硬編碼的密碼或 API Key、不安全的加密演算法(MD5/SHA1)、路徑遍歷(Path Traversal)等。常見工具:Semgrep(開源規則引擎,支援多語言)、SonarQube(程式碼品質 + 安全)、GitLab SAST(內建在 GitLab Ultimate 裡,底層用了多個開源引擎)。SAST 的缺點是誤報率高——很多它標記的問題在特定上下文中其實不是漏洞。
-
DAST(Dynamic Application Security Testing,動態應用安全測試):DAST 是對運行中的應用做黑箱測試,模擬攻擊者從外部發送惡意請求,看應用會不會中招。它能發現的問題:CSRF 漏洞、HTTP header 設定不當(缺少 Content-Security-Policy、X-Frame-Options)、Session 管理缺陷、Server-side Request Forgery(SSRF)。常見工具:OWASP ZAP(開源,最流行的 DAST 工具)、Burp Suite(商業版功能強大)、GitLab DAST(整合在 pipeline 裡自動執行)。DAST 的前提是要有可以存取的環境,所以通常在部署到 staging 之後才執行。
-
SCA(Software Composition Analysis,軟體組成分析):SCA 除了找漏洞之外,還處理授權合規問題。你用了一個 GPL-3.0 的套件,但你的專案是閉源商業軟體——這可能有法律風險。SCA 工具會列出專案裡所有第三方依賴的授權條款(MIT、Apache-2.0、GPL、BSD 等),標記出有合規風險的依賴。Snyk、FOSSA、WhiteSource(現在叫 Mend)都是 SCA 工具。在大公司,法務部門通常會要求 SCA 報告才允許上線。
-
Shift-left Security(安全左移):傳統做法是在部署前才做安全審查——開發花了兩個月寫完功能,然後安全團隊花兩週做 penetration test,發現一堆問題,打回去改,又花兩週。Shift-left 的核心理念是把安全檢查搬到開發流程的最早期:IDE 裡裝 linter 即時抓漏洞、pre-commit hook 擋住含有 secret 的提交、CI pipeline 每次 push 都做 SAST + Dependency Scan。問題發現得越早,修復成本越低——在 IDE 裡改只要幾秒,在 production 裡修可能要半天做 hotfix + rollback。
工具比較
| 特性 | Trivy | Snyk | Clair | GitLab Built-in |
|---|---|---|---|---|
| 授權 | 開源 (Apache-2.0) | 免費方案 + 商業版 | 開源 (Apache-2.0) | GitLab Ultimate |
| Image Scanning | 是 | 是 | 是 | 是(底層用 Trivy) |
| Dependency Scanning | 是(filesystem mode) | 是(核心功能) | 否 | 是 |
| SAST | 有限(misconfiguration) | 是(Snyk Code) | 否 | 是(多引擎整合) |
| IaC Scanning | 是(Dockerfile, K8s, Terraform) | 是 | 否 | 是 |
| 掃描速度 | 快(本地 DB) | 中等(API 呼叫) | 慢(需 PostgreSQL) | 中等 |
| CI 整合難度 | 低(單一 binary) | 低(CLI + API) | 高(需額外部署) | 內建(零設定) |
| 修復建議 | 列出修復版本 | 自動 PR 修復 | 列出修復版本 | 列出修復版本 |
| 離線使用 | 支援(下載 DB) | 不支援 | 支援 | 不支援 |
| 適合場景 | 中小團隊、CI 整合 | 需要自動修復 PR | 搭配 Harbor/Quay | 已用 GitLab Ultimate |
Trivy 是目前最推薦的選擇——它是單一 binary,不需要額外部署資料庫或伺服器,掃描速度快,支援的格式最多(container image、filesystem、git repo、IaC config),而且完全開源。Snyk 的優勢在於它能自動開 PR 幫你升級有漏洞的依賴版本,對不想手動追蹤每個 CVE 的團隊很方便,但免費方案有掃描次數限制。
實作範例
範例一:Trivy CLI 掃描(Image、Filesystem、Config)
Trivy 最大的優勢是一個工具搞定所有掃描場景。以下是三種最常用的掃描方式。
# ===== 1. 掃描 Container Image =====
# 掃描本地 Docker image,列出所有已知漏洞
trivy image myapp:latest
# 只顯示 CRITICAL 和 HIGH 等級,忽略沒有修復版本的漏洞
trivy image --severity CRITICAL,HIGH --ignore-unfixed myapp:latest
# 掃描遠端 Registry 上的 image(不需要 docker pull)
trivy image --username $HARBOR_USER --password $HARBOR_TOKEN \
harbor.example.com/ec/api:v1.2.3
# 輸出 JSON 格式(方便後續自動化處理)
trivy image --format json --output trivy-report.json myapp:latest
# 設定 exit code:有 CRITICAL/HIGH 時回傳非零,讓 CI 失敗
trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:latest
# ===== 2. 掃描 Filesystem(應用依賴) =====
# 掃描專案目錄,自動偵測 package-lock.json、requirements.txt 等
trivy filesystem --severity CRITICAL,HIGH .
# 只掃描特定語言的依賴
trivy filesystem --scanners vuln --skip-dirs node_modules .
# ===== 3. 掃描 Config(IaC 設定檔) =====
# 掃描 Dockerfile、docker-compose.yml、Kubernetes YAML 的不安全設定
trivy config .
# 掃描 Dockerfile 是否使用了 root user、是否有不安全的指令
trivy config --policy-bundle-repository ghcr.io/aquasecurity/trivy-policies \
Dockerfile
# ===== 4. 更新漏洞資料庫 =====
# 離線環境需要手動下載 DB
trivy image --download-db-only
# DB 存放在 ~/.cache/trivy/db/範例二:GitLab CI 整合 Trivy(Pipeline 掃描 + 阻擋部署)
把 Trivy 整合到 CD pipeline 裡,在 build 完映像檔後自動掃描,CRITICAL 或 HIGH 漏洞直接讓 pipeline 失敗。
# .gitlab-ci.yml - 安全掃描整合
variables:
REGISTRY: "harbor.example.com"
IMAGE: "${REGISTRY}/ec/${CI_PROJECT_NAME}"
TRIVY_SEVERITY: "CRITICAL,HIGH"
stages:
- lint
- dependency-scan
- build
- image-scan
- deploy
# ===== SAST(靜態分析)=====
sast:semgrep:
stage: lint
image: returntocorp/semgrep:latest
script:
- semgrep scan --config auto --error --json --output semgrep-report.json .
artifacts:
reports:
sast: semgrep-report.json
expire_in: 7 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
allow_failure: true # SAST 誤報率高,初期建議不擋 pipeline
# ===== Dependency Scanning =====
dependency-scan:
stage: dependency-scan
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy filesystem
--exit-code 1
--severity ${TRIVY_SEVERITY}
--ignore-unfixed
--format table
.
# 同時輸出 JSON 報告
- trivy filesystem
--format json
--output dependency-report.json
.
artifacts:
paths:
- dependency-report.json
expire_in: 7 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
# ===== Build =====
build:docker:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $HARBOR_ROBOT_USER -p $HARBOR_ROBOT_TOKEN $REGISTRY
script:
- docker build -t ${IMAGE}:${CI_COMMIT_SHA} .
- docker push ${IMAGE}:${CI_COMMIT_SHA}
rules:
- if: $CI_COMMIT_BRANCH == "main"
# ===== Image Scanning =====
image-scan:trivy:
stage: image-scan
image:
name: aquasec/trivy:latest
entrypoint: [""]
variables:
TRIVY_USERNAME: $HARBOR_ROBOT_USER
TRIVY_PASSWORD: $HARBOR_ROBOT_TOKEN
script:
# 嚴格模式:有 CRITICAL/HIGH 就讓 pipeline 失敗
- trivy image
--exit-code 1
--severity ${TRIVY_SEVERITY}
--ignore-unfixed
--format table
${IMAGE}:${CI_COMMIT_SHA}
# 完整報告(包含所有等級)
- trivy image
--format json
--output trivy-image-report.json
${IMAGE}:${CI_COMMIT_SHA}
artifacts:
paths:
- trivy-image-report.json
reports:
container_scanning: trivy-image-report.json
expire_in: 7 days
rules:
- if: $CI_COMMIT_BRANCH == "main"
# ===== Deploy(只有掃描通過才能部署)=====
deploy:dev:
stage: deploy
needs:
- build:docker
- image-scan:trivy
image: alpine:latest
environment:
name: dev
script:
- echo "Deploying scanned and verified image..."
# 部署邏輯省略
rules:
- if: $CI_COMMIT_BRANCH == "main"這個 pipeline 的關鍵設計:dependency-scan 在 build 之前執行,如果依賴有嚴重漏洞就不浪費時間 build image。image-scan 在 build 之後、deploy 之前執行,掃描 image 的 OS 層漏洞。deploy:dev 使用 needs 確保必須通過 image scan 才能部署。初期建議 SAST 用 allow_failure: true,等團隊熟悉掃描結果後再改成阻擋模式。
範例三:npm audit / pip-audit 依賴掃描
不需要 Trivy 也能做基本的依賴掃描,每個語言生態系都有原生的安全檢查工具。
# ===== Node.js:npm audit =====
# 檢查 package-lock.json 裡的已知漏洞
npm audit
# 只顯示 high 和 critical 等級
npm audit --audit-level=high
# 輸出 JSON 格式(方便 CI 解析)
npm audit --json > npm-audit-report.json
# 自動修復可以安全升級的依賴(minor/patch 版本)
npm audit fix
# 強制修復(包含 breaking changes 的 major 版本升級,慎用)
npm audit fix --force
# 在 CI 裡使用:有 high/critical 漏洞時讓 pipeline 失敗
npm audit --audit-level=high || exit 1
# ===== Python:pip-audit =====
# 安裝 pip-audit
pip install pip-audit
# 掃描當前環境安裝的所有套件
pip-audit
# 掃描 requirements.txt(不需要真的安裝套件)
pip-audit -r requirements.txt
# 只顯示有修復版本的漏洞
pip-audit --fix --dry-run
# 輸出 JSON 格式
pip-audit -f json -o pip-audit-report.json
# 在 CI 裡使用:有漏洞時回傳非零 exit code(預設行為)
pip-audit -r requirements.txt
# ===== Go:govulncheck =====
# 安裝 govulncheck
go install golang.org/x/vuln/cmd/govulncheck@latest
# 掃描當前 module
govulncheck ./...
# ===== Java:OWASP Dependency-Check =====
# Maven plugin
mvn org.owasp:dependency-check-maven:check
# Gradle plugin(需在 build.gradle 裡加 plugin)
gradle dependencyCheckAnalyze範例四:安全的 Dockerfile 最佳實踐
映像檔掃描能找到漏洞,但更好的策略是從源頭減少攻擊面——寫出更安全的 Dockerfile。
# ===== 不好的寫法 =====
# FROM node:20 # 完整 image,1GB+,包含大量不需要的工具
# RUN npm install # 安裝 dev dependencies 進 production image
# COPY . . # 複製 .env、.git 等敏感檔案
# CMD ["node", "app.js"] # 以 root 身份運行
# ===== 推薦寫法:多階段建置 + 最小化攻擊面 =====
# Stage 1: Build(安裝依賴、編譯)
FROM node:20-alpine AS builder
WORKDIR /app
# 先複製 lock file,利用 Docker layer cache
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
# 複製原始碼並編譯
COPY src/ ./src/
COPY tsconfig.json ./
RUN npm run build
# 移除 devDependencies,只保留 production 依賴
RUN npm ci --omit=dev --ignore-scripts
# Stage 2: Production(只複製必要的檔案)
FROM node:20-alpine AS production
# 安全加固:安裝安全更新
RUN apk update && apk upgrade --no-cache \
&& rm -rf /var/cache/apk/*
# 建立非 root 使用者
RUN addgroup -g 1001 -S appgroup \
&& adduser -u 1001 -S appuser -G appgroup
WORKDIR /app
# 只從 builder 階段複製 production 需要的東西
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
# 移除不需要的工具(減少攻擊面)
# Alpine 預設已經很精簡,但可以進一步移除 shell(極端情況)
# RUN rm -rf /bin/sh /bin/ash
# 不要在 image 裡存放 secret
# ENV DATABASE_URL="..." ← 絕對不要這樣做
# 使用非 root 使用者執行
USER appuser
# 設定唯讀檔案系統(搭配 docker run --read-only)
# 暴露必要的 port
EXPOSE 3000
# 健康檢查
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/app.js"]多階段建置的核心效果:production image 裡沒有 TypeScript 原始碼、沒有 devDependencies、沒有 build 工具,只有編譯後的 JavaScript 和 production 依賴。image 大小從可能的 1GB+ 降到 150-200MB,可被攻擊的套件數量也大幅減少。加上非 root 使用者執行,即使應用被入侵,攻擊者的權限也被限制在低權限的容器環境裡。
範例五:Harbor 自動掃描設定
Harbor 整合了 Trivy 掃描器,可以在映像檔被 push 時自動觸發掃描,甚至阻止有嚴重漏洞的映像檔被 pull。
# ===== Harbor 安裝時啟用 Trivy =====
# 在 Harbor 安裝目錄下
./install.sh --with-trivy
# ===== 透過 Harbor API 設定自動掃描策略 =====
# 1. 對 Project 啟用「Push 時自動掃描」
curl -X PUT "https://harbor.example.com/api/v2.0/projects/ec" \
-H "Content-Type: application/json" \
-u "admin:${HARBOR_ADMIN_PASSWORD}" \
-d '{
"metadata": {
"auto_scan": "true"
}
}'
# 2. 設定漏洞阻擋策略:阻止 pull 含有 Critical 漏洞的映像檔
curl -X PUT "https://harbor.example.com/api/v2.0/projects/ec" \
-H "Content-Type: application/json" \
-u "admin:${HARBOR_ADMIN_PASSWORD}" \
-d '{
"metadata": {
"auto_scan": "true",
"prevent_vul": "true",
"severity": "critical"
}
}'
# severity 可設定為:critical, high, medium, low
# 設為 "high" 代表 Critical 和 High 等級的漏洞都會被阻擋
# 3. 手動觸發全 Project 掃描(更新 CVE 資料庫後使用)
curl -X POST "https://harbor.example.com/api/v2.0/projects/ec/repositories/api/artifacts/latest/scan" \
-H "Content-Type: application/json" \
-u "admin:${HARBOR_ADMIN_PASSWORD}"
# 4. 查詢掃描結果
curl -s "https://harbor.example.com/api/v2.0/projects/ec/repositories/api/artifacts/latest?with_scan_overview=true" \
-u "admin:${HARBOR_ADMIN_PASSWORD}" | jq '.scan_overview'
# 5. 定期掃描排程(Harbor 2.x 支援)
# 在 Harbor Web UI → Administration → Interrogation Services
# 設定 Vulnerability Scanning 的 Schedule:
# - 每天 02:00 自動掃描所有映像檔
# - 這能確保新發布的 CVE 被及時偵測到Harbor 的自動掃描機制和 CI pipeline 掃描是互補的:CI pipeline 掃描確保 build 當下沒有已知漏洞;Harbor 的定期掃描確保之前 build 時還不存在的新 CVE 也能被偵測到。設定 prevent_vul 後,即使映像檔已經存在於 Registry 裡,只要新的掃描發現了嚴重漏洞,就會阻止後續的 pull 操作,避免有漏洞的映像檔被部署到環境中。
常見問題與風險
-
誤報疲勞(Alert Fatigue from False Positives):掃描工具報了 200 個漏洞,其中 150 個在你的使用場景下根本不會被觸發(例如某個 CVE 只影響特定的函式呼叫路徑,但你沒有用到那個函式)。團隊花大量時間評估每個 CVE 是否真的有影響,最後疲勞了開始忽略所有掃描結果——這比不掃描更危險,因為你會產生「我們有做安全掃描」的虛假安全感。應對方式:使用
.trivyignore檔案記錄經過評估確認為誤報的 CVE,附上評估原因和評估日期,定期 review 這個清單。只對 CRITICAL 和 HIGH 等級設定阻擋,Medium 和 Low 用報告追蹤即可。 -
Base Image 的漏洞不歸你管但要你修:你用了
node:20-alpine,掃描出 Alpine 的libcrypto有 CVE。你能做的只有等上游釋出修補版本然後重新 build。但如果上游遲遲不修,你的 pipeline 就一直過不了。應對方式:選擇更新頻繁的 base image(如 Chainguard Images、Google Distroless);設定--ignore-unfixed忽略沒有修復版本的漏洞;定期重新 build 映像檔以取得最新的安全更新。 -
掃描讓 CI 變慢:Trivy 第一次掃描需要下載漏洞資料庫(約 30-50MB),加上掃描本身可能需要 30 秒到 2 分鐘。如果每次 push 都做完整掃描,CI 時間會顯著增加。應對方式:在 CI Runner 上快取 Trivy DB(設定
TRIVY_CACHE_DIR並用 GitLab cache 保存);非 main branch 的 MR 只做 dependency scan(快),main branch 和 tag 才做完整 image scan(慢);或者把 image scan 改成allow_failure: true,不阻擋 pipeline 但產出報告。 -
供應鏈攻擊防不勝防:掃描工具只能找到「已知漏洞」(已被揭露並有 CVE 編號的)。零時差漏洞、惡意維護者故意植入的後門(如 xz-utils 事件)、typosquatting(惡意套件名稱和知名套件只差一個字母),這些掃描器都抓不到。應對方式:鎖定依賴版本(使用 lock file);啟用 npm 的
--ignore-scripts避免安裝時自動執行惡意腳本;使用 Socket.dev 等新一代工具分析套件行為而非只看已知 CVE。 -
不同工具掃出來的結果不一致:Trivy 說有 50 個漏洞、Snyk 說有 30 個、GitLab built-in 說有 70 個。每個工具用的 CVE 資料庫來源、版本偵測邏輯、嚴重程度評分都不完全相同。團隊不知道該信哪個。應對方式:選定一個工具作為 single source of truth,其他工具的結果作為參考。建議以 Trivy 為主(開源、更新快、社群活躍),搭配 Snyk 的自動修復 PR 功能作為補充。
-
License 合規問題被忽略:團隊專注在漏洞掃描,忽略了授權合規。專案裡用了 GPL-3.0 的套件但沒有開源自己的程式碼,直到收到律師函才發現問題。應對方式:在 CI pipeline 裡加入 SCA 掃描(Trivy 的
--scanners license或專門的 SCA 工具),建立團隊的授權白名單(例如只允許 MIT、Apache-2.0、BSD),有 copyleft 授權的套件需要經過技術主管核准才能使用。
小結
安全掃描不是裝了工具就萬事大吉——它需要融入開發流程才有效果。在 IDE 裡裝 linter 抓硬編碼密碼、在 CI 裡用 Trivy 掃依賴和映像檔、在 Harbor 裡設定自動掃描和漏洞阻擋、在 staging 環境跑 DAST。每一層都是一道防線,任何一道防線漏掉的東西都有機會被下一道攔住。最重要的是團隊文化:安全不是安全團隊的專屬責任,每個開發者都應該在 npm install 一個新套件時想一下——這個套件有多少人在維護?上次更新是什麼時候?它需要什麼權限?這種意識加上自動化工具,才是真正有效的安全策略。