ストーリー
佐藤CTOがSonarQubeのダッシュボードを開きました。
SAST(Static Application Security Testing)
SASTの仕組み
SASTはソースコードやバイトコードを実行せずに解析し、セキュリティ上の脆弱性を検出します。
| 特徴 | 説明 |
|---|---|
| 解析対象 | ソースコード、バイトコード |
| 実行タイミング | ビルド前(コードレビュー時) |
| 検出できるもの | SQLi, XSS, パストラバーサル, バッファオーバーフロー等 |
| 検出できないもの | ランタイムの設定ミス、ビジネスロジックの欠陥 |
| 長所 | 早期発見、全コードパスの網羅的チェック |
| 短所 | False Positive(誤検知)が多い |
主要SASTツール
| ツール | 対応言語 | 特徴 | ライセンス |
|---|---|---|---|
| Semgrep | 多言語 | カスタムルール記述が容易 | OSS + Commercial |
| SonarQube | 多言語 | 品質+セキュリティの統合 | Community/Enterprise |
| CodeQL | 多言語 | GitHub統合、クエリ言語 | OSS (GitHub) |
| ESLint Security | JavaScript/TypeScript | 既存のESLintに追加 | OSS |
| Bandit | Python | Python特化 | OSS |
Semgrepのカスタムルール
# .semgrep/custom-rules.yml
rules:
# SQLインジェクション検出
- id: sql-injection-template-literal
pattern: |
$DB.query(`...${$INPUT}...`)
message: "テンプレートリテラルでのSQL構築は SQLインジェクションのリスクがあります。パラメータ化クエリを使用してください。"
languages: [typescript, javascript]
severity: ERROR
metadata:
category: security
cwe: "CWE-89: SQL Injection"
owasp: "A03:2021 - Injection"
fix: |
$DB.query('... $1 ...', [$INPUT])
# ハードコードされた認証情報
- id: hardcoded-credentials
patterns:
- pattern: |
const $VAR = "$SECRET"
- metavariable-regex:
metavariable: $VAR
regex: (password|secret|apiKey|api_key|token|credential)
- metavariable-regex:
metavariable: $SECRET
regex: .{8,}
message: "ハードコードされた認証情報が検出されました。環境変数またはシークレットマネージャーを使用してください。"
languages: [typescript, javascript]
severity: ERROR
# eval() の使用禁止
- id: no-eval
pattern: eval($X)
message: "eval() の使用はコードインジェクションのリスクがあります。"
languages: [typescript, javascript]
severity: ERROR
metadata:
cwe: "CWE-95: Eval Injection"
# innerHTML の使用検出
- id: no-innerhtml
pattern: $EL.innerHTML = $X
message: "innerHTML の直接代入はXSSのリスクがあります。textContent または DOMPurify を使用してください。"
languages: [typescript, javascript]
severity: WARNING
metadata:
cwe: "CWE-79: Cross-site Scripting"
SonarQubeの設定
// sonar-project.properties
// sonar.projectKey=my-project
// sonar.sources=src
// sonar.tests=tests
// sonar.typescript.lcov.reportPaths=coverage/lcov.info
// sonar.qualitygate.wait=true
// Quality Gateの条件例(TypeScript)
interface SonarQualityGate {
conditions: {
metric: string;
operator: 'GT' | 'LT';
threshold: number;
}[];
}
const securityQualityGate: SonarQualityGate = {
conditions: [
{ metric: 'new_security_rating', operator: 'GT', threshold: 1 }, // A(脆弱性なし)
{ metric: 'new_security_hotspots_reviewed', operator: 'LT', threshold: 100 }, // 全ホットスポットレビュー済み
{ metric: 'new_vulnerabilities', operator: 'GT', threshold: 0 }, // 新規脆弱性ゼロ
{ metric: 'new_coverage', operator: 'LT', threshold: 80 }, // カバレッジ80%以上
],
};
SCA(Software Composition Analysis)
SCAの仕組み
SCAは、プロジェクトが使用するオープンソースライブラリとその依存関係の脆弱性を検出します。
| 特徴 | 説明 |
|---|---|
| 解析対象 | package.json, lock files, コンテナイメージ |
| 脆弱性DB | NVD, GitHub Advisory, Snyk DB |
| 検出対象 | 既知の脆弱性(CVE)、ライセンス違反 |
| 推移的依存関係 | 直接依存だけでなく、間接依存も検出 |
Snyk によるSCA
// .snyk ポリシーファイル
// 特定の脆弱性を一時的に無視する設定
const snykPolicy = {
version: 'v1.25.0',
ignore: {
// 緩和策適用済みの脆弱性を90日間無視
'SNYK-JS-LODASH-1018905': {
reason: 'WAFでサニタイズ済み。次のメジャーアップデートで対応予定。',
expires: '2024-06-30T00:00:00.000Z',
},
},
patch: {},
};
# GitHub Actions での Snyk SCA実行
- name: Snyk Security Check
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: >-
--severity-threshold=high
--fail-on=all
--json-file-output=snyk-results.json
# npm audit の活用
- name: npm audit
run: |
npm audit --audit-level=high --json > audit-results.json || true
# Critical/High の脆弱性がある場合はビルドを失敗させる
CRITICAL=$(cat audit-results.json | jq '.metadata.vulnerabilities.critical')
HIGH=$(cat audit-results.json | jq '.metadata.vulnerabilities.high')
if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ]; then
echo "Critical: $CRITICAL, High: $HIGH vulnerabilities found"
exit 1
fi
SBOM(Software Bill of Materials)
SBOMは、ソフトウェアに含まれる全コンポーネントの一覧です。
# SBOM生成(Syft)
# syft app:latest -o spdx-json > sbom.spdx.json
# SBOM例(SPDX形式の一部)
spdxVersion: "SPDX-2.3"
name: "my-application"
packages:
- name: "express"
versionInfo: "4.18.2"
supplier: "Organization: Express Contributors"
downloadLocation: "https://registry.npmjs.org/express/-/express-4.18.2.tgz"
licenseConcluded: "MIT"
externalRefs:
- referenceCategory: SECURITY
referenceType: cpe23Type
referenceLocator: "cpe:2.3:a:expressjs:express:4.18.2:*:*:*:*:node.js:*:*"
シークレットスキャン
検出対象のシークレットパターン
| パターン | 例 | リスク |
|---|---|---|
| AWS アクセスキー | AKIA... | AWSリソースへの不正アクセス |
| GitHub トークン | ghp_..., github_pat_... | リポジトリの不正操作 |
| JWT署名鍵 | -----BEGIN RSA PRIVATE KEY----- | トークン偽造 |
| データベース接続文字列 | postgresql://user:pass@host/db | データ漏洩 |
| Slack Webhook | https://hooks.slack.com/... | チャネルへの不正投稿 |
GitLeaks の設定
# .gitleaks.toml
title = "Custom Gitleaks Config"
[extend]
useDefault = true
# カスタムルール追加
[[rules]]
id = "custom-internal-api-key"
description = "Internal API Key"
regex = '''internal-api-key-[a-zA-Z0-9]{32}'''
tags = ["key", "internal"]
[[rules]]
id = "custom-database-url"
description = "Database URL with credentials"
regex = '''(postgres|mysql|mongodb)://[^:]+:[^@]+@[^/]+/\w+'''
tags = ["database", "credential"]
# 許可リスト(テストファイル等)
[allowlist]
paths = [
'''tests/fixtures/.*''',
'''docs/examples/.*''',
]
regexTarget = "line"
regexes = [
'''EXAMPLE_.*''',
'''test-api-key-.*''',
]
Pre-commit Hook の設定
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
- repo: https://github.com/semgrep/semgrep
rev: v1.50.0
hooks:
- id: semgrep
args: ['--config', 'p/security-audit', '--error']
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: detect-private-key
- id: detect-aws-credentials
運用のベストプラクティス
False Positive(誤検知)の管理
| 戦略 | 説明 |
|---|---|
| ベースラインの設定 | 既存の指摘事項をベースラインとし、新規のみ報告 |
| 抑制ルール | 明確な誤検知はルールで抑制 |
| トリアージプロセス | セキュリティチャンピオンが定期的にレビュー |
| フィードバック | 誤検知をツールベンダーに報告して改善 |
// 脆弱性トリアージの自動化
interface VulnerabilityTriagePolicy {
autoFix: {
enabled: boolean;
types: string[]; // dependabot auto-merge対象
maxSeverity: string; // auto-fixの上限
};
escalation: {
critical: { notifyChannel: string; sla: string };
high: { notifyChannel: string; sla: string };
medium: { assignTeam: string; sla: string };
low: { trackInBacklog: boolean };
};
}
const triagePolicy: VulnerabilityTriagePolicy = {
autoFix: {
enabled: true,
types: ['patch-update', 'minor-update'],
maxSeverity: 'medium',
},
escalation: {
critical: { notifyChannel: '#security-incidents', sla: '4h' },
high: { notifyChannel: '#security-alerts', sla: '24h' },
medium: { assignTeam: 'security-champions', sla: '7d' },
low: { trackInBacklog: true },
},
};
まとめ
| ポイント | 内容 |
|---|---|
| SAST | ソースコードの静的解析で脆弱性を検出(Semgrep, SonarQube, CodeQL) |
| SCA | 依存ライブラリの既知の脆弱性を検出(Snyk, npm audit) |
| シークレットスキャン | コードに埋め込まれた秘密情報を検出(GitLeaks) |
| SBOM | ソフトウェアの構成要素を一覧化し、サプライチェーンリスクを管理 |
| Pre-commit | コミット前にローカルでスキャンし、問題の混入を防止 |
チェックリスト
- SAST / SCA / シークレットスキャンの違いを説明できる
- Semgrepのカスタムルールを記述できる
- Snykを使った依存関係の脆弱性チェックを設定できる
- GitLeaksでシークレットスキャンを構成できる
- Pre-commit hookでローカルスキャンを設定できる
次のステップへ
次は「DAST・コンテナセキュリティ」を学びます。実行中のアプリケーションに対する動的テストと、コンテナイメージのセキュリティスキャンを身につけましょう。
推定読了時間: 40分