ストーリー
田
田中VPoE
Step 1でセキュリティ要件を定義した。ここからはそれを自動で検証する仕組みを作る。まずはSAST — 静的アプリケーションセキュリティテストだ
あなた
SASTは「コードを実行せずにソースコードを解析して脆弱性を見つける」ものですよね
あ
田
田中VPoE
その通り。SASTはShift Leftの最前線だ。開発者がコードを書いた瞬間、PRを出した瞬間に脆弱性を検出できる。リリース前のゲートレビューを待たずにフィードバックが返る
あなた
ESLintのセキュリティ版のようなイメージですか?
あ
田
田中VPoE
近い。ただし、ESLintが構文レベルの問題を見つけるのに対し、SASTツールはデータフローを追跡する。ユーザー入力がどこを通ってSQLクエリに到達するか、そのパスに適切なサニタイズがあるかを解析する。これが「汚染解析(Taint Analysis)」だ
SASTの仕組み
解析手法の種類
| 解析手法 | 概要 | 検出精度 | 速度 |
|---|
| パターンマッチング | 正規表現ベースで危険なパターンを検出 | 低〜中(誤検知多い) | 高速 |
| AST解析 | 抽象構文木を解析し構文レベルの問題を検出 | 中 | 高速 |
| データフロー解析 | 変数の値がどう伝播するかを追跡 | 中〜高 | 中速 |
| 汚染解析(Taint Analysis) | ユーザー入力(Source)から危険な操作(Sink)への到達パスを追跡 | 高 | 低速 |
| 制御フロー解析 | 実行パスの分岐を考慮した解析 | 高 | 低速 |
汚染解析の概念
汚染解析(Taint Analysis)の基本:
Source(汚染源): ユーザーからの入力
├── req.body
├── req.params
├── req.query
├── req.headers
└── 環境変数(場合による)
Sink(危険な操作): 汚染データが到達すると危険な箇所
├── SQL クエリ実行 → SQLインジェクション
├── HTML レンダリング → XSS
├── OS コマンド実行 → コマンドインジェクション
├── ファイルパス操作 → パストラバーサル
└── ログ出力 → ログインジェクション
Sanitizer(無害化): 汚染を除去する処理
├── パラメータ化クエリ
├── HTML エスケープ
├── 入力バリデーション
└── 型変換
判定ロジック:
Source → (Sanitizerなし) → Sink = 脆弱性あり
Source → Sanitizer → Sink = 安全
主要なSASTツール
ツール比較
| ツール | 対象言語 | ライセンス | 特徴 |
|---|
| Semgrep | 30以上の言語 | OSS(Community)/ 商用 | ルールの記述が容易、高速、カスタムルール |
| SonarQube | 30以上の言語 | OSS(Community)/ 商用 | 品質管理と統合、豊富なルール |
| CodeQL | JavaScript, Python, Java, Go等 | 無料(GitHub) | GitHub連携が強力、クエリ言語による高度な解析 |
| Snyk Code | 多言語 | Freemium | AI支援、IDE統合、修正提案 |
| Checkmarx | 多言語 | 商用 | エンタープライズ向け、高い検出精度 |
| Bandit | Python | OSS | Python特化、軽量 |
| ESLint Security | JavaScript/TypeScript | OSS | ESLintプラグイン、導入が容易 |
Semgrepのルール例
# Semgrep ルール: SQLインジェクション検出
rules:
- id: sql-injection-node
patterns:
- pattern: |
$DB.query(`...${$USER_INPUT}...`)
- pattern-not: |
$DB.query(`...${parseInt($USER_INPUT)}...`)
message: |
ユーザー入力がSQLクエリに直接埋め込まれています。
パラメータ化クエリを使用してください。
要件参照: SEC-INPUT-001
severity: ERROR
languages: [typescript, javascript]
metadata:
category: security
cwe: "CWE-89"
owasp: "A03:2021"
compliance:
pci-dss: "6.5.1"
soc2: "CC7.1"
# Semgrep ルール: ハードコードされたシークレット検出
rules:
- id: hardcoded-secret
patterns:
- pattern: |
$VAR = "..."
- metavariable-regex:
metavariable: $VAR
regex: ".*(password|secret|api_key|token|credential).*"
- metavariable-regex:
metavariable: "$VALUE"
regex: ".{8,}"
message: |
シークレットがハードコードされている可能性があります。
環境変数またはシークレット管理サービスを使用してください。
要件参照: SEC-DATA-004
severity: ERROR
languages: [typescript, javascript, python]
SASTの導入パターン
段階的導入戦略
| フェーズ | 範囲 | ルール | ブロック |
|---|
| Phase 1(1-2週間) | 新規PRのみ | Critical脆弱性のみ | 警告のみ(ブロックしない) |
| Phase 2(1ヶ月) | 新規PR + 差分 | Critical + High | Criticalはブロック |
| Phase 3(2-3ヶ月) | 全コードベース | Critical + High + Medium | Critical + Highはブロック |
| Phase 4(定常運用) | 全コードベース + カスタムルール | 全レベル | ポリシーに基づきブロック |
「最初からすべてをブロックすると開発が止まる。段階的に導入し、開発者がSASTに慣れる時間を作ることが成功の鍵だ」 — 田中VPoE
誤検知(False Positive)への対処
| 対処法 | 方法 | 適用場面 |
|---|
| インラインサプレス | コメントで特定行を除外 | 誤検知が明確な場合 |
| ルールチューニング | ルールのパターンを調整 | 誤検知が頻発する場合 |
| ベースライン設定 | 既存の検出結果を除外し、新規のみ検出 | レガシーコードベース |
| 除外パス設定 | テストコード等を除外 | テストファイルの誤検知 |
# .semgrepignore の例
# テストファイルは除外
tests/
__tests__/
*.test.ts
*.spec.ts
# 自動生成コードは除外
generated/
node_modules/
SASTのメトリクス
| メトリクス | 定義 | 目標値 |
|---|
| 検出率(Detection Rate) | 実際の脆弱性のうちSASTが検出した割合 | 80%以上 |
| 誤検知率(False Positive Rate) | 検出結果のうち実際には脆弱性でない割合 | 20%以下 |
| 修正時間(MTTR) | 検出から修正までの平均時間 | 48時間以内 |
| カバレッジ | SASTが対象とするコードの割合 | 95%以上 |
| ルール数 | 有効なSASTルールの数 | 言語ごとに100以上 |
まとめ
| ポイント | 内容 |
|---|
| SAST | ソースコードを実行せずに解析し、脆弱性を検出する手法 |
| 汚染解析 | Source→Sink間のデータフローを追跡し、サニタイズの欠如を検出 |
| ツール選定 | Semgrep(OSS)、CodeQL(GitHub)、SonarQube等を比較検討 |
| 段階的導入 | 警告のみ→Criticalブロック→全レベルブロックと段階的に導入 |
チェックリスト
次のステップへ
次は「DAST(動的解析)の基礎」を学びます。実行中のアプリケーションに対してセキュリティテストを行う手法を身につけましょう。
推定読了時間: 30分