いつも堅苦しく「こうするほうが良い」とばかり書いているので、たまには「あまり良くない」と言われているセキュリティ対策も、有用かつ必要である例を紹介します。サニタイズの話です。
サニタイズ(Sanitize)とは消毒、汚れた物を綺麗にする事を意味します。汚れた物、つまり悪い物を除去・変換して綺麗にする処理がサニタイズ処理と言われています。悪い物を定義し排除する典型的なブラックリスト型の処理です。ブラックリスト型の処理を行うと「悪い物」の指定に「漏れ」が発生しやすく、間違いの元なのでセキュリティ処理では基本的に使わないことが推奨されています。
「サニタイズはするな!」これはほとんどの状況でサニタイズしない方が安全になる可能性が高くなるので正しいと言えます。しかし、これは全ての状況で正しいでしょうか?
銀行の勘定系システムの例
ますWebアプリに関係ないアプリでサニタイズが必要かつ妥当である例です。
随分昔の話ですが、あるプロジェクトで銀行の勘定系(お金を扱う系)のコードに”O”(アルファベットのO)を数字の”0″に変換するコードがある事を知りました。このコードは数値を期待するデータにアルファベットの”O”が含まれてる場合、問題無く処理できる数字の”0″に変換するものでした。
なぜこのようなコードが存在するかと言うと、このデータが外部から送信されてくるデータだからです。受信側は送られてくるデータをバリデーションやエラーで「処理不可能」とする事は可能です。しかし、これは妥当でしょうか?
「数値データにアルファベットが混ざっているのが間違い、送り側が悪いのだ!」と言っても送信側のシステムを改修する事は不可能です。送ってくる側が悪いからといって「貴方のトランザクションは実行できません」と送金処理が時々止まっても良いでしょうか?もちろん困ります。
「可用性」や「信頼性」もセキュリティ対策として守るべき要素です。システムが止まらない事が重要なのであれば、アルファベットの”O”を数字の”0”に変換するサニタイズも必要なセキュリティ処理です。
Webサービスを利用するWebアプリの例
同じような状況はWebアプリでも発生します。自分がコントロールできない(つまりバグが修正できない)Webサービスから「おかしなデータ」が送られてくる場合どうすれば良いでしょうか?もし、そのWebサービスが自分のアプリケーションに必須のWebサービスで「無効なデータが送信されてきました」とエラーにできない場合はどうすべきでしょうか?
時々送られてくるおかしなデータがサニタイズで何とかなるなら、何とかしますよね?どうしても使わなければならないWebサービスにおかしな仕様や困った仕様があって問題となるなら、サニタイズ処理で使えるようにするのは仕方ないです。
ユーザーが記入したHTML文を整形したり、許可しないタグや属性を取り除いたり(ブラックリスト型でなく、ホワイトリスト型で”正しいHTMLを作る”、とする方法)する場合などは妥当なサニタイズの使い方です。 ただし、この場合でも壊れた文字エンコーディングや制御文字はバリデーションで拒否すべきです。
出力時のエスケープやAPI
実は出力時の”エスケープ”や”エスケープが必要ないAPI”の利用は「サニタイズ」です。
”エスケープ”や”エスケープが必要ないAPI”の利用は出力対策として必須の対策です。「サニタイズが絶対悪」であれば、出力時にもバリデーションした後にサニタイズすることになります。(実際にはバリデーションは”入力”と”ロジック”の仕事)
参考:
2017年版 – OWASP TOP 10
このエントリはよく読まれているので2017年度版 OWASP TOP 10に追加予定の脆弱性を追記して紹介します。執筆時点(2017/4)ではリリース予定版が公開されています。
2017年版では”A7 – Insufficient Attack Protection”(不十分な攻撃防御)が追加されます。攻撃行われているにも関わらず、それを無視してサービスを提供することが脆弱性である、としてWebアプリ脆弱性トップ7の問題としています。
つまり「サニタイズをして、攻撃を無視すること」は「OWASPセキュリティガイドライン的には脆弱性」と見なされることになります。PCI DSSではOWASPなどのセキュリティガイドラインに準拠したアプリケーションを構築することを要求しています。従って、「サニタイズをして、攻撃を無視するアプリケーション」はPCI DSSに準拠しないアプリケーションと見なされる可能性が高くなります。
”A7 – Insufficient Attack Protection”(不十分な攻撃防御)脆弱性を作らないようにする最も良い方法は、セキュアプログラミング/セキュアコーディングの第一番のセキュリティ対策である「入力バリデーション」を行い、バリデーションエラーを発生させるユーザー/IP/IPレンジからシステムを保護します。
OWASPなどが言う入力バリデーションとはホワイトリスト型のバリデーションのことを言います。ブラックリスト型はバリデーションとしては脆弱であり、一部の例外を除き基本的には脆弱性である、と考えられます。
まとめ
一般に「良くない」と言われているセキュリティ対策でも状況によっては必要なセキュリティ対策になる場合があります。乱用には注意が必要ですが、自分が制御することが不可能なデータソースからの入力をどうしても受け入れなければならない場合、サニタイズ処理を採用するのは仕方ありません。(注:古いWebブラウザはこれをやり過ぎて、とんでも無い事になっていました。乱用は厳禁です)
全体を自分が作っているアプリなら、入力処理でサニタイズ処理をしなければならないような状況は、一部の例外を除きまずありません。サニタイズ処理する場合には細心の注意が必要です。(例:HTMLタグの除去)くれぐれも乱用には注意して頂きたいですが、一般に良くないとされる対策も例外として必要になるケースがあることを知っていて損はありません。
PHPのescapeshellarg関数もWindows用にはエスケープでなく、サニタイズを行っています。エスケープする方法が無いので危険な文字を削除するしか、安全性を保てないからです。入力のみでなく出力でもサニタイズが必要となる場合があります。※ ただし、コマンド出力はバリデーションでないと安全性を保証できないことは覚えておく必要があります。
広い意味では出力時の安全なAPIの利用やエスケープ処理も、サニタイズの一種と考える事も可能です。
原則はありますが、原則には例外もあります。セキュリティ対策は適材適所で使えば良いです。
参考:セキュアコーディングの10原則とバリデーションの原則
CERT Top 10 Secure Coding Practices
セキュアコーディングの第一原則は「入力をバリデーションする」です。入力バリデーションを行う際の基本原則には
- できる限り早くバリデーションする
- ただし、全ての正規化(入力データの形式変更、つまりサニタイズ含む)が終わってからバリデーションする
があります。サニタイズは正規化の一種なので、サニタイズすると再バリデーションしないとセキュリティ問題の原因になります。