DASTは「魔法の杖」ではない:インジェクションを現場で確実に仕留めるための戦術論
「DAST(動的アプリケーションセキュリティテスト)を回せば、脆弱性は全部見つかる」。そう信じているなら、今すぐ考えを改めてほしい。
現場で数々のインシデントを見てきた私から言わせれば、DASTは「優秀な偵察兵」ではあるが、決して「無敵の盾」ではない。今日は、インジェクション攻撃を例に、なぜDASTがすり抜けてしまう盲点があるのか、そして、それを補うために我々エンジニアが書くべき「本当にセキュアなコード」とは何か、現場の視点で語ろう。
—
1. DASTが突き当たる「攻撃の死角」
DASTは、URLやフォームフィールドをクローリングし、そこに' OR 1=1 --のような典型的なペイロードを放り投げてレスポンスの変化を監視する。だが、敵もさるもの。DASTが陥りやすい罠がある。
- 認証の壁: ログイン後の複雑な画面遷移や、多要素認証(MFA)が必要なページには、安易なクローラーは到達できない。
- 非同期通信(AJAX): モダンなSPA(Single Page Application)において、JSONでやり取りされるペイロードは、古いDASTツールでは適切に解釈・注入できないことが多い。
- ビジネスロジックの脆弱性: 「IDが一致するユーザーのデータを返す」というSQLクエリ自体は正しくても、権限チェックの欠如による「IDを書き換えて他人のデータを盗む」タイプの攻撃(Insecure Direct Object Reference: IDOR)は、DASTの単純なスキャンではまず検知できない。
だからこそ、「DASTの結果を待つ」のではなく「DASTが通るような堅牢な実装を最初から仕込む」という姿勢が、真のセキュリティ・プロフェッショナルの条件だ。
—
2. インジェクションを根絶する:セキュア実装の模範解答
インジェクション対策の鉄則は「入力値とコードの分離」だ。これを徹底すれば、DASTのペイロードがどれだけ巧妙でも、システムはびくともしない。
【Python】SQLインジェクションを確実に防ぐ(SQLAlchemy/Psycopg2)
文字列結合でSQLを作るのは、自宅の鍵を玄関先に置くようなものだ。必ず「プレースホルダー」を使え。
悪い例:絶対にやってはいけない(SQLインジェクションの温床)
cursor.execute(f”SELECT FROM users WHERE username = ‘{user_input}'”)
良い例:パラメータ化クエリを使用する
ライブラリが安全にエスケープ処理を自動で行う
def get_user_data(db_conn, user_id):
query = “SELECT username, email FROM users WHERE id = %s”
with db_conn.cursor() as cursor:
# SQL本体とデータを分離して送る
cursor.execute(query, (user_id,))
return cursor.fetchone()
【Node.js】OSコマンドインジェクションの回避
exec() は極力使うな。どうしても使う場合は、入力値のホワイトリスト検証を徹底し、引数を配列で渡せ。
const { execFile } = require(‘child_process’);
// 悪い例:exec(user_input) はコマンドが乗っ取られる
// 良い例:execFileで引数を分離する
function runImageOptimizer(filename) {
// ファイル名が英数字のみか厳密にチェック
if (!/^[a-zA-Z0-9]+\.jpg$/.test(filename)) {
throw new Error(“Invalid filename”);
}
// 引数は配列として渡すことで、シェル実行を回避する
execFile(‘/usr/bin/optimize’, [filename], (error, stdout) => {
if (error) console.error(error);
console.log(stdout);
});
}
—
3. インフラレイヤーでの「最後の砦」:WAFとNginx
アプリケーションのバグを完璧にゼロにするのは難しい。だからこそ、インフラ側で「疑わしい通信」を遮断する。Nginxのmod_securityやクラウドのWAFは、DASTツールが使ってくるような定型的な攻撃パターンを弾くのに有効だ。
Nginx設定例(SQLi検知のヒント):
簡易的なSQLキーワードのブロック例
※本来はWAF製品やModSecurityで運用すべき
location /api/ {
if ($query_string ~ “UNION.SELECT”) {
return 403;
}
if ($query_string ~ “OR.1=1”) {
return 403;
}
proxy_pass http://backend_cluster;
}
—
最後に:エンジニアへの提言
DASTは「テスト」であり、コードそのものの「防御」ではない。
インシデント対応の現場で最も悲惨なのは、「DASTで緑色の合格マークが出たからリリースした」という理由で、深刻な脆弱性を放置してしまうケースだ。
1. DASTはあくまで「お守り」: 脆弱性がないことを証明するのではなく、ケアレスミスを見つけるためのもの。
2. コードレビューが最強の防御: プレースホルダーを使っているか、権限チェックは正しいか。この「人の目」に勝るツールはない。
3. 攻撃者視点を持て: 自分が書いたコードに対して、「もし自分が攻撃者なら、ここをどう突くか?」と常に自問自答せよ。
脆弱性を修正するたびに、君たちのスキルは強固になる。面倒を避けず、泥臭くコードと向き合ってほしい。それが、ユーザーの信頼を守る唯一の道だ。
コメント