OWASP ZAP คืออะไรและใช้งานกับ Progressive Delivery อย่างไร
OWASP ZAP (Zed Attack Proxy) เป็นเครื่องมือทดสอบความปลอดภัยของเว็บแอปพลิเคชันแบบ Open Source ที่ได้รับความนิยมสูงสุดในโลก พัฒนาภายใต้โครงการ OWASP (Open Web Application Security Project) รองรับการสแกนช่องโหว่ทั้งแบบ Passive Scan ที่ตรวจจับปัญหาจาก HTTP Traffic โดยไม่ส่ง request เพิ่ม และ Active Scan ที่ทดสอบช่องโหว่ด้วยการส่ง payload ต่างๆเข้าไปทดสอบระบบจริง
Progressive Delivery เป็นแนวคิดการ deploy แอปพลิเคชันแบบค่อยๆเปิดให้ผู้ใช้เข้าถึงทีละส่วน เช่น Canary Deployment ที่ส่ง traffic เพียง 5% ไปยังเวอร์ชันใหม่ก่อน ถ้าไม่มีปัญหาค่อยเพิ่มเป็น 25%, 50% จนถึง 100% แนวคิดนี้ช่วยลดความเสี่ยงจากการ deploy เวอร์ชันที่มีบั๊กหรือช่องโหว่ด้านความปลอดภัย
การนำ OWASP ZAP มาใช้ร่วมกับ Progressive Delivery จะช่วยให้ระบบตรวจจับช่องโหว่ด้านความปลอดภัยตั้งแต่ขั้นตอน Canary ก่อนที่จะ rollout ไปยังผู้ใช้ทั้งหมด ถ้า ZAP พบช่องโหว่ร้ายแรงระบบจะ rollback อัตโนมัติโดยไม่กระทบผู้ใช้ส่วนใหญ่
สถาปัตยกรรมของระบบประกอบด้วย 3 ส่วนหลักคือ ZAP Daemon ที่ทำงานเป็น background service, Automation Framework ที่กำหนดขั้นตอนการสแกน และ CI/CD Pipeline ที่เชื่อมต่อกับ Flagger หรือ Argo Rollouts เพื่อตัดสินใจ promote หรือ rollback
ติดตั้ง OWASP ZAP บน Linux และ Docker
วิธีที่แนะนำสำหรับการใช้งาน ZAP ใน CI/CD คือรันผ่าน Docker เพราะไม่ต้องติดตั้ง dependency และเลือกเวอร์ชันได้ง่าย
# ดึง Docker Image ของ ZAP (stable)
docker pull ghcr.io/zaproxy/zaproxy:stable
# รัน ZAP แบบ Daemon Mode (headless)
docker run -d --name zap \
-p 8080:8080 \
-v $(pwd)/wrk:/zap/wrk:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap.sh -daemon -host 0.0.0.0 -port 8080 \
-config api.addrs.addr.name=.* \
-config api.addrs.addr.regex=true \
-config api.key=my-zap-api-key
# ตรวจสอบว่า ZAP พร้อมใช้งาน
curl http://localhost:8080/JSON/core/view/version/
# Output: {"version":"2.15.0"}
# ติดตั้งบน Ubuntu โดยตรง (ไม่ใช้ Docker)
sudo apt install default-jre -y
wget https://github.com/zaproxy/zaproxy/releases/download/v2.15.0/ZAP_2.15.0_Linux.tar.gz
tar xzf ZAP_2.15.0_Linux.tar.gz
cd ZAP_2.15.0
./zap.sh -daemon -port 8080
สำหรับ Kubernetes สามารถ deploy ZAP เป็น sidecar container หรือ standalone pod ที่ทำงานร่วมกับ CI/CD pipeline ได้
# zap-deployment.yaml สำหรับ Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: zap-scanner
namespace: security
spec:
replicas: 1
selector:
matchLabels:
app: zap-scanner
template:
metadata:
labels:
app: zap-scanner
spec:
containers:
- name: zap
image: ghcr.io/zaproxy/zaproxy:stable
command: ["zap.sh"]
args:
- "-daemon"
- "-host"
- "0.0.0.0"
- "-port"
- "8080"
- "-config"
- "api.addrs.addr.name=.*"
- "-config"
- "api.addrs.addr.regex=true"
ports:
- containerPort: 8080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "2000m"
---
apiVersion: v1
kind: Service
metadata:
name: zap-scanner
namespace: security
spec:
selector:
app: zap-scanner
ports:
- port: 8080
targetPort: 8080
ตั้งค่า ZAP Automation Framework สำหรับ CI/CD
ZAP Automation Framework เป็นวิธีที่แนะนำสำหรับการรัน ZAP ใน CI/CD แทนที่จะใช้ script แบบเดิม ทุกอย่างกำหนดผ่าน YAML file ทำให้ version control ได้และ reproducible
# zap-automation.yaml
env:
contexts:
- name: "target-app"
urls:
- "https://canary.example.com"
includePaths:
- "https://canary.example.com/.*"
excludePaths:
- "https://canary.example.com/logout.*"
- "https://canary.example.com/static/.*"
authentication:
method: "browser"
parameters:
loginPageUrl: "https://canary.example.com/login"
loginPageWait: 5
verification:
method: "response"
pollFrequency: 60
loggedInRegex: "\\QDashboard\\E"
users:
- name: "test-user"
credentials:
username: ""
password: ""
parameters:
failOnError: true
failOnWarning: false
progressToStdout: true
jobs:
- type: passiveScan-config
parameters:
maxAlertsPerRule: 10
scanOnlyInScope: true
- type: spider
parameters:
context: "target-app"
user: "test-user"
maxDuration: 5
maxDepth: 5
maxChildren: 10
- type: spiderAjax
parameters:
context: "target-app"
user: "test-user"
maxDuration: 5
maxCrawlDepth: 3
- type: passiveScan-wait
parameters:
maxDuration: 10
- type: activeScan
parameters:
context: "target-app"
user: "test-user"
maxRuleDurationInMins: 5
maxScanDurationInMins: 30
policy: "API-Scan"
- type: report
parameters:
template: "traditional-json"
reportDir: "/zap/wrk/reports"
reportFile: "zap-report"
risks:
- high
- medium
- low
รัน Automation Framework ด้วยคำสั่ง
# รัน ZAP Automation Framework
docker run --rm \
-v $(pwd):/zap/wrk:rw \
ghcr.io/zaproxy/zaproxy:stable \
zap.sh -cmd -autorun /zap/wrk/zap-automation.yaml
# ดูผลลัพธ์
cat reports/zap-report.json | python3 -m json.tool | head -50
รวม ZAP เข้ากับ Canary Deployment ด้วย Flagger
Flagger เป็น Progressive Delivery Operator สำหรับ Kubernetes ที่ทำงานร่วมกับ Istio, Linkerd หรือ Nginx Ingress ในการทำ Canary Deployment อัตโนมัติ เราจะตั้งค่าให้ Flagger เรียก ZAP scan ในแต่ละขั้นตอนของ Canary
# canary.yaml - Flagger Canary Resource
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: my-app
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
service:
port: 80
targetPort: 8080
gateways:
- public-gateway
hosts:
- app.example.com
analysis:
interval: 2m
threshold: 3
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: zap-security-scan
type: pre-rollout
url: http://zap-webhook.security:9090/scan
timeout: 300s
metadata:
type: "baseline"
target: "http://my-app-canary.production:8080"
- name: zap-gate-check
type: rollout
url: http://zap-webhook.security:9090/gate
timeout: 60s
metadata:
max_high: "0"
max_medium: "3"
สร้าง Webhook Server ที่รับคำสั่งจาก Flagger แล้วเรียก ZAP scan
# zap-webhook-server.py
from flask import Flask, request, jsonify
import requests
import json
import os
app = Flask(__name__)
ZAP_API = os.getenv("ZAP_API_URL", "http://zap-scanner:8080")
ZAP_KEY = os.getenv("ZAP_API_KEY", "my-zap-api-key")
@app.route("/scan", methods=["POST"])
def trigger_scan():
data = request.json
target = data.get("metadata", {}).get("target", "")
if not target:
return jsonify({"error": "no target"}), 400
# เริ่ม Spider
r = requests.get(f"{ZAP_API}/JSON/spider/action/scan/",
params={"apikey": ZAP_KEY, "url": target, "maxChildren": 10})
scan_id = r.json().get("scan")
# รอ Spider เสร็จ
while True:
status = requests.get(f"{ZAP_API}/JSON/spider/view/status/",
params={"apikey": ZAP_KEY, "scanId": scan_id}).json()
if int(status.get("status", 0)) >= 100:
break
# เริ่ม Active Scan
r = requests.get(f"{ZAP_API}/JSON/ascan/action/scan/",
params={"apikey": ZAP_KEY, "url": target})
return jsonify({"status": "scan_started", "target": target})
@app.route("/gate", methods=["POST"])
def gate_check():
data = request.json
max_high = int(data.get("metadata", {}).get("max_high", 0))
max_medium = int(data.get("metadata", {}).get("max_medium", 5))
alerts = requests.get(f"{ZAP_API}/JSON/alert/view/alertsSummary/",
params={"apikey": ZAP_KEY}).json()
summary = alerts.get("alertsSummary", {})
high_count = int(summary.get("High", 0))
medium_count = int(summary.get("Medium", 0))
if high_count > max_high:
return jsonify({"error": f"BLOCKED: {high_count} high alerts"}), 403
if medium_count > max_medium:
return jsonify({"error": f"BLOCKED: {medium_count} medium alerts"}), 403
return jsonify({"status": "passed", "high": high_count, "medium": medium_count})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9090)
สแกนช่องโหว่อัตโนมัติในแต่ละ Deployment Stage
ในระบบ Progressive Delivery ที่ใช้ Argo Rollouts สามารถตั้งค่า Analysis Template ให้เรียก ZAP scan ในแต่ละ step ของ rollout ได้
# argo-rollout-with-zap.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: my-app
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 2m}
- analysis:
templates:
- templateName: zap-security-analysis
args:
- name: canary-url
value: "http://my-app-canary:8080"
- setWeight: 30
- pause: {duration: 5m}
- analysis:
templates:
- templateName: zap-security-analysis
args:
- name: canary-url
value: "http://my-app-canary:8080"
- setWeight: 60
- pause: {duration: 5m}
- setWeight: 100
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: zap-security-analysis
spec:
args:
- name: canary-url
metrics:
- name: zap-high-alerts
interval: 5m
successCondition: result == 0
failureLimit: 0
provider:
job:
spec:
template:
spec:
containers:
- name: zap-scan
image: ghcr.io/zaproxy/zaproxy:stable
command:
- /bin/bash
- -c
- |
zap.sh -daemon -port 8080 &
sleep 15
zap-cli -p 8080 quick-scan {{args.canary-url}}
HIGH=$(zap-cli -p 8080 alerts -l High | wc -l)
echo $HIGH > /dev/termination-log
exit 0
restartPolicy: Never
สำหรับ GitLab CI/CD สามารถรวม ZAP scan เข้าไปใน pipeline ได้ดังนี้
# .gitlab-ci.yml
stages:
- build
- deploy-canary
- security-scan
- promote
security-scan:
stage: security-scan
image: ghcr.io/zaproxy/zaproxy:stable
variables:
TARGET_URL: "https://canary.example.com"
script:
- mkdir -p /zap/wrk/reports
- zap.sh -cmd -autorun /zap/wrk/zap-automation.yaml
- |
HIGH_COUNT=$(python3 -c "
import json
with open('/zap/wrk/reports/zap-report.json') as f:
data = json.load(f)
alerts = [a for a in data.get('site', [{}])[0].get('alerts', []) if a.get('riskcode') == '3']
print(len(alerts))
")
if [ "$HIGH_COUNT" -gt 0 ]; then
echo "SECURITY GATE FAILED: $HIGH_COUNT high-risk alerts found"
exit 1
fi
artifacts:
paths:
- reports/
when: always
allow_failure: false
วิเคราะห์ผลสแกนและตั้ง Policy Gate
หลังจากสแกนเสร็จ ZAP จะสร้างรายงานที่ประกอบด้วย alerts จำแนกตามระดับความเสี่ยง 4 ระดับคือ High, Medium, Low และ Informational การตั้ง Policy Gate ที่ดีควรบล็อก deployment เมื่อพบ High alert แม้แต่ 1 รายการ และกำหนด threshold สำหรับ Medium alert ตามความเหมาะสม
สร้าง script สำหรับตรวจสอบผลสแกนและตัดสินใจ
#!/usr/bin/env python3
# zap-policy-gate.py
import json
import sys
import requests
ZAP_API = "http://localhost:8080"
API_KEY = "my-zap-api-key"
POLICY = {
"high_max": 0,
"medium_max": 3,
"low_max": 20,
"ignore_rules": [10096, 10027], # Timestamp Disclosure, Info Leakage
}
def check_alerts():
r = requests.get(f"{ZAP_API}/JSON/core/view/alerts/",
params={"apikey": API_KEY, "start": 0, "count": 500})
alerts = r.json().get("alerts", [])
high, medium, low = [], [], []
for alert in alerts:
rule_id = int(alert.get("pluginId", 0))
if rule_id in POLICY["ignore_rules"]:
continue
risk = int(alert.get("riskcode", 0))
if risk == 3:
high.append(alert)
elif risk == 2:
medium.append(alert)
elif risk == 1:
low.append(alert)
print(f"=== ZAP Security Gate Results ===")
print(f"High: {len(high)} (max: {POLICY['high_max']})")
print(f"Medium: {len(medium)} (max: {POLICY['medium_max']})")
print(f"Low: {len(low)} (max: {POLICY['low_max']})")
if len(high) > POLICY["high_max"]:
print(f"\nBLOCKED: {len(high)} high-risk vulnerabilities found!")
for a in high:
print(f" - {a['alert']}: {a['url']}")
return False
if len(medium) > POLICY["medium_max"]:
print(f"\nBLOCKED: {len(medium)} medium-risk vulnerabilities found!")
return False
print("\nPASSED: Security gate check passed")
return True
if __name__ == "__main__":
passed = check_alerts()
sys.exit(0 if passed else 1)
สำหรับการจัดการ False Positive สามารถสร้าง Alert Filter ใน ZAP Automation Framework เพื่อไม่ให้ alerts ที่ไม่เกี่ยวข้องบล็อก deployment
# เพิ่มใน zap-automation.yaml
jobs:
- type: alertFilter
parameters:
deleteGlobalAlerts: true
alertFilters:
- ruleId: 10096 # Timestamp Disclosure
newRisk: "False Positive"
- ruleId: 10027 # Information Disclosure
newRisk: "Info"
context: "target-app"
url: "https://canary.example.com/api/health"
urlRegex: false
FAQ คำถามที่พบบ่อย
Q: OWASP ZAP กับ Burp Suite ต่างกันอย่างไร?
A: ZAP เป็น Open Source ฟรีทั้งหมดเหมาะกับการรันใน CI/CD pipeline แบบอัตโนมัติ ส่วน Burp Suite มีเวอร์ชัน Community (ฟรีแต่จำกัดความสามารถ) และ Professional (เสียเงิน) ที่มี scanner ดีกว่าในบางด้าน สำหรับ Progressive Delivery แนะนำ ZAP เพราะรองรับ Automation Framework และ Docker ได้ดีกว่า
Q: ZAP Active Scan ใช้เวลานานแค่ไหนและจะเร่งความเร็วได้อย่างไร?
A: ขึ้นอยู่กับขนาดของแอปพลิเคชัน โดยทั่วไป 5-30 นาทีสำหรับแอปขนาดเล็กถึงกลาง เร่งความเร็วได้ด้วยการจำกัด scope เฉพาะ endpoint ที่เปลี่ยนแปลง ใช้ Scan Policy ที่เลือกเฉพาะ rule ที่จำเป็น และตั้ง maxRuleDurationInMins ให้เหมาะสม
Q: ควรรัน ZAP scan ทุกครั้งที่ deploy หรือไม่?
A: แนะนำให้รัน Passive Scan ทุกครั้งเพราะใช้เวลาน้อยและไม่กระทบ performance ส่วน Active Scan ควรรันเฉพาะเมื่อมีการเปลี่ยนแปลง code ที่เกี่ยวกับ security เช่น authentication หรือ authorization หรือรันเป็น nightly scan
Q: ZAP สามารถสแกน API (REST/GraphQL) ได้ไหม?
A: ได้ ZAP รองรับการ import OpenAPI/Swagger spec และ GraphQL schema สำหรับสแกน API โดยเฉพาะ ใช้ job type openapi หรือ graphql ใน Automation Framework เพื่อ import schema แล้วสแกนทุก endpoint อัตโนมัติ
