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
| Tiêu chí | SAST | DAST | IAST |
|---|---|---|---|
| Input | Source code | URL/endpoint của app | App instrumented + test traffic |
| Cần app chạy? | Không | Có | Có |
| False positive | Cao (cần tune) | Thấp (vuln thật) | Rất thấp |
| Tìm được | SQLi, XSS pattern, hardcoded secret, insecure crypto API | SQLi, XSS, IDOR thực tế qua request | Cả hai loại trên + data flow runtime |
| Ngôn ngữ | Per-language tool | Language-agnostic | Cần agent cho language |
| Khi nào | Mỗi PR | Nightly hoặc pre-prod | Trong test environment |
| Coverage code | 100% reachable | Chỉ endpoint test gọi | Chỉ 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:
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
| Tool | Loại | Điểm mạnh | Điểm yếu |
|---|---|---|---|
| SonarQube / SonarCloud | Multi-language | Quality + security, dashboard tốt, free tier | Rule security limited so với chuyên dụng |
| Semgrep | Multi-language | Rule viết bằng YAML pattern, nhanh, community rules tốt, free OSS | Không deep dataflow như CodeQL |
| CodeQL (GitHub) | Multi-language | Dataflow analysis sâu, free cho public repo | Phức tạp viết custom rule (QL language) |
| Snyk Code | Multi-language | AI-based, ít FP | Commercial |
| Checkmarx, Veracode, Fortify | Enterprise | Coverage 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.
Tool
| Tool | Loại | Use case |
|---|---|---|
| OWASP ZAP | Free, open-source | Standard pick, CI integration tốt |
| Burp Suite | Free Community + Pro | Manual pentesting, intercept proxy |
| Nuclei | Template-based | Fast scan với cộng đồng template (CVE, misconfig) |
| Acunetix, Netsparker | Commercial | Enterprise, 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
| Tool | Loại | Đặc điểm |
|---|---|---|
| gitleaks | OSS Go binary | Nhanh, custom rule TOML, dễ tích hợp CI |
| truffleHog | OSS Python | Entropy detection + regex, verify secret còn live |
| GitHub Secret Scanning | Native | Free cho public repo, có partner program (AWS, Stripe... auto-revoke) |
| GitGuardian | Commercial | Dashboard, 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 |
| Snyk | Multi-ecosystem, prioritization tốt |
| Trivy | OSS, scan cả container và dep |
| OWASP Dependency-Check | Free, 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
| Pros | Cons |
|---|---|
| Catch sớm trước khi commit | Chậm commit experience |
| Bảo vệ developer khỏi sai sót | Dev có thể bypass với --no-verify |
| Không cần CI quay vòng | Khô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-verifybypass đượ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
-
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ưngxthự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.
- SAST FP, DAST không: SAST flag
-
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ùevalchỉ 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. -
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. -
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.
-
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
- OWASP SAST tools
- Semgrep registry
- CodeQL documentation
- OWASP ZAP user guide
- Nuclei templates
- gitleaks
- GitHub secret scanning