SiamCafe.net Blog
Cybersecurity

OWASP ZAP Progressive Delivery — ระบบสแกนช่องโหว่อัตโนมัติสำหรับ Canary Deployment

owasp zap progressive delivery
OWASP ZAP Progressive Delivery | SiamCafe Blog
2026-03-15· อ. บอม — SiamCafe.net· 9,549 คำ

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 อัตโนมัติ

📖 บทความที่เกี่ยวข้อง

OWASP ZAP Citizen Developerอ่านบทความ → CSS Nesting Progressive Deliveryอ่านบทความ → OWASP ZAP Shift Left Securityอ่านบทความ → OWASP ZAP Production Setup Guideอ่านบทความ → GitHub Actions Matrix Progressive Deliveryอ่านบทความ →

📚 ดูบทความทั้งหมด →