妥当性とは仕様の所作 - SQLインジェクション対策とバリデーション


繰り返しになりますが、妥当性検証は仕様の問題であってセキュリティ対策ではありません。

バリデーションは仕様の問題であってセキュリティ対策ではないとはどういうことか説明します。SQLインジェクションの対策は、1. SQLを文字列結合で作らない 2. プレースホルダを使う です。バリデーションは関係ありません。

簡単な例

Webアプリケーションで郵便番号を指定するフォームを考えましょう。
日本の郵便番号を指定するフォームの設計でよく見るものは大きく分けて2通りあり、上3桁と下4桁を別々に入力させるものと、1つのフォームにまとめて入力させるものです。住所から補完させる設計もありえますがここではおいておきます。


<input type="text" name="postal_code_1"> -
<input type="text" name="postal_code_2">


<input type="text" name="postal_code"> 例:100-0001

どちらを選んでもよいのですが、選んだ設計によって仕様が変わることに注意してください。
クエリパラメタ postal_code_1 を受け取ったサーバは数値のみを妥当な入力として受け入れる仕様となるでしょう。その際にいわゆる半角数字のみを受け入れるのか、いわゆる全角数字が入力されたら適切に変換してあげるのかを決める必要があります。
postal_codeの場合は数値のみ7桁の入力を受け入れるか、ハイフンを必須とするか、いわゆる全角のハイフンはどうかなどをさらに意思決定する必要があります。

ここでは以下の一連の意思決定をし、それを共有して仕様としたとします。

  • クエリパラメタ postal_code_1 は郵便番号上3桁の数値文字列を受け取る
  • クエリパラメタ postal_code_2 は郵便番号上4桁の数値文字列を受け取る
  • いわゆる全角文字の入力については、いわゆる全角数値がUTF8で表現されている場合のみ受け入れた上でサーバ側でいわゆる半角数値に変換する
  • 入力が数値のみで構成されているが必要な桁数に満たない場合は、「7桁の郵便番号を入力してください」というメッセージを表示して再入力を促す
  • それ以外の入力については、「郵便番号を入力してください」というメッセージを表示して再入力を促す

ここで行うバリデーションは、postal_code_1/postal_code_2 それぞれが「数値」のみで構成されているかどうか、必要な桁数であるかどうか、になります。
ここまでの仕様をさだめないとバリデーション(妥当性検証)は定義できないことに注意してください。何をもって妥当とするかは仕様が定めるものであり、従って仕様がなければ妥当性を論じることはできません。また、postal_codeに7桁分入力される場合は全く異なるバリデーションを行うことになります。

バリデーションがセキュリティ対策とは異なる具体例

マイクロブログサービスを作るとしましょう。

  • ユーザのつぶやきは最大140文字(バイト数ではなく文字数)
  • 空のつぶやきは受けつけない
  • 妥当なUTF8エンコーディング文字列のみを受けつける
  • 140文字を超えるつぶやきはメッセージを表示して再入力を促す

この仕様のもとでできるバリデーションは

  • つぶやきのエンコーディングが妥当かどうか
  • 文字数が1文字以上140文字以下であるかどうか

だけです。「"K&R" from 」のようなつぶやきは仕様上妥当な入力ですので受け入れる必要があります。
この入力にはSQLやHTMLとして特別な意味を持つ文字が含まれていますが、そもそもRDBMSとのやり取りで適切にプレースホルダを使用していたり、HTMLとして出力する直前にHTMLとして特別な意味を持つ文字を適切にエスケープしていれば気にする必要はありません。
ここで、「危険wwwな文字列を削除すればいいんじゃね?www俺天才wwwww」として「KR from OReilly」のようにしてしまう愚行など、何が正しいか理解しないで不適切な対症療法をとることを専門用語で「サニタイズ脳」と言います。

バリデーションは仕様の問題であってセキュリティ対策ではないというのはこういう意味です。繰り返しになりますが、SQLインジェクションの対策は、1. SQLを文字列結合で作らない 2. プレースホルダを使う です。バリデーションは関係ありません。
文字列結合でSQLを構築するなど不適切な取り扱いをしている場合は、上記のような仕様ではバリデーションを行なってもおかしなSQLになることでしょう。