セキュリティ

入力バリデーションとは?防げる攻撃の範囲と許可リスト設計を実装目線で解説

入力バリデーションは、外から届いた値を処理に渡す前に「許可した形と範囲に収まっているか」を判定し、外れたものを拒否する仕組みです。セキュリティの文脈では、OSコマンド実行やパストラバーサルの成立条件そのものを潰す一次防御として置かれます。ただし検証だけで塞げる脆弱性と、検証では届かない脆弱性があり、そこを取り違えた設計は脆弱性診断で必ず指摘を受ける弱点です。この記事では攻撃種別ごとの効き方の線引き、許可リスト方式の組み立て手順、デコードと正準化の処理順序、WAFや診断との役割分担、そして検証を厚くしない判断条件までを実装目線で整理します。バリデーション全般の定義や区分は別記事に譲り、ここではセキュリティ対策としての入力検証に絞った内容です。

まとめ:入力バリデーションで守れる範囲と許可リスト設計の要点

結論から示します。入力バリデーションは「危険な文字を消す仕組み」ではなく、「許可した値だけを通す関門」です。拒否リストで危険文字を数え上げる発想は、エンコード違いや未知の記法で抜けが出ます。型・長さ・文字集合・値域の四つで許可条件を先に固定し、外れた入力は加工せず拒否する。これが実装の基本形です。

効き方は攻撃種別で変わります。OSコマンド実行やパストラバーサルは、入力を許可リストで絞れば攻撃の成立条件を消せます。一方、SQLインジェクションとクロスサイトスクリプティングの本命はパラメータ化クエリと出力時のエスケープです。入力検証はここでは補助にとどまり、これを主対策に据えた設計は診断で指摘を受けます。認可不備や業務ロジックの悪用に至っては、入力検証の守備範囲の外側です。

処理の順序も設計対象になります。多重にURLエンコードされた値やUnicodeの互換文字は、デコードと正規化を済ませてから判定しないと検証を素通りします。そして検証を厚くする場所は選ぶ。信頼できない経路から来て、抜ければ実害が出る値だけを厳格に扱い、内部で生成された値まで同じ強度で縛らない。濃淡をつけた設計が、保守の軽さと安全の両立に効きます。

入力バリデーションの定義と、信頼境界の内側と外側で変わる検証の役割

同じ「バリデーション」という語でも、品質の話とセキュリティの話では狙いが違います。まず守備範囲をそろえます。

入力バリデーションと一般的なバリデーションの守備範囲の切り分け

一般的なバリデーションは、業務要件を満たすデータかどうかを判定する工程を広く指します。必須・桁数・日付の前後関係・在庫数との突き合わせまで含み、目的はデータ整合と利用者への案内です。この全体像と実装パターンはバリデーションの目的・種類と実装パターンの解説で扱っています。

一方、セキュリティ文脈の入力バリデーションが見ているのは「その値が後段の解釈器に渡ったとき、データではなく命令として読まれないか」です。MITREのCWEでは、この不備をCWE-20(Improper Input Validation)として独立した弱点に位置づけています。判定の目線は業務要件ではなく攻撃面の広さで、たとえば全角と半角が混ざった氏名は業務上は許容しても、SQLの区切り記号やパス区切りが混ざった識別子は許容しません。品質の検証が「利用者の入力ミスを拾う」のに対し、セキュリティの検証は「意図的に作り込まれた値を弾く」ことに寄せて条件を組みます。

信頼境界の引き方と、境界を越える値だけを検証対象にする判断基準

検証を置く場所は、信頼境界の線から決まります。信頼境界とは、値の作り手を管理下に置けなくなる地点です。ブラウザからのフォーム送信、公開APIのリクエストボディ、取引先から届くCSV、外部SaaSのWebhook、これらはすべて境界の外から来ます。逆に、同一プロセス内で自分が組み立てた定数や、DBから読み戻した自社管理の値は境界の内側です。

実務での判断基準は二つに絞れます。第一に「その値の生成元を自分たちが制御できるか」、第二に「その値が後段でSQL・シェル・ファイルパス・HTMLなど別の言語として解釈されるか」。両方に当てはまる境界越えの値は厳格に検証し、内側の値は型の担保で足ります。ブラウザ側の検証は境界の外にあるため、安全側の判定には数えません。開発者ツールやHTTPクライアントから直接叩けば迂回できるので、サーバ側の再検証を省いた時点で防御は成立していない状態です。

攻撃種別ごとの有効範囲:入力検証で防げる脆弱性と防げない脆弱性

「入力検証をすれば安全」でも「入力検証は無意味」でもありません。攻撃ごとに効き方の濃淡があります。

SQLインジェクションとXSSで入力検証が担う役割の切り分け

この二つは、入力検証が主役ではない代表例です。値がSQL文やHTMLに埋め込まれる時点で解釈が決まるため、対策は埋め込み方の側にあります。SQLはパラメータ化クエリ(プレースホルダ)で値と構文を分離し、HTMLは出力コンテキストに応じたエスケープを施す。ここが本命で、入力検証は攻撃面を狭める補助として重ねます。攻撃の成立過程とコード例はSQLインジェクションの仕組みと対策の解説が詳しいです。

攻撃種別 入力検証の効き方 本命の対策
SQLインジェクション 補助(値域を狭める) パラメータ化クエリ
クロスサイトスクリプティング 補助(形式を固定する) 出力時のエスケープ
OSコマンド実行 有効(許可リスト) シェル経由の実行を避ける
パストラバーサル 有効(正準化後に判定) 基準ディレクトリの照合
認可不備 効かない サーバ側の権限判定

コマンド実行・パス走査・SSRFで許可リストが効く条件と限界

入力検証が主役を張れるのは、受け取る値の形が業務上あらかじめ決まっている場面です。OSコマンド実行では、外部入力をそのままシェルに渡す設計をやめるのが第一手ですが、どうしても渡す必要があるなら、値を許可した固定候補の集合に写像します。ファイル名なら英数字とハイフンだけ、処理モードなら定義済みの三択だけ、といった形です。

パストラバーサル(CWE-22)は、正準化を先に済ませれば判定できます。相対パス記法やエンコード済みの区切り文字を解決したうえで、基準ディレクトリの配下に収まっているかを絶対パスで照合する。この順序を逆にすると、判定後の文字列操作で境界の外へ出る余地が生じるため、順序を守る設計が必要です。ただし、入力検証にも守備範囲の限界がある点は見落とせません。SSRFのように「URLとして正しい値」が攻撃に使われる場合、書式の検証だけでは足りず、接続先ホストを許可リストで固定し、リダイレクト追従まで含めて制御する必要が出ます。形式が自由になるほど、入力検証単独で守れる幅は狭まります。

入力検証では届かない認可不備・業務ロジック不正という守備範囲外

入力検証で塞げない領域を、設計者が正しく認識しているかどうかで事故率が変わります。代表が認可不備です。他人のIDを指定して他人のデータを読み出す操作は、送られてきた値そのものは書式も型も正当で、検証をすべて通過します。防ぐのはサーバ側の権限判定であり、入力検証をいくら厳しくしても届きません。

業務ロジックの悪用も同様です。割引の重複適用、数量にマイナスを渡しての金額操作、手順を飛ばしたAPI呼び出し。値域の検証で拾えるものもありますが、多くは「業務上ありえない組み合わせ」を状態側で判定する話になります。OWASPのTop 10 2025年版では、インジェクション系がA05に置かれる一方、アクセス制御の不備はA01として最上位に据えられ、旧版で独立していたSSRFもそこへ統合されました。項目ごとの変更点はOWASP Top 10 2025年版の10項目と変更点にまとめています。入力検証はA05側の対策であり、A01側は別の設計で守ると割り切ってください。

許可リスト方式の組み立て手順と、正準化・デコードを先に済ませる順序

方式の優劣は結論が出ています。ここからは、許可リストを実際にどう組むかの手順に踏み込みます。

型・長さ・文字集合・値域の四段で許可条件を固定する組み立て手順

許可リスト方式は「危険なものを列挙して弾く」拒否リストの逆で、通す条件を先に定義します。拒否リストが破られるのは、攻撃側の記法が実装側の想像を常に上回るからです。四段で条件を固定すると、抜けが減ります。

  1. 型:数値・日付・真偽値・列挙のどれかに落とし、文字列のまま扱う項目を減らす
  2. 長さ:業務上の上限を決める。上限のない自由入力欄を残さない
  3. 文字集合:許可する文字の範囲を定める。制御文字とヌルバイトは全項目で除外する
  4. 値域:数値の範囲、選択肢の集合、日付の期間を業務ルールから確定する

OWASP ASVS 4.0系では、この入力検証とサニタイズ、エンコーディングをV5章として一括で要件化しています。実装時は検証層を判定だけに留め、値を書き換えて通す処理を混ぜないでください。書き換えは後段の無害化の仕事で、検証層が値を加工し始めると「何が通ったのか」を追えなくなります。

多重エンコードとUnicode正規化で検証をすり抜けさせない処理順序

検証条件を正しく書いても、判定するタイミングを誤ると素通りします。攻撃側は、危険な文字をURLエンコードで二重に包んだり、全角や互換文字に置き換えたりして、検証時点では無害に見える形で送り込んできます。判定の直前に、デコードと正規化を必ず済ませてください。

順序は、デコード(必要なら変化がなくなるまで繰り返す)、Unicode正規化(NFKCなど採用形式を1つ決める)、正準化(パスやURLの解決)、そして検証、最後に業務処理という流れです。実装で見落としやすいのが、フレームワークがすでに一段デコードしている前提を確かめずに、自前でもう一段デコードしてしまう二重処理です。この場合、正規の入力に含まれるパーセント記号が壊れる副作用が出ます。どの層で何回デコードされるかを確定してから、検証層の位置を決めてください。

正規表現による書式検証の限界と、アンカー漏れ・ReDoSの回避策

書式の検証には正規表現を使いますが、二つの定番の落とし穴があります。一つ目はアンカー漏れです。部分一致の関数に先頭・末尾のアンカーを付け忘れると、「先頭だけ条件を満たし、後ろに任意の文字列を続けた値」が通過します。行単位でマッチする記号ではなく、文字列全体を対象とする \A\z を使い分けてください。改行を含む値を渡された場合の挙動は、言語ごとに差があります。

二つ目はReDoS(CWE-1333)です。入れ子の量指定子とバックトラックの組み合わせで、数十文字の入力に対して処理時間が指数的に伸び、1リクエストでCPUを占有される事故につながります。回避策は三つ。量指定子の入れ子を避けること、長さ検証を正規表現より前に置いて評価対象を短く保つこと、そしてメールアドレスのような自由度の高い書式で厳密な表現を目指さないことです。書式判定は正規表現、値の範囲や業務ルールはコードで、と役割を分けると保守もしやすくなります。

検証を置く層の判断と、WAF・脆弱性診断にどこまで任せるかの線引き

どの層に何を置くかで、抜けの出方が変わります。設計の判断材料を整理します。

APIとファイル取り込みで検証を置く位置と内部呼び出しの再検証

画面のフォームだけを検証対象と考えると、抜けが残ります。公開APIのリクエストボディ、バッチが取り込むCSVやXML、外部システムからのWebhook、メッセージキュー経由のイベント。これらはすべて境界の外から来る値で、同じ強度の検証が要ります。特にファイル取り込みは、行単位の型検証に加えて、文字コードの判定と件数・サイズの上限を先に置いてください。

内部サービス間の呼び出しをどう扱うかは判断が分かれます。ここでの線引きはこうです。呼び出し元が同じチームの管理下で、境界の外の値をすでに検証済みで渡していると保証できるなら、再検証は型の担保で足ります。逆に、複数チームが独立して開発するサービス群や、外部由来の値が加工されずに流れてくる経路では、受け手側でも検証する。「上流が検証しているはず」という暗黙の前提が、責任の空白地帯を作ります。

WAFを入力検証の代替にしない線引きと、併用時の役割の分け方

WAFは通信の内容をパターンで判定して遮断する仕組みで、アプリ改修なしに既知の攻撃パターンを止められる点が利点です。ただし、アプリの業務ルールも認可の文脈も知らないため、正規の書式に見える攻撃や、業務ロジックの悪用は素通りします。仕組みと他の防御機器との違いはWAFの仕組みとIPS・IDSとの違いの解説で整理しています。

併用時の役割はこう分けます。WAFは既知パターンの広域遮断と、脆弱性が見つかってから修正が出るまでの時間稼ぎ。アプリ側の入力検証は、業務ルールに基づく個別条件の判定。この順序を逆にして「WAFがあるから入力検証は軽くてよい」と判断した設計は、WAFのルール更新が追いつかない新しい記法で破られます。仮想パッチはあくまで一時的な措置で、恒久対策はコード側に置いてください。

検証の抜けを外部から確かめる診断ツールとレビューでの確認観点

入力検証の抜けは、書いた本人からは見えにくい性質があります。実装者は自分が想定した入力パターンでしか試さないためです。外から確かめる手段は三段あります。動的スキャナで境界値と異常系を機械的に流し込む、静的解析で外部入力が検証を経ずに危険な関数へ届く経路を追う、そして実装者以外の目でコードレビューを行う。

レビューで見る観点は絞れます。サーバ側で検証しているか、許可リスト方式になっているか、デコードと正規化が検証より前にあるか、そして検証を通らなかった値が例外処理の分岐で握り潰されていないか。この四点だけでも、診断で挙がる指摘の多くは事前に潰せます。既存システムで検証設計を客観的に確かめたい場合は、一創の脆弱性診断・セキュリティ診断で、入力検証の抜けを含めた検出と修正方針の提示までを引き受けています。

入力検証を厚くしない判断条件と、出力側の対策へ配分を寄せる場面

ここは立場を明確にします。全項目に一律で最大強度の検証を積む設計は、採用しません。

入力検証を唯一の防御に据えた設計が破綻する三つの典型パターン

破綻の形は、現場でおおむね三つに収束します。一つ目は、入力時点で無害化したから出力時のエスケープを省いた設計です。同じ値がHTML本文・属性値・JavaScript内・URLパラメータのどこに置かれるかで必要な処理は変わるため、入力時点で正解を1つに決められません。

二つ目は、検証を通った値をDBに保存し、その後は無検査で使い回す設計です。保存時の仕様と、数年後に追加された機能が要求する条件は一致しません。三つ目は、フロントエンドの検証だけで済ませ、サーバ側を素通しにした設計で、これは検証を書いていないのと同じ状態です。共通するのは、検証を「一度通れば以後は安全」という関門として扱っている点。実際には、値が別の言語として解釈される地点ごとに、その地点用の処理が要ります。

検証を軽くしてよい条件と、出力エスケープへ手間を回す判断基準

逆に、検証を軽くしてよい条件も明確に言えます。値の生成元が自システム内に閉じていて、かつ後段で別の言語として解釈されない項目です。内部バッチが組み立てる集計キー、列挙型として型システムが保証している区分値、表示専用で後続処理に渡らないラベル。ここに複雑な業務ルール検証を積んでも、増えるのは保守の手間だけです。

配分の判断基準は二問で足ります。「その値は信頼できない経路から来るか」「抜けたときデータ不整合かセキュリティ事故になるか」。両方が「はい」なら厳格に検証し、片方でも「いいえ」なら型の担保に留めて、浮いた手間をパラメータ化クエリの徹底と出力エスケープの網羅に回してください。とくにコードベース全体で文字列連結によるSQL組み立てが残っている状況なら、入力検証の条件を精緻にするより、そちらを先に潰すほうが事故の期待値を下げられます。優先順位を間違えないでください。

よくある質問

入力検証の実装でつまずきやすい点を、5つの質問に絞って整理します。

入力バリデーションとサニタイズは何が違いますか?

判定して拒否するのがバリデーション、加工して安全な形に変えるのがサニタイズです。入力バリデーションは条件に合わない値を処理に進めず突き返しますが、サニタイズは値を受け入れたうえで危険な要素を除去・変換します。役割が違うため、片方でもう片方を代替できません。実装上は、検証層で判定だけを行い、無害化は出力する場所ごとに施す形が扱いやすくなります。

許可リスト方式が現実的でない自由記述欄はどう検証しますか?

問い合わせ本文のような自由入力では、文字集合を絞る方式は成り立ちません。この場合は、長さの上限、制御文字とヌルバイトの除外、文字コードの妥当性という三点に検証を絞り、安全性の担保は出力側に寄せます。HTMLとして表示するならエスケープ、SQLに渡すならパラメータ化クエリ、という具合です。自由記述欄で入力検証を厳しくしすぎると、正当な入力を弾く誤検知が増えます。

入力バリデーションだけでSQLインジェクションは防げますか?

防げる前提で設計しないでください。入力検証は攻撃面を狭める補助にはなりますが、SQL文の組み立てが文字列連結のままなら、想定外の記法やエンコードで抜ける余地が残ります。本命の対策はパラメータ化クエリ(プレースホルダ)で、値と構文を分離することです。テーブル名やソート順のようにプレースホルダを使えない箇所だけ、許可リストによる照合で補います。

文字数制限はセキュリティ上どこまで効きますか?

単独の防御としては弱いものの、置く価値は十分にあります。長さ制限は、攻撃コードを組み立てる余地を物理的に狭め、正規表現の処理時間を抑えてReDoSのリスクを下げ、過大なリクエストによる資源の消費も抑えます。一方で、短い文字列でも成立する攻撃は多く存在するため、長さだけを根拠に安全と判断はできません。ほかの検証条件と重ねて使う前提の項目です。

既存システムの入力検証の抜けはどこから調べればよいですか?

外部入力の入口を洗い出すところから始めます。フォーム、公開API、ファイル取り込み、Webhookの一覧を作り、それぞれの値が最終的にどの解釈器(SQL・シェル・ファイルパス・HTML)へ届くかを追跡してください。そのうえで、サーバ側検証の有無と許可リスト方式かどうかを確認します。自力での棚卸しが難しい規模なら、動的スキャナによる検出と第三者のコードレビューを組み合わせる方法が現実的です。

関連記事

資料請求

RELATED POSTS 関連記事