Hari 24: DAST Pertama, 66 Pass 0 Fail
5 min read

Hari 24: DAST Pertama, 66 Pass 0 Fail

ZAP baseline scan via Docker ke SecureBank API. Hasilnya 66 pass, 0 fail, 1 warn. Security headers yang dibikin di Day 15 ternyata bekerja — DAST konfirmasi.

devsecops
dast
owasp-zap
security-headers
docker
dynamic-testing
Share

DAST — Scan yang Beda dari SAST

Selama 23 hari terakhir kita pakai SAST (Semgrep) yang scan source code dan SCA (Trivy) yang scan dependencies. Keduanya scan tanpa menjalankan aplikasi. Hari ini beda — pakai DAST.

DAST (Dynamic Application Security Testing) scan aplikasi yang sedang berjalan. Tools-nya mengirim request ke API, baca response, dan analisis header, cookie, dan behavior. Kalau SAST itu baca kode, DAST itu kayak hacker yang coba-coba kirim request dan lihat reaksinya.

Tool yang dipakai: OWASP ZAP (Zed Attack Proxy). Salah satu DAST tool paling populer, open source, dan punya Docker image yang siap pakai.

Setup — Dari Docker Pull Sampai Scan

Step 1: Build dan Start SecureBank API

Aplikasi di-build dan dijalankan via Docker Compose. Image distroless yang sudah hardened dari Day 16-18 dipakai. API jalan di port 8080.

Step 2: Pull ZAP Docker Image

docker pull ghcr.io/zaproxy/zaproxy:stable

Image-nya sekitar 300MB. Di network yang lagi lambat, butuh waktu lumutan dan 2 layer susah di-download — berkali-kali retry. Tapi akhirnya selesai juga.

Step 3: Run ZAP Baseline Scan

docker run --rm \
  -v "$(pwd)/securebank-api/security:/zap/wrk" \
  ghcr.io/zaproxy/zaproxy:stable \
  zap-baseline.py \
  -t http://host.docker.internal:8080 \
  -r zap-report.html \
  -J zap-report.json \
  -I

Penting buat yang pakai macOS Docker Desktop: --network host tidak berfungsi. Pakai host.docker.internal untuk akses host dari dalam container. Beda dengan Linux yang bisa langsung pakai localhost.

ZAP kemudian spidering aplikasi (crawling semua URL), lalu passive scanning (analisis setiap response yang diterima). Baseline scan tidak melakukan active attack — tidak kirim SQL injection atau XSS payload. Untuk itu butuh zap-full-scan.py.

Hasil — 66 Pass, 0 Fail, 1 Warn

FAIL-NEW: 0  FAIL-INPROG: 0  WARN-NEW: 1  WARN-INPROG: 0  INFO: 0  IGNORE: 0  PASS: 66

Yang Pass (66 checks)

Beberapa check penting yang lolos:

Check ID Name Status
10021 X-Content-Type-Options Header Missing PASS ✅
10035 Strict-Transport-Security Header PASS ✅
10038 Content Security Policy (CSP) Header Not Set PASS ✅
10063 Permissions Policy Header Not Set PASS ✅
10020 Anti-clickjacking Header PASS ✅
10010 Cookie No HttpOnly Flag PASS ✅
10011 Cookie Without Secure Flag PASS ✅
10054 Cookie without SameSite Attribute PASS ✅

Security headers yang diimplementasi di Day 15 (middleware SecurityHeaders) ternyata bekerja. ZAP mengkonfirmasi: CSP, X-Content-Type-Options, X-Frame-Options, X-XSS-Protection — semua terdeteksi di response header.

Yang Warn (1 check)

10049 — Storable and Cacheable Content (Informational/Medium)

Dua URL dikasih warning karena response-nya bisa di-cache:

  • http://host.docker.internal:8080 (root path, 404)
  • http://host.docker.internal:8080/robots.txt (404)

Kenapa? Karena path ini dapat 404 response default dari Go's http.HandleFunc — yang tidak melalui middleware SecurityHeaders. Middleware hanya apply ke route yang explicit didaftarkan: /health, /balance, /transfer. Path lain dapat 404 default tanpa Cache-Control: no-store.

Fix-nya: tambahin catch-all handler yang juga apply middleware ke 404 responses. Akan dikerjakan di Day 26 (DAST Remediation).

Pelajaran yang Didapat

1. DAST dan SAST saling melengkapi, bukan menggantikan. SAST nemuin insecure crypto di source code (md5 → bcrypt, Day 10). DAST nemuin missing cache-control header di 404 response. Keduanya melihat hal yang berbeda — SAST lihat kode, DAST lihat behavior. Pipeline yang ideal punya keduanya.

2. Middleware hanya apply ke route yang didefinisikan. Go's http.HandleFunc bahkan tidak apply middleware ke 404 responses. Setiap request ke path yang nggak didefinisikan dapat response default tanpa security headers. Kalau mau catch-all, perlu custom handler atau http.NotFoundHandler yang juga di-wrap dengan middleware.

3. Baseline scan = passive scan. ZAP baseline scan tidak mengirim payload attack. Hanya spidering + analisis response. Untuk scan yang lebih agresif (active attack), gunakan zap-full-scan.py atau zap-api-scan.py. Untuk API yang punya endpoint terbatas, baseline scan sudah cukup untuk phase pertama.

4. Security headers bekerja di level HTTP response. Bukan teori di kode — ZAP benar-benar membaca response header dan mengkonfirmasi: "ya, header ini ada di response". Ini bedanya DAST dengan SAST — DAST bukti langsung di runtime, bukan analisis static.

5. host.docker.internal bukan localhost di macOS. Docker Desktop di macOS tidak support --network host untuk akses host dari container. Pakai host.docker.internal sebagai hostname. Ini specific ke macOS Docker Desktop, bukan masalah di Linux.

Kesimpulan

Day 24 selesai. DAST pertama dengan OWASP ZAP. 66 dari 67 check pass (98.5%). 0 fail. 1 warn yang akan diperbaiki di Day 26. Security headers yang dibikin sejak Day 15 terbukti bekerja di level runtime.

Beberapa hal yang unik hari ini: Docker pull yang butuh waktu lama karena 2 layer susah ke-download, ZAP local install yang coba dulu tapi akhirnya pakai Docker image yang lebih clean, dan host.docker.internal yang specific ke macOS.

Besok Day 25: DAST di Pipeline — ZAP scan otomatis di GitHub Actions. Supaya setiap push ke repo, DAST scan jalan otomatis. Sampai jumpa besok!


Repo: https://github.com/stayrelevantid/chalange-devsecops

Enjoyed this article? Share it!

Share

Diskusi & Komentar

Artikel Terkait