セキュリティ脆弱性は、修正するよりも予防する方がはるかに容易です。コードレビューで発見されたSQLインジェクションの脆弱性は、修正に数分しかかかりません。しかし、侵害後に同じ脆弱性が発見された場合、インシデント対応、規制当局による調査、修復に数週間かかるだけでなく、その間に流出したデータも回収しなければなりません。静的コード分析は、ソースコードの構造、データフロー、パターンを既知の脆弱性シグネチャと照合することで、コード実行前にこれらの問題を発見する手法です。これを体系的に適用することで、セキュリティは事後対応的な手法から、開発プロセスに組み込まれた特性へと変わります。
OWASP Top 10は、Webアプリケーションセキュリティにおける最も重大なリスクを網羅した権威あるカタログであり、Open Web Application Security Project(OWASP)が数千ものアプリケーションにわたる実際の脆弱性データに基づいて更新しています。リストに掲載されているすべての項目は、静的解析ツールによって程度の差こそあれ検出可能です。このガイドでは、OWASPの各カテゴリと静的解析で検出できる内容を対応付け、脆弱なコードパターンとその安全な代替案を示し、各手法を適用できるツールについて説明します。
静的アプリケーションセキュリティテスト(SAST)とは何ですか?
静的アプリケーションセキュリティテスト(SAST)は、プログラムを実行せずにソースコード、バイトコード、またはバイナリを分析します。この分析では、アプリケーション内でのデータの流れ、データが到達するセキュリティ上重要な操作、および外部入力(ユーザー入力、HTTPパラメータ、ファイルの内容、環境変数)からのパスが、適切な検証やサニタイズなしに危険な操作(データベースクエリ、システムコマンド、HTML出力、暗号化機能)につながるかどうかを検証します。
SASTは、包括的なアプリケーションセキュリティプログラムにおける一つのレイヤーです。その位置づけを理解するには、他の選択肢と比較する必要があります。
| アプローチ | 実行すると | 発見されたこと | 何が欠けているか |
|---|---|---|---|
| SAST(静的) | 実行前に、ソースコード上で | コードレベルの脆弱性、インジェクションパターン、暗号化の悪用、ハードコードされた秘密情報 | 実行時のみの脆弱性、デプロイメントにおける構成上の問題 |
| DAST(ダイナミック) | 実行中のアプリケーションに対して | 実行時動作、認証の欠陥、サーバー構成の問題 | テスト中にトリガーされないコードレベルのパターン |
| SCA(ソフトウェア構成分析) | 依存関係マニフェストについて | サードパーティライブラリにおける既知のCVE | カスタムコードの脆弱性 |
| IAST(インタラクティブ) | 計測機器を用いたテスト実行中 | 高精度なランタイムデータフロー | アプリケーションの実行が必要で、フィードバックが遅くなります。 |
SASTは、脆弱性がテスト環境に到達する前に、まだデプロイされていないコード、CI/CDパイプライン、あるいはIDE上で実行されるため、最も早期にフィードバックを提供します。この早期性こそが、SASTの最大のセキュリティ上の価値です。
静的コード分析はどのような種類の脅威を軽減できるのか?
これはSASTに関する最も検索されている質問の1つです。直接的な回答は次のとおりです。
静的コード分析は、ソースコードパターンに現れる脅威(SQL、コマンド、XSSの脆弱性、暗号化の悪用、ハードコードされた認証情報、安全でない認証実装、コードロジックにおけるアクセス制御の不備、データ整合性の障害など)を軽減できます。しかし、実行時構成、ネットワークトポロジー、インフラストラクチャ設定に起因する脅威は軽減できません。これらの脅威には、DAST、侵入テスト、またはインフラストラクチャセキュリティスキャンが必要です。
OWASPカバレッジにおける静的解析と動的解析の比較
動的解析(DAST)と静的解析(SAST)は、OWASPの脆弱性の異なるサブセットを検出します。どちらもすべての脆弱性を網羅しているわけではありません。OWASP Web Security Testing Guide(WSTG)は動的テストの方法論フレームワークであり、CodeQL、Semgrep、SonarQubeなどのSASTツールはソースコード層を対象としています。
| OWASPトップ10カテゴリー | SASTの報道 | DAST報道 |
|---|---|---|
| 壊れたアクセス制御 | 部分的、コードロジックのギャップ | 良い、ランタイム動作テスト |
| 暗号化の失敗 | 強力なアルゴリズム検出 | 弱く、外からは観察しにくい |
| 注射 | 強力な汚染分析 | 強力でアクティブなペイロードテスト |
| 安全でない設計 | 部分的、パターン検出 | 弱点があり、設計知識が必要 |
| セキュリティの構成ミス | 部分的、コード内の設定 | 実環境下での厳しいテスト |
| 脆弱なコンポーネント | 弱い、SCAの方が良い | 弱い、SCAの方が良い |
| 認証失敗 | 部分的なハードコードされた認証情報、脆弱なパターン | 強力なセッションおよび認証テスト |
| データ整合性障害 | 部分的な逆シリアル化パターン | 弱く、外部からは検出しにくい |
| ログ記録の失敗 | 部分的または欠落したログステートメント | 弱く、観察しにくい欠如 |
| SSRF | HTTP呼び出しに強い汚染を与える | 強力でアクティブなリクエストテスト |
結論:SASTとDASTは相互補完的な関係にあります。OWASPのカバレッジを最大限に高めるには、両方を実施してください。予算に制約のある新規チームの場合は、SASTを最初に実施することをお勧めします。SASTは開発者へのフィードバックが最も早く、インジェクションのカバレッジも最も広範囲に及ぶためです。
OWASPトップ10:静的解析で何が見つかるか、そしてどのように
A01、アクセス制御の不具合
OWASPが指摘する最大のリスクは、アクセス制御の不備です。静的解析では、コードレベルでの脆弱性、すなわち、関数やエンドポイントにおける認証チェックの欠落、検証なしに内部IDを公開するオブジェクト参照、意図された権限構造を回避するハードコードされたロール割り当てなどを特定します。
静的解析で検出されるもの:対応する認証チェックなしに機密性の高い操作を処理するメソッド。アクセス検証なしにユーザー入力からオブジェクトIDを取得する直接オブジェクト参照。認証状態はチェックされるがオブジェクトの所有権はチェックされない強制ブラウジングパターン。
ジャワ
// Vulnerable: no ownership check -- any authenticated user can access any order
@GetMapping("/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId) {
return orderRepository.findById(orderId).orElseThrow();
}
// Secure: verify the order belongs to the requesting user
@GetMapping("/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId,
@AuthenticationPrincipal UserDetails user) {
Order order = orderRepository.findById(orderId).orElseThrow();
if (!order.getOwnerId().equals(user.getUserId())) {
throw new AccessDeniedException("Order does not belong to requesting user");
}
return order;
}
静的解析では検出できないもの:実行時アクセス制御の失敗。ロジック自体は正しいものの、判断に使用されるデータが侵害されている場合など。このような場合は、DAST(データアクセス制御テスト)と侵入テストが必要です。
A02、暗号化の失敗
脆弱な暗号化方式は、脆弱なパターン(MD5、SHA-1、DES、ECBモード、ハードコードされた鍵など)がソースコード内で字句的に識別できるため、静的解析によって確実に検出できます。
パイソン
# Vulnerable: MD5 for password hashing (broken algorithm)
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()
# Vulnerable: hardcoded encryption key
KEY = b"mysecretkey12345"
cipher = AES.new(KEY, AES.MODE_ECB) # ECB mode also vulnerable
# Secure: bcrypt for passwords, environment-sourced keys
import bcrypt, os
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))
# Secure: AES-GCM with environment-sourced key
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = os.environ["ENCRYPTION_KEY"].encode()
aesgcm = AESGCM(key)
静的解析ルールフラグ: セキュリティに敏感な目的のためのMD5/SHA-1、DES/3DES/RC4/ECBモード、ハードコードされた暗号鍵と秘密、機密データ送信のためのHTTPSではなくHTTP、および証明書検証の無効化(verify=False Pythonのrequestsでは、 setHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER) Javaで)。
A03、注射
インジェクション、SQL、OSコマンド、LDAP、XSS、テンプレートインジェクションは、プロシージャ間汚染分析が最も直接的な価値を発揮するカテゴリです。この脆弱性では、信頼できない入力が、関数呼び出しを通じて危険なシンクに到達するまで追跡する必要があります。
ジャバスクリプト
// Vulnerable: direct string interpolation in SQL (SQL injection)
app.get('/users', async (req, res) => {
const name = req.query.name;
const result = await db.query(`SELECT * FROM users WHERE name = '${name}'`);
res.json(result.rows);
});
// Secure: parameterized query
app.get('/users', async (req, res) => {
const name = req.query.name;
const result = await db.query('SELECT * FROM users WHERE name = $1', [name]);
res.json(result.rows);
});
PHP
// Vulnerable: unescaped output (XSS)
echo "Welcome, " . $_GET['username'];
// Secure: context-appropriate escaping
echo "Welcome, " . htmlspecialchars($_GET['username'], ENT_QUOTES, 'UTF-8');
Cシャープ
// Vulnerable: command injection in C#
var process = new Process();
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/c " + userInput;
process.Start();
// Secure: avoid shell interpretation, validate and whitelist inputs
var allowedCommands = new HashSet<string> { "report", "export" };
if (!allowedCommands.Contains(userInput))
throw new ArgumentException("Invalid command");
インジェクションに対するプロシージャ間汚染分析を実行するツール:CodeQL(最も正確)、汚染モード付きSemgrep、Snyk Code、セキュリティルール付きSonarQube。
A04、安全性の低い設計
設計上の脆弱性は、コードパターンではなくアーキテクチャ上の決定に関わるため、静的解析において最も難しいOWASPカテゴリです。静的解析では、以下のような兆候を検出できます。
認証エンドポイントにおけるレート制限ロジックの欠如、認証失敗後のアカウントロックアウトの欠如、検証手順をスキップするビジネスロジック、監査ログなしで特権操作を実行する関数。これらは欠落パターンとして検出可能であり、静的解析では存在するものよりも存在しないものを報告します。
SASTツールの中には、組織のセキュリティ設計要件をエンコードできるカスタムルールをサポートするものがあります。例えば、すべてのコントローラメソッドは認証関数を呼び出す必要があり、すべてのデータベース書き込みは入力検証を先行させる必要があり、すべての外部API呼び出しはタイムアウトを使用する必要がある、といったルールです。これらのカスタムルールは、設計要件を強制力のあるコード制約に変換します。
A05、セキュリティ設定ミス
コードにおけるセキュリティ設定の誤りには、セキュリティ機能の無効化、CORSヘッダーの許容範囲の広さ、セキュリティ応答ヘッダーの欠落、本番環境でのデバッグモードの有効化、スタックトレースを露呈する冗長なエラーメッセージなどが含まれます。
パイソン
# Vulnerable: Flask debug mode enables interactive debugger in production
app = Flask(__name__)
app.run(debug=True) # exposes console access if error occurs
# Vulnerable: overly permissive CORS
from flask_cors import CORS
CORS(app, origins="*") # allows any origin
# Secure: environment-controlled debug, restricted CORS
import os
debug_mode = os.environ.get("FLASK_DEBUG", "false").lower() == "true"
CORS(app, origins=os.environ.get("ALLOWED_ORIGINS", "").split(","))
app.run(debug=debug_mode)
静的解析ルールフラグ: デバッグモードが設定されています True ソースコードにおけるワイルドカードCORSオリジン、HTTPレスポンス構成におけるセキュリティヘッダーの欠落、SSL証明書検証の無効化、および構成ファイルにおけるデフォルトの認証情報。
A06、脆弱で旧式のコンポーネント
このカテゴリは、従来のSASTではなく、主にソフトウェア構成分析(SCA)によって対処されます。SCAスキャンは package.json, pom.xml, requirements.txt、および同様のマニフェストを脆弱性データベース(National Vulnerability Database、GitHub Advisory Database)に対して提出する。
SASTは、非推奨のAPIの使用、セキュリティ上の欠陥により廃止されたライブラリ関数の呼び出し、あるいは最新のライブラリでさえ推奨しなくなった脆弱なパターンの直接使用を特定することで貢献します。
A06専用のツール: npm audit, pip-audit, snyk test, OWASP Dependency-CheckGitHub Dependabot、および Mend(旧 WhiteSource)。
A07、識別および認証の失敗
静的解析により、コードレベルの認証アンチパターンが見つかりました。ハードコードされたパスワード、脆弱なパスワード検証、非暗号的なランダム性で生成されたセッショントークン、ログアウト時のセッション無効化の欠如、およびJWT実装が受け入れる none アルゴリズム。
ジャバスクリプト
// Vulnerable: JWT accepting 'none' algorithm -- allows signature bypass
const decoded = jwt.verify(token, secret, { algorithms: ['HS256', 'none'] });
// Vulnerable: hardcoded admin credentials
if (username === 'admin' && password === 'admin123') {
grantAccess();
}
// Secure: algorithm whitelist, no hardcoded credentials
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'] // explicit allowlist only
});
Cシャープ
// Vulnerable: weak random for session token generation in C#
var sessionToken = new Random().Next().ToString();
// Secure: cryptographically secure random
using var rng = RandomNumberGenerator.Create();
var bytes = new byte[32];
rng.GetBytes(bytes);
var sessionToken = Convert.ToBase64String(bytes);
A08、ソフトウェアおよびデータ整合性障害
このカテゴリは、安全でない逆シリアル化と未検証のソフトウェア更新を対象としています。静的解析で検出されるもの:Java ObjectInputStream 信頼できないソースからのデータの逆シリアル化、Python pickle.loads() 外部データ、PHP unserialize() ユーザー制御入力、および安全でないローダーを使用するYAMLパーサー。
パイソン
# Vulnerable: pickle deserialization of untrusted data
import pickle
data = pickle.loads(request.data) # arbitrary code execution risk
# Vulnerable: unsafe YAML loader
import yaml
config = yaml.load(user_input) # yaml.load without Loader is unsafe
# Secure: safe alternatives
import json
data = json.loads(request.data) # JSON cannot execute code
import yaml
config = yaml.safe_load(user_input) # safe_load disables arbitrary object creation
A09、セキュリティログ記録および監視の障害
静的解析では、ログ記録の失敗は、ログ出力が伴わないパターンとして検出できます。例えば、機密性の高い操作、認証イベント、アクセス制御の決定、データ変更などが挙げられます。静的解析ルールでは、特定の関数呼び出しが常に監査ログ呼び出しと同時に発生することを要求できます。
静的解析で検出される脆弱性としては、パスワードやトークンのログ記録(機密データのログ記録も脆弱性となる)、ログを記録せずにエラーを黙って無視する例外ハンドラ、および生の例外メッセージをログに記録するcatchブロック(機密データが含まれている可能性がある)などが挙げられる。
ジャワ
// Vulnerable: swallowed exception, no logging
try {
authenticateUser(username, password);
} catch (Exception e) {
// silent failure -- no log, no audit trail
}
// Vulnerable: logging sensitive data
log.info("User logged in with password: " + password);
// Secure: log the event, not the credential
try {
authenticateUser(username, password);
auditLog.info("Authentication success for user: {}", username);
} catch (AuthenticationException e) {
auditLog.warn("Authentication failure for user: {}", username);
throw e; // do not swallow
}
A10、サーバーサイドリクエストフォージェリ(SSRF)
SSRFは、プロシージャ間汚染分析の有効性を強く示す事例です。この脆弱性を悪用するには、ユーザーが制御する入力からHTTPリクエストに至るまでの経路を追跡する必要があり、多くの場合、複数の関数呼び出しを経由します。URLの構築とHTTP呼び出しが異なる関数で行われている場合、個々の関数のみを分析するツールではSSRFを検出できません。
パイソン
# Vulnerable: user-controlled URL in HTTP request (SSRF)
import requests
def fetch_resource(url):
return requests.get(url).content # no validation
def api_endpoint(request):
target = request.json().get("url") # attacker controls this
return fetch_resource(target) # SSRF across function boundary
# Secure: allowlist validation before making the request
from urllib.parse import urlparse
ALLOWED_HOSTS = {"api.internal.example.com", "cdn.example.com"}
def fetch_resource(url: str) -> bytes:
parsed = urlparse(url)
if parsed.hostname not in ALLOWED_HOSTS:
raise ValueError(f"URL host not allowed: {parsed.hostname}")
return requests.get(url, timeout=5).content
OWASPセキュリティのための静的コード分析ツール
以下の表は、主要なSASTツールと、それらが最も効果的に対応できるOWASPのカテゴリを対応付けたものです。
| ツール | 主要言語 | OWASPの強み | アプローチ |
|---|---|---|---|
| コードQL | Java、JS/TS、Python、C/C++、Go、Ruby | A03 注射、A10 SSRF(深部汚染) | 手続き間意味解析 |
| セムグレップ | 30以上の言語 | A03、A02、A07(パターンベース+汚染モード) | パターンマッチング+浅い会陰 |
| スニックコード | Java、JS/TS、Python、C# | A03、A07、A08 | 機械学習に基づく汚染分析 |
| ソナーキューブ | 30以上の言語 | A02、A03、A05、A07、A09 | ルールベース+データフロー |
| チェックマーク | 30以上の言語 | OWASPの全項目を網羅 | 処置間汚染 |
| ベラコード | Java、.NET、JS、PHP | OWASPの全項目を網羅 | バイトコード+汚染分析 |
| OWASP ザップ | 言語に依存しない | A01、A05、A07(実行時) | DAST、動的テスト |
| SMART TS XL | COBOL、JCL、Java、Python、RPG、SQL | 言語間の汚染、依存リスク | 言語横断的な構造的影響 + 汚染 |
認定条件 SMART TS XL エンタープライズコードベースにおけるセキュリティ対策
企業セキュリティプログラムは、単一言語のSASTツールでは解決できない課題に直面しています。それは、攻撃対象領域が複数の言語にまたがっていることです。Webアプリケーションは、JavaScriptでユーザー入力を受け取り、Javaで処理し、メッセージキューを通してCOBOLプログラムに渡して、DB2データベースに対してSQLを実行する場合があります。インジェクションの脆弱性は、4つの言語の境界を越えて存在します。個々の言語スキャナーでは、完全な汚染経路を把握することはできません。
SMART TS XLさん 静的コード分析 このチェーン内のすべての言語を同時にカバーします。信頼できない入力が JavaScript API ハンドラから Java サービスを経由して COBOL プログラムに流れ込み、動的 SQL クエリを構築する場合、 SMART TS XL これは、言語をまたがる呼び出しグラフ全体にわたってその経路をトレースし、CodeQLがJava内で適用するのと同じ手続き間汚染追跡を、Java、COBOL、SQL全体にまとめて適用します。
アプリケーション依存関係マッピング機能は、システム内のどのコンポーネントが外部入力とやり取りしているか、どのコンポーネントが特権操作にアクセスしているか、そしてシステムの攻撃対象領域全体が実際にはどのような状態になっているかといった、セキュリティに関連する情報を提供します。このアーキテクチャ上のセキュリティビューは脅威モデリングの基盤となります。構造を理解していないシステムに対して脅威をモデリングすることはできません。
影響分析機能は、セキュリティ対策プログラムに役立ちます。コンポーネントに脆弱性が発見された場合、影響分析によって、その脆弱性のあるコンポーネントに依存する他のすべてのコンポーネントが特定され、コード変更前に対策作業の範囲が正確に把握されます。セキュリティ上の欠陥を持つCOBOLプログラムが数百もの他のプログラムに組み込まれているような大規模なレガシーコードベースでは、対策を開始する前に完全な対策範囲を把握できるかどうかが、管理された修正で済むか、連鎖的なインシデントに発展するかの分かれ目となります。
管理する組織向け レガシーの近代化 プログラムにおいては、レガシーコードベースのセキュリティ分析は必須事項であり、後付けの検討事項ではありません。脆弱なCOBOLプログラムをJavaに移行すると、脆弱なJavaプログラムが生成されます。 SMART TS XLセキュリティ分析により、脆弱性は移行後のシステムで発見されるのではなく、近代化プロセスの一環として特定され、修復されることが保証されます。
SASTを開発ライフサイクルに統合する
静的セキュリティ分析は、コードが記述されレビューされる意思決定の段階で実行される場合に最大の効果を発揮し、デプロイ後に実行される場合には効果を発揮しません。
IDEにおいては、 SonarLint、SnykのIDE拡張機能、CodeQLのVS Code拡張機能などが、開発者がコードを記述する際にインラインで検出結果を表示します。開発者が脆弱なパターンを入力した瞬間に表示されるSQLインジェクションのフラグは、修正に数秒しかかかりません。
プルリクエストの場合: GitHub Actions、GitLab CI、またはJenkinsに統合されたSASTは、すべてのプルリクエストで実行され、検出された内容をコードレビューのコメントとしてインラインで表示します。開発者は、検出された内容を、その原因となったコードと並べて確認できます。
品質ゲートとして: SonarQubeの品質ゲートモデルは、新たな重大なセキュリティ上の問題点が発見された場合、マージをブロックします。これにより、セキュリティはオプションのレビュー手順ではなく、マージプロセスにおける構造的な要件となります。
定期的に実行される:詳細な手続き間分析、CodeQL、Checkmarx、完全なSemgrepルールセットは、通常、コミットごとに実行するには遅すぎるが、メインブランチで毎晩または毎週実行され、完全な呼び出しグラフ分析が必要な脆弱性を検出します。
階層的なアプローチ、IDEおよびコミットにおける高速なパターンベースのルール、プルリクエストおよびナイトリービルドにおける詳細な汚染分析により、開発者が必要とする即時性と、セキュリティプログラムが要求する徹底性の両方が実現されます。