</>Học Dev
Bài học

Tuần 4 - Ngày 22: SAST, DAST và Secret Scanning

Tuần 4 – Ngày 22

Mục tiêu học tập

  • Hiểu SAST (Static), DAST (Dynamic), IAST (Interactive) — khi nào dùng cái nào
  • Sử dụng SonarQube, Semgrep, CodeQL cho SAST với low false-positive rules
  • Triển khai DAST với OWASP ZAP, Burp Suite, Nuclei cho web app
  • Setup secret scanning: gitleaks, truffleHog, GitHub native secret scanning
  • Tích hợp SCA (Software Composition Analysis) với SBOM/dependency scan
  • Đặt scan trong pre-commit hook (husky) và CI/CD pipeline
  • Thiết kế quy trình fail build + exception cho finding critical

1. SAST vs DAST vs IAST — tổng quan

CodeRunningAppProductionSAST(Static)scansourceDAST(withcaveat)(Dynamic)black-boxfuzzendpointsIAST(Interactive)instrumentedduringtestRASP(RuntimeblockattacksSelf-Protection)atruntime
Tiêu chíSASTDASTIAST
InputSource codeURL/endpoint của appApp instrumented + test traffic
Cần app chạy?Không
False positiveCao (cần tune)Thấp (vuln thật)Rất thấp
Tìm đượcSQLi, XSS pattern, hardcoded secret, insecure crypto APISQLi, XSS, IDOR thực tế qua requestCả hai loại trên + data flow runtime
Ngôn ngữPer-language toolLanguage-agnosticCần agent cho language
Khi nàoMỗi PRNightly hoặc pre-prodTrong test environment
Coverage code100% reachableChỉ endpoint test gọiChỉ code chạy trong test

Best practice: dùng cả 3 lớp — SAST (sớm, broad), DAST (xác minh exploit thật), SCA cho deps (Day 16). IAST là bonus khi có ngân sách.


2. SAST — Static Application Security Testing

Cách hoạt động

SAST đọc source code (hoặc bytecode) và áp rule pattern matching + dataflow analysis:

SourcecodeParserAST/IRRuleengineFindingsPatternmatch(regexAST)Tainttracking(userinputsink)ControlflowanalysisSymboltable,callgraph

Ví dụ rule "SQL injection":

Source: HTTP request param (req.query.id, request.GET[...])
Sink:   raw SQL string ("SELECT ... " + var, cursor.execute(f"..."))
Path:   source → sink without sanitizer → ALERT

Tool phổ biến

ToolLoạiĐiểm mạnhĐiểm yếu
SonarQube / SonarCloudMulti-languageQuality + security, dashboard tốt, free tierRule security limited so với chuyên dụng
SemgrepMulti-languageRule viết bằng YAML pattern, nhanh, community rules tốt, free OSSKhông deep dataflow như CodeQL
CodeQL (GitHub)Multi-languageDataflow analysis sâu, free cho public repoPhức tạp viết custom rule (QL language)
Snyk CodeMulti-languageAI-based, ít FPCommercial
Checkmarx, Veracode, FortifyEnterpriseCoverage rộngĐắt, chậm

Semgrep — ví dụ thực tế

# .semgrep.yml — rule custom phát hiện SQL string concat
rules:
  - id: python-sql-string-concat
    pattern-either:
      - pattern: |
          $CURSOR.execute("..." + $X)
      - pattern: |
          $CURSOR.execute(f"...{$X}...")
    message: |
      SQL string concatenation/f-string detected — dùng parameterized query.
    languages: [python]
    severity: ERROR
# Chạy local
semgrep --config auto .

# Chạy với community rules
semgrep --config "p/security-audit" --config "p/owasp-top-ten" .

# Trong CI (GitHub Actions)
semgrep ci --sarif --output=semgrep.sarif

CodeQL trong GitHub Actions

# .github/workflows/codeql.yml
name: CodeQL
on:
  push: { branches: [main] }
  pull_request: { branches: [main] }
  schedule:
    - cron: "0 0 * * 1"     # weekly Monday

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      actions: read
      contents: read
    strategy:
      matrix:
        language: [javascript, python]
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          queries: security-extended       # broader, more FP nhưng deeper
      - uses: github/codeql-action/analyze@v3

Finding hiện trong tab Security → Code scanning alerts.

Giảm false positive

Anti-pattern: bật tất cả rule "kitchen sink" → 10000 finding → đội ignore tất cả
                                                              → SAST mất tác dụng

Best practice:
  1. Start với "security-audit" hoặc "OWASP Top 10" rules (high signal)
  2. Triage: dismiss false-positive với lý do (codeql có suppress comment)
  3. Tune custom rule cho domain (vd: pattern crypto công ty đã chuẩn hoá)
  4. Severity-based gating: chỉ fail build ở HIGH/CRITICAL
  5. Diff scan trong PR (chỉ check code thay đổi) thay vì full scan

3. DAST — Dynamic Application Security Testing

Cách hoạt động

DAST chạy như attacker bên ngoài: gửi request có payload độc hại, quan sát response.

DASTScannerHTTPrequestRunningAppResponseCrawlendpointDatabase/dependenciesInjectpayload(SQLi,XSS,SSRF,commandinjection)Detectanomaly(errortrace,timedelay,blindboolean)Reportfinding

Tool

ToolLoạiUse case
OWASP ZAPFree, open-sourceStandard pick, CI integration tốt
Burp SuiteFree Community + ProManual pentesting, intercept proxy
NucleiTemplate-basedFast scan với cộng đồng template (CVE, misconfig)
Acunetix, NetsparkerCommercialEnterprise, GUI-friendly

OWASP ZAP trong CI

# .github/workflows/zap.yml
name: ZAP Baseline Scan
on:
  schedule:
    - cron: "0 2 * * *"       # nightly 2 AM
  workflow_dispatch:

jobs:
  zap:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: ZAP Baseline Scan
        uses: zaproxy/action-baseline@v0.12.0
        with:
          target: 'https://staging.example.com'
          rules_file_name: '.zap/rules.tsv'
          cmd_options: '-a'                 # active scan (not just passive)

ZAP có 2 mode:

  • Baseline (passive): chỉ analyze response, không gửi payload → an toàn cho prod
  • Active scan: inject payload (SQLi, XSS) → chỉ chạy trên staging/test

Nuclei — template-based fast scan

# Chạy template CVE phổ biến
nuclei -u https://example.com -t cves/ -t vulnerabilities/

# Template ví dụ (.yaml):
# id: nginx-default-page
# requests:
#   - method: GET
#     path: ["{{BaseURL}}"]
#     matchers:
#       - type: word
#         words: ["Welcome to nginx!"]

Cộng đồng có hàng nghìn template (https://github.com/projectdiscovery/nuclei-templates). Chạy trong CI để check infrastructure misconfig.

Authenticated scan

DAST mặc định scan như anonymous → bỏ sót lỗ hổng sau login. Cấu hình ZAP với session token:

# ZAP context với auth
auth:
  loginUrl: https://staging/login
  loggedInIndicatorRegex: "Logout"
  username: dast-user@example.com
  password: ${{ secrets.DAST_PASSWORD }}

Cảnh báo: tạo user riêng cho DAST, không dùng admin (DAST có thể fire DELETE /admin/users test).


4. IAST — Interactive

IAST = SAST insight + DAST reality. Một agent (instrumentation) embed vào app trong test environment, theo dõi:

  • Source: dữ liệu user vào (request param)
  • Sink: SQL execute, exec system command, eval
  • Flow: trace data từ source qua function calls đến sink

Khi test integration chạy, IAST agent log thấy req.body.id chảy đến db.query("SELECT ... " + id) không qua sanitizer → finding precise (line cụ thể + request thực).

Tools: Contrast Security, Seeker (Synopsys), Veracode IAST. Open-source ít. Yêu cầu instrumentation runtime — hỗ trợ Java, .NET, Node, Python nhưng config khác nhau.

Khi nào dùng: team có integration test coverage tốt, muốn finding low FP, có ngân sách commercial tool. Không thay thế SAST — bổ sung.


5. Secret scanning trong CI

Bài toán

Developer commit .env chứa AWS key → push lên GitHub public → 60 giây sau bot quét xong và bắt đầu mining crypto trên account bạn. Đây là sự cố phổ biến nhất, không phải hypothetical.

Tool

ToolLoạiĐặc điểm
gitleaksOSS Go binaryNhanh, custom rule TOML, dễ tích hợp CI
truffleHogOSS PythonEntropy detection + regex, verify secret còn live
GitHub Secret ScanningNativeFree cho public repo, có partner program (AWS, Stripe... auto-revoke)
GitGuardianCommercialDashboard, alerting, full history scan

gitleaks trong pre-commit và CI

# Cài
brew install gitleaks

# Scan working dir + staged
gitleaks detect --source . --verbose

# Scan toàn bộ history
gitleaks detect --source . --log-opts="--all"
# .pre-commit-config.yaml (dùng với pre-commit framework)
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
# CI: .github/workflows/secret-scan.yml
name: Secret Scan
on: [pull_request, push]
jobs:
  gitleaks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }       # full history
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GitHub native secret scanning

Bật trong Settings → Security → Code security:

  • Secret scanning: GitHub scan commit + PR, alert maintainer
  • Push protection: chặn push nếu có secret pattern match
  • Partner program: GitHub forward leaked key cho provider (AWS, GCP, Stripe...) — provider auto-revoke và notify

Free cho public repo từ 2023. Private repo cần GitHub Advanced Security.

Xử lý secret đã leak

SAI: commit "revert" — secret vẫn trong git history
ĐÚNG:
  1. Revoke/rotate secret NGAY (AWS console, Stripe dashboard...)
  2. Rotate trước, dọn history sau (history rewrite không xoá khỏi forks/clones)
  3. Notify team — ai dùng key đó cần lấy key mới
  4. Audit log provider: kiểm tra xem key đã bị abuse chưa
  5. (Optional) git filter-repo / BFG để xoá khỏi history (nếu repo private)

6. SCA — Software Composition Analysis

(Đã học sâu ở Day 16 — Dependency Security & SBOM. Tóm tắt vai trò trong DevSecOps pipeline.)

SCA scan dependencies (npm, pip, Maven, Go modules) vs CVE database:

ToolĐặc điểm
Dependabot (GitHub)Free, auto-PR khi có CVE
SnykMulti-ecosystem, prioritization tốt
TrivyOSS, scan cả container và dep
OWASP Dependency-CheckFree, OWASP project
OSV-Scanner (Google)Dùng OSV database, multi-ecosystem

SCA + SBOM: generate CycloneDX/SPDX SBOM cho mỗi build (syft, cdxgen), attach release. Đáp ứng Executive Order 14028 cho US gov và SLSA framework.


7. Pre-commit hook: client-side scan

Git hooks vs Husky

Git hooks: .git/hooks/pre-commit — bash script, không version control mặc định, mỗi dev phải cài tay.

Husky (Node ecosystem): commit hook lưu trong .husky/ (version control), npm install tự setup.

pre-commit framework (Python, đa ngôn ngữ): config YAML, install hook tự động.

Husky setup

npm install --save-dev husky lint-staged
npx husky init
// package.json
{
  "scripts": {
    "prepare": "husky"
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}
# .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# 1. Lint + format
npx lint-staged

# 2. Secret scan staged files
gitleaks protect --staged --verbose --redact

# 3. Quick SAST trên file thay đổi
git diff --cached --name-only --diff-filter=ACM | \
  xargs -r semgrep --config p/security-audit --error

Trade-off pre-commit

ProsCons
Catch sớm trước khi commitChậm commit experience
Bảo vệ developer khỏi sai sótDev có thể bypass với --no-verify
Không cần CI quay vòngKhông thay thế CI (CI authoritative)

Quy tắc: pre-commit là defense in depth, không phải defense duy nhất. CI vẫn phải scan độc lập (vì --no-verify bypass được).


8. CI/CD: fail build và exception process

Severity-based gating

# Ví dụ: fail nếu HIGH/CRITICAL, warn nếu MEDIUM
- name: Trivy
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: app:latest
    severity: HIGH,CRITICAL
    exit-code: 1                        # fail build
    ignore-unfixed: true                # bỏ qua CVE chưa có fix

Exception process (allow-list có kiểm soát)

Không phải mọi finding đều fix được ngay (vendor chưa patch, false positive). Cần process để chấp nhận có thời hạn:

# .trivyignore — CVE allow-list với reason + expiry
CVE-2024-12345 exp:2025-01-31    # vendor chưa fix, mitigated by NetworkPolicy
CVE-2024-67890 exp:2025-03-15    # FP — không reachable trong code path
# Gitleaks allow-list trong .gitleaks.toml
[allowlist]
description = "Test fixtures with fake credentials"
paths = [
  '''tests/fixtures/.*''',
]
commits = [
  '''abc123...''',                     # specific commit reviewed
]

Exception governance

1. Finding xuất hiện → CI fail
2. Developer mở "Exception Request" (Jira/Backlog ticket)
3. Security team review:
   - Reason hợp lý? (FP, vendor delay, business critical)
   - Mitigation? (WAF rule, NetworkPolicy, monitoring)
   - Expiry? (30d, 90d — không vĩnh viễn)
4. Approved → thêm vào allow-list với expiry
5. CI có job nightly check expiry, re-open ticket nếu hết hạn

Không có exception process: dev sẽ bypass (--no-verify, comment out scan) → security debt tăng không kiểm soát.


9. Câu hỏi ôn tập

  1. SAST và DAST tìm cùng loại bug (vd SQLi). Khi nào kết quả khác nhau?

    Xem đáp án
    • SAST FP, DAST không: SAST flag db.query("SELECT " + x) nhưng x thực ra là enum cố định trong code → không exploitable. DAST không thấy vì không có endpoint nào pass user input đến đó.
    • DAST tìm thấy, SAST miss: lỗ hổng nằm trong dependency (vd ORM bug) — code app sạch, SAST không scan deps. Hoặc lỗ hổng cấu hình runtime (vd CORS misconfig) — không có trong source code.
    • Cả hai thấy: bug "kinh điển" — req.body.id đi thẳng vào raw SQL.

    Vì vậy cần cả hai lớp + SCA cho deps để cover được different surface.

  2. Tại sao SAST có FP rate cao và làm sao giảm?

    Xem đáp án

    SAST scan code không chạy → không biết runtime context. Một vài nguyên nhân FP:

    • Variable trông nguy hiểm nhưng đã sanitize ở chỗ khác (rule không track được)
    • Code dead (chưa bao giờ chạy)
    • Pattern match quá rộng (vd: bất kỳ eval đều flag, dù eval chỉ trên constant)

    Giảm FP: (1) bắt đầu với rule set tinh (OWASP Top 10, security-audit) thay vì "all rules", (2) tune custom rule cho codebase, (3) suppress với reason ghi rõ (CodeQL có // codeql[js/sql-injection]), (4) chỉ fail build ở HIGH/CRITICAL, (5) diff scan PR thay vì full repo scan.

  3. Khác nhau giữa ZAP baseline scan và active scan? Nguy hiểm gì khi nhầm môi trường?

    Xem đáp án
    • Baseline (passive): chỉ analyze response từ traffic ZAP tự crawl, không gửi payload độc hại. An toàn để chạy trên staging hoặc thậm chí production (nếu rate-limit).
    • Active scan: gửi SQLi, XSS, command injection payload. Có thể tạo dữ liệu rác, gọi DELETE endpoint, crash service.

    Nguy hiểm: chạy active scan trên production → có thể xoá dữ liệu thật, trigger order/email với payload <script>, gây outage. Luôn verify URL target trước khi chạy active. Tạo user DAST riêng với quyền hạn hẹp.

  4. Tại sao chỉ pre-commit hook không đủ để scan secret?

    Xem đáp án

    Pre-commit chạy trên máy dev. Dev có thể bypass bằng git commit --no-verify (cố ý hoặc vô tình). Pre-commit cũng không có nếu dev không cài Husky/pre-commit framework (lần đầu clone repo, hoặc dùng GitHub web editor).

    Cần defense in depth: (1) pre-commit để bắt sớm, (2) CI scan độc lập (authoritative gate), (3) GitHub native secret scanning + push protection (chặn ngay từ git push). Push protection là lớp cuối cùng — chặn trước khi commit vào shared remote.

  5. Khi gitleaks báo có AWS key trong commit cũ, hành động đầu tiên đúng là gì?

    Xem đáp án

    Revoke/rotate key NGAY trên AWS console (deactivate access key cũ, tạo key mới). Lý do: key đã ở trong git history, có thể đã được public hoá qua fork/clone/mirror trong nhiều giây/phút. Rewrite git history (BFG, filter-repo) không giải quyết — vì:

    • Forks/clones existing vẫn có history cũ
    • GitHub cache, archive (archive.org) có thể đã copy
    • Bot scan public repo thường tìm key trong < 60 giây

    Sau khi rotate: audit CloudTrail xem key đã bị dùng cho action gì, notify team có dùng key đó, sau đó mới history rewrite nếu cần.

Bài tập thực hành

# 1. Cài và chạy gitleaks
brew install gitleaks
gitleaks detect --source . --verbose

# Tạo "honeypot" commit để test
echo 'AWS_KEY="AKIAIOSFODNN7EXAMPLE"' > .env.test
git add .env.test
gitleaks protect --staged --verbose
# → Finding: AWS access key

# 2. Cài Semgrep và scan
pipx install semgrep        # hoặc brew install semgrep
semgrep --config auto .

# Hoặc rule set chuyên security
semgrep --config "p/security-audit" --config "p/owasp-top-ten" .

# 3. Setup pre-commit framework
pipx install pre-commit
cat > .pre-commit-config.yaml <<'EOF'
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
  - repo: https://github.com/returntocorp/semgrep
    rev: v1.50.0
    hooks:
      - id: semgrep
        args: ["--config", "p/security-audit", "--error"]
EOF
pre-commit install
pre-commit run --all-files

# 4. Chạy ZAP baseline (Docker)
docker run --rm -v $(pwd):/zap/wrk -t \
  ghcr.io/zaproxy/zaproxy:stable \
  zap-baseline.py -t https://example.com -r zap-report.html

# 5. Chạy Nuclei
brew install nuclei
nuclei -u https://example.com -t cves/

# 6. Setup GitHub workflow tổng hợp
# Copy snippet ở mục 2 và 5 vào .github/workflows/

Tài liệu tham khảo chính thức


Bài tiếp theo →