ほぼ全てのインジェクション攻撃を無効化/防止する入力バリデーション


入力バリデーションはセキュリティ対策として最も重要なセキュリティ対策です。なぜセキュリティ対策であるのか?を理解していない方も見かけますが「ほぼ全てのインジェクション攻撃を無効化/防止する入力バリデーション」の効果と拡張方法を見れば解るのではないでしょうか?

ソフトウェア開発者が知っておくべきセキュリティの定義/標準/ガイドで紹介しているセキュリティガイドラインでは入力バリデーションが最も重要なセキュリティ対策であるとしています。

厳格な入力バリデーションを行うと、開発者が意識しなくても、非常に多くの脆弱性を利用した攻撃を防止できます。今回は比較的緩い入力バリデーション関数でも、ほとんどのインジェクション攻撃を防止できることを紹介します。

単純かつ緩い制限であっても効果的な入力バリデーション関数

入力バリデーションは厳格に行うべきです。しかし、開発中に厳しい入力バリデーションを行うとアプリケーション仕様の変更に伴い、入力バリデーション仕様も変更しなければなりません。これでは開発速度が遅くなります。

今回は開発中に仕様変更があっても、あまり開発に影響せず、かつほぼ全てのインジェクション攻撃を無効化するバリデーション関数を作ります。

方針

  • 言語にはPHP 5.6以降を使う
  • 入力は全て文字列として扱う(Webアプリの入力は基本的に全てテキスト
  • 英数字を受け入れる
  • UTF-8エンコーディングを受け入れる
  • 文字列の長さは開発(開発中の動作確認)に影響しない程度の長さを許可する(60バイトまで)
  • バリデーション違反はエラーを発生させ、確実にプログラム実行を停止する

バリデーション関数

上記の方針に従ったバリデーション関数(validate_default関数)は以下の通りです。

 

バリデーション関数の動作確認

ソースコードの最後にテスト用のコードも記載されているので、簡単に動作確認できます。上記コードをvalidation.phpに保存した場合、validation.phpを保存しているディレクトリから

と実行するとPHPのビルトインWebサーバーが起動します。Webブラウザから

とアクセスすると、最後の行が実行され以下の出力が行われます。

これ以降はアクセスしたURLを出力を一緒に記述します。

拒否する入力例

ヌル文字

改行

他の制御文字

“<“(他の記号全て同じ)

無効な文字エンコーディング

空白文字

 

許可する入力例

“abcde”

“日本語あいうえお”

 

なぜこのバリデーション関数でほぼ全てのインジェクション攻撃が防げるのか?

可変長インターフェースを持つシステムに対するインジェクション攻撃の本質は

  • プログラムが出力先の入力仕様を理解せず、誤作動する入力を受け付けて出力してしまう

ことにあります。誤作動してしまう原因は以下のような物があります。

SQL

  • 文字列/識別子を区切る ‘ や “、LIKE文のメタ文字の %や_

HTML/JavaScript

  • タグの開始/終了文字の < , > 、属性の開始/終了文字の ” , ‘ 、エンティティ開始の&など

XPath

XQuery

OSコマンド

  • OSコマンドのエスケープ に代表的な危険文字を書いています。ただし、これだけではありません。OSコマンドを確実に安全にエスケープする良い方法はありません。

メールヘッダー

  • 改行インジェクション攻撃が可能。不正にメールを送信する攻撃に利用される。

書いていくときりがないので、これくらいにします。このバリデーション関数ではこれらの出力先が特殊な意味を持つ文字をほぼ全てを無効な文字として拒否しています。このため、インジェクション攻撃はできません。文字エンコーディングもバリデーションしているので壊れた文字エンコーディングを使った攻撃もできません。

 

バリデーション関数の評価

ここに記載していない文字でかなり危険な文字に” “(半角のスペース)があります。半角スペースもかなり危険です。例えば、SQL文の識別子をエスケープ無しで出力している場合、

$_GET[‘field_name’]に “1; DROP TABLE mytable; –” が与えられた場合、

というクエリになり、不正なSQL文を実行できてしまいます。このような攻撃を行うには” “(半角スペース)が欠かせません。

この例のバリデーション関数では” “(半角スペース)はもちろん、; や – も受け入れないのでバリデーション関数で防御できます。SQLリテラルを文字列で作る場合、あまり危険と考えられていない ; や – を許可しているとリスクが増える、と認識してしている方はあまり多くないのではないでしょうか?

SQL以外でも意味を持つことが多い記号文字を全て拒否しているので攻撃できません。例えば、HTML5で認められているクオート無しので属性値でエスケープを忘れていても、攻撃できません。

のようなコードであっても、空白で区切ることができないので属性値無しの属性でさえインジェクションできません。

またデフォルトだと最大長が60バイトに制限されているので、プログラムが利用しているライブラリなどにバッファーオーバーフロー脆弱性があったとしても、攻撃できないか攻撃できても困難なレベルにまで小くなっています。

参考:今時のShellcodeとセキュア/防御的プログラミング

つまり、この簡単かつ単純なバリデーション関数を使っているだけでも「ほとんど全てのインジェクション脆弱性を防止」できます

 

バリデーション関数の拡張

制御文字はもちろん、記号文字全てを禁止していますが開発中のダミーデータを入れる程度なら英数字/UTF-8文字を許可しているのであまり困る事はないと思います。しかし、改行も” “(半角スペース)も使えないようでは困る場合も多いと思います。

こういう場合はホワイトリスト型で許可すれば良いです。例えば、”\n”と” “を許可したい場合、

と”\n” (\x0A)と” “(\x20)を許可する文字として追加します。サンプルのテスト例を以下に書きます。

 

このバリデーション関数の残存リスク

このバリデーション関数の残存リスクはエクササイズとして残しておきます。このバリデーション関数を使っていても攻撃できるケースを思いついた方はぜひコメントをお願いします。

※ ヒント:文字エンコーディング、外部システム、詐称、余計な物

一緒に”\n”と” “を追加したことによる残存リスクの増加も考えると良い演習になると思います。

ペネトレーションテスト(侵入テスト)をしている方なら、リスクが高い文字、一文字一文字を追加することによって増えるリスクを的確に答えることができると思います。しかし、一般の開発者はそこまで知る必要はありません。入力バリデーションでリスクを低減し、出力時に完全に安全な形で出力すれば良い※だけです。

※ これが結構難しかったりします。ここの本題ではないないので解説しませんが、難しい場合もあるので入力バリデーションで十分リスクを低減し、出力時に確実に安全になるよう心掛けることが重要です。

セキュリティ対策を正しく評価することは重要です。詳しくは実は標準の方が簡単で明解 – セキュリティ対策の評価方法をご覧ください。

 

入力バリデーションは第一のセキュリティ対策

ソフトウェア開発者が知っておくべきセキュリティの定義/標準/ガイドが最も重要なセキュリティ対策として紹介している理由は紹介したバリデーション関数の有効性で明らかだと思います。入力バリデーションはセキュリティ対策として最も大切です。

とは言っても多くの入力はここで紹介したようなバリデーション関数では使い物にならない、と思うかも知れません。使い物にならない入力もあります。しかし、多くの入力はここで紹介したバリデーション関数よりももっと厳しい条件でバリデーションできます。

  • データベースのレコードID: 数字のみ15桁まで(符号付き64整数の最大値より大きい数値。オーバーフローに注意)
  • 月:数字のみ2桁まで
  • 都道府県:予め決まった都道府県名のみ(1都1道2府43県)
  • 価格:数字のみ15桁まで(整数オーバーフローのリスクを考えると20億未満の方が良い)
  • ハッシュ値:数字+ABCDEF、必要な桁数(SHA256なら64文字)
  • 電話番号/郵便番号/さまざまなコード類/etc

プログラマなら予め仕様を熟知しているハズの入力値を厳格にバリデーションするだけで、多岐にわたるインジェクション攻撃のリスクをゼロにできます。万が一、うっかりがあった場合、ライブラリなどにバグがあった場合でも勝手に防御できてしまうボーナス付きのセキュリティ対策が入力バリデーションです。

もちろん、全ての入力をここで紹介したような入力バリデーション方法でバリデーションできません。もっと緩い条件のバリデーションが必要なケースも多くあります。サンプルのバリデーション関数より緩いバリデーション条件のバリデーションはリスクが在ることを認識しつつ、緩いバリデーションを使いましょう。

 

入力バリデーションと出力制御の関係

CERTトップ10セキュアコーディングプラクティスで分かり易く解説されています。

7. 他のシステムに送信するデータを無害化する

コマンドシェル、リレーショナルデータベースや商用製品コンポーネントなどの複雑なシステムへの渡すデータは全て無害化する。攻撃者はこれらのコンポーネントに対してSQL、コマンドやその他のインジェクション攻撃を用い、本来利用してない機能を実行できることがある。これらは入力バリデーションの問題であるとは限らない。これは複雑なシステム機能の呼び出しがどのコンテクストで呼び出されたか入力バリデーションでは判別できないからである。これらの複雑なシステムを呼び出す側は出力コンテクストを判別できるので、データの無害化はサブシステムを呼び出す前の処理が責任を持つ。

入力のセキュリティ対策である入力バリデーションと出力のセキュリティ対策であるエスケープ(エンコーディング)/セキュアなAPIの利用/バリデーションは独立したセキュリティ対策です。

オブジェクト指向設計の原則であるSOLIDのSRP(単一責任原則)をセキュアコーディングにもあてはめようとする方を時々見かけますが、これは誤りです。プログラミング原則の1つは防御的プログラミングであり、防御的プログラミングでは境界防御(入力バリデーション)はもちろん縦深防御(多重のセキュリティ、出力対策としてのセキュリティ確保)を確実に行います。

参考:エンジニア必須の概念 – 契約による設計と信頼境界線

そもそも危険と思われる入力にはプログラマは注意を払っていると思います。もし今まで注意を払っていなかったとしても、確実に安全な方法でバリデーションできていない入力がある、出力時のセキュリティ対策は独立したセキュリティ対策として完全に安全化する必要がある、と理解するだけでも安全なプログラムを書くために大きな助けとなります。

 

まとめ

ここで紹介した内容を理解すれば、なぜセキュアプログラミングの第一の対策が入力バリデーションなのか理解できると思います。誤解が解けない方はコメント頂ければ幸いです。

入力対策の次に重要な対策は出力対策です。プログラムから何らか出力を行う場合、確実に全ての出力が安全に行わなければなりません。入力と出力のセキュリティ対策は独立したセキュリティ対策であることを忘れないようお願いします。

時々、不可解な入力仕様のアプリケーションを見ることがあると思います。例えば、パスワードにほとんど記号が使えない、などのアプリケーションを見た事がある方も居るのではないでしょうか?あまり原理主義的に厳しいバリデーションにしてしまうと、おかしなアプリケーションになってしまうので、適切に適用するようお願いします。

最後に、くれぐれもこの緩い入力バリデーション関数で運用に入らないようお願いします。入力仕様に合わせた適切/厳格な入力バリデーションを行うようにしてください。これは”インターフェース”が安定してきた開発の後の方で十分です。コードと異り、インターフェースは安定しています。オブジェクト指向でインターフェースを使うのもこのためです。セキュリティ対策としても安定したインターフェース部分でバリデーションすることは合理的です。

 

 

 

 


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">