「攻撃者が”嫌う”セキュリティ対策=効果的なセキュリティ対策」です。これに異論は無いと思います。
攻撃者が最も困るセキュリティ対策とは何でしょうか?それは境界防御です。なぜ境界防御が攻撃者が最も困るセキュリティ対策なのでしょうか?それはCWEやCVEを見ればわかります。CWEやCVEに登録されている脆弱性の多くが、境界防御で守れるからです。
境界防御とは?
境界防御はWebアプリケーション開発者に最も誤解されているセキュリティの基本概念だと思います。これについては先日既に書いています。まだ読まれていない方はぜひ標準と基本概念から学ぶ正しいセキュリティの基礎知識を参照してください!
攻撃者が最も困る対策は入力バリデーション
入力バリデーションは様々な脆弱性を防止または緩和してくれます。自分が書いた出力コードに脆弱性がある場合でも、利用しているライブラリに脆弱性がある場合、利用しているサーバーの実装に脆弱性がある場合でも問題を緩和もしくは防止してくれます。CWEやCVEを羅列しても良いのですが、それでは大量で退屈なので幾つか例をあげます。
正しくバリデーションを行うと多くの問題を防止または緩和できます。一般にソフトウェア開発におけるバリデーション(Validation)とは、パラメーターなどが「正しい物か確認」する処理を言います。「不正な物」は境界防御で排除すべきなので、バリデーションエラーとなる不正な入力は受け入れないようにします。
正しい入力バリデーションとは「ホワイトリスト方式」(既知の良い物のみを許可)でのバリデーションを言います。入力バリデーションに失敗する入力とは「アプリケーションで許可しない入力」です。ユーザーの入力ミスは「アプリケーションが許可する入力」です。稀に「入力バリデーションはできない」と勘違いしている方が居ますが、ほとんど全ての入力はバリデーションできます。
ホワイトリストの基本中の基本は”デフォルトで全て拒否する”であることに注意してください。全て拒否した上で、許可するモノ、を指定しないとホワイトリストになりません。
ブラックリスト方式(既知の悪い物をのみを不許可)は誤りを生みやすいバリデーション方式です。「サニタイズ方式」(既知の悪い物をのみ除去)はブラックリスト方式の一種です。悪い物を除去して入力を受け付けてしまうのでバリデーションではありません。稀にブラックリスト方式、サニタイズ方式を採用した方が良い場合もありますが、基本的には常に「ホワイトリスト>ブラックリスト」と考えます。
バリデーションで緩和または防止可能な脆弱性
- 数値のみでバリデーションする場合、SQLインジェクション、JavaScriptインジェクション、LDAPインジェクション、XPATHインジェクション、コマンドインジェクション、ヌル文字インジェクション、改行インジェクションなどに加え、パスインジェクション(パストラバーサル)、エンコーディングベースのインジェクションを含むインジェクション攻撃全てが防止できる。これに加えバッファーオーバーフローなどの利用しているライブラリの脆弱性も緩和または防止できる。
- 英字のみでバリデーションする場合、SQLインジェクション、JavaScriptインジェクション、LDAPインジェクション、XPATHインジェクション、コマンドインジェクション、ヌル文字インジェクション、改行インジェクションなどに加え、パスインジェクション(パストラバーサル)、エンコーディングベースのインジェクションを含むインジェクション攻撃全てが防止できる。これに加えバッファーオーバーフローなどの利用しているライブラリの脆弱性も緩和または防止できる。
- パラメーターの形式(ホスト名、メールアドレス、ドロップダウンの値、電話番号、氏名など)をバリデーションする場合、SQLインジェクション、JavaScriptインジェクション、LDAPインジェクション、XPATHインジェクション、コマンドインジェクション、ヌル文字インジェクション、改行インジェクションなどに加え、パスインジェクション(パストラバーサル)、エンコーディングベースのインジェクションを含むインジェクション攻撃全てが防止または緩和できる。これに加えバッファーオーバーフローなどの利用しているライブラリの脆弱性も緩和または防止できる。
- 文字列の長さを短い長さでバリデーションする場合、SQLインジェクション、JavaScriptインジェクション、LDAPインジェクション、XPATHインジェクション、コマンドインジェクション、ヌル文字インジェクション、改行インジェクションなどに加え、パスインジェクション(パストラバーサル)、エンコーディングベースのインジェクションを含むインジェクション攻撃全てが緩和できる。これに加えバッファーオーバーフローなどの利用しているライブラリの脆弱性も緩和または防止できる。
- 制御文字を含まない事をバリデーションする場合、ヌル文字インジェクション、改行インジェクションなど防止できる。SQLインジェクション、JavaScriptインジェクションも緩和できる。これに加えバッファーオーバーフローなどの利用しているライブラリの脆弱性も緩和または防止できる。
- 文字エンコーディングをバリデーションする場合、文字エンコーディングベースのSQLインジェクション、JavaScriptインジェクション、コマンドインジェクションを緩和または防止できる。
- 数値の範囲をバリデーションする場合、利用しているライブラリなどの整数オーバーフローを緩和または防止できる。
- パラメーターの数と名前、その組み合わせをバリデーションする場合、SSRFを用いた攻撃を緩和または防止できる。PHPのregister_globalsやRailsのマスアサイメントのような仕様を持つシステム/ライブラリの脆弱性を緩和または防止できる。
ざっと書いたので書き漏らしている物もあると思いますが、これでも十分に多いと思いませんか?現在はJavaScriptを使ってクライアント側で送信するパラメーターの形式を予め制限できるので、昔より厳格なバリデーションが可能です。できる限り厳格なバリデーションを行えば、より高いセキュリティを実現できます。(注:クライアントでの制限は「信頼境界線」の外の制限なので、サーバー側では無い物として扱い、サーバー側で再度バリデーションします)
これだけ多くの脆弱性が緩和または防止されてしまうと、攻撃者が最も困るセキュリティ対策であり、アプリケーション開発者が「最大のセキュリティ対策」として必ず採用すべき対策であることは明らかです。私もSANSのセキュリティ専門家も、この状況を理解しているので「入力バリデーションを確実に行うべき」としているのです。当時はまだSANS CWE TOP25は無かったですが、拙著のWebアプリセキュリティ対策入門にも書いています。基本は変わっていないので、ソフトウェアセキュリティを理解している人は同じ考えを随分昔から持っていたと思います。
アプリケーションを作っている開発者本人であれば、各入力パラメーターがどのようなパラメーターであるのか知っているはずです。やり方さえ知れば確実な入力バリデーションを行えるようになります。開発者がどのような入力パラメーターなのか知らないなら、作っているアプリの入力をわからずに作っている事になります。開発体制に何らかの問題がある可能性があります。
「とは言っても、自由に入力できるデータがある場合の効果は低いのでは?」と考えるかも知れません。実際に発生する脆弱性の多くはIDなどの数値・英数字のみ、特定のフォーマットを持つ入力(ドロップダウンやラジオボタン)に対するケアレスミスで発生しています。自由に入力可能なデータに関しては、開発者の多くが注意しています。十分に効果的です。
入力バリデーションの実装はアプリケーション開発の初期段階から入れる必要はありません。最近の開発形態では仕様変更が頻繁に発生するので、開発速度を遅くするだけです。ある程度仕様が固まってから入力バリデーションを導入すると良いでしょう。
入力バリデーションの大きなメリットの一つに「開発者がリスクを認識していない問題にも対応可能」があります。例えば、ライブラリなどのバッファーオーバーフローなど普通は注意しませんよね?ここエスケープが必要だったの?(SQLの識別子、タグの属性名、コマンド引数など)とか、うっかりしてた!という問題にも対処できる可能性があります。
攻撃者が次に困る対策は確実な出力
次に困るのは「確実に安全な出力で」す。これは「APIを使う」では実現しません。APIの使用方法を誤った事が原因で発生した脆弱性は数えきれません。適切なAPIが無い場合もあります。では、どうすべきか?「出力先の入力仕様を正しく把握」し確実に安全な出力を行います。
安全な出力に必要な「出力先の入力仕様」を正しく理解する事が必要です。安全な出力を知るために、出力先の全ての仕様を知る必要があるでしょうか?必要ありません。出力先がテキストインターフェースを持つなら、テキストインターフェース処理の基本、インジェクション対策の基本を理解した上で「エスケープ仕様」を知れば良いだけです。
Webアプリは基本的にテキストインターフェースのコンポーネントから成り立っています。SQLクエリ、XPathクエリ、LDAPクエリ、OSコマンド、HTTP、STMP、HTML、CSS、XML、JavaScript、JSON、全てテキストです。中には適切なエスケープAPIさえ無い物もあります。セキュリティの事を考えるとエスケープファーストは変えられないのです。安全に利用できるAPIがあれば使えうべきです。エスケープAPIもAPIの一種です。他に”安全”で便利なAPIがある場合、勿論それを使っても構いません。(一見、安全に見えるAPIでも完璧に安全でない場合は多いです。確認が必要です)OSコマンドのようにエスケープが非常に煩雑な場合もあります。このような場合には安全な出力となるようなバリデーションが良いでしょう。エスケープ仕様を理解していれば安全なバリデーションが可能になります。
入力バリデーションと違い、確実な出力は開発の初期段階から確実に行うようにすべきです。特に複数のパーサーが介在するHTMLやOSコマンドへの出力は安全性の維持が複雑になります。出力先を理解し、一つ一つを確実に出力するようにしないと漏れが発生する確立が高くなります。
SSRFは企業の情報セキュリティを守る側からすると厄介な問題になっています。リダイレクトする場合、ホスト名のみでなく、プロトコル、ポート、パラメーターの数・組み合わせもチェックすると安全です。
まとめ
反対に攻撃者が歓迎するセキュリティ対策とは何でしょうか?境界防御を行わない事です。簡単に実行できる境界防御をせずに、色々な脆弱性を攻撃できる可能性を開いているくれる境界”無”防御は大歓迎です。Web通信がネットワークのファイアウォールで許可された際、攻撃者とセキュリティ研究家は「これでまだまだ攻撃できる」と小躍りして喜んだという話もあります。実際、Webサーバー、Webクライアントの両方が攻撃者の格好のターゲットになっています。
WAF(Web Application Firewall)はネットワークの境界に置けるので、ネットワークセキュリティを行っている人が欲しがる機能です。しかし、WAFの導入と運用はコスト(遅い&高い)がかかります。アプリケーションで比較的簡単に実施できるホワイトリスト方式の入力バリデーションをWAFで実施するのは難しいです。現在の技術環境では、WAFはアプリケーションが直ぐに改修できない場合や軽量なチェックのみに限定、フェイルセーフに徹する方が良いでしょう。環境によってはWAFで頑張るしか無い場合もありますが、Web開発に関わる方達はWAFなど要らないアプリケーションを作るべきです。WAFでできることのほぼ全てが入力バリデーションで行えます。
そもそもWebアプリはWAFに頼るべきでない(普通はアプリの信頼境界の外だと考える)のでWAFがあっても無くてもWebアプリでは境界防御を行うべきです。ネットワークで端末・クライアント全てにファイアウォールを導入する事と同じです。
Web開発者からテキストインターフェースに直接アクセスする”自由”を奪ってセキュリティを維持するべきだ、と考えている方も居ます。しかし、それはWeb開発者からテキストインターフェースに直接アクセスする”自由”を確実に奪うこと可能になり、その環境が当たり前の環境になってから、メリット・デメリットを天秤にかけて採用すれば良い話です。そもそもそのような環境は存在しない、利用できないのでは現在の話ではありません。セキュリティ問題は今現在の問題です。存在しない、利用できない環境での理想はこうだ、と必要かつ効果的なセキュリティ対策を否定したり、有効性を疑問視して採用しないのであれば「現在」の安全性の確保は困難です。セキュリティ対策にはリアリズムが必要です。
攻撃者が最も困るのは確実な「入力」と「出力」のセキュリティ対策です。攻撃者が最も困るセキュリティ対策から始めるのは合理的な対策です。攻撃者が最も困るセキュリティ対策を確実に行いませんか?成果がある事は保証します。
世界屈指のセキュリティ専門家も同意する、現在最も有効なセキュリティ対策を採用しない理由はありません。基本は簡単です!楽しくセキュアにコーディングしましょう。
参考
Leave a Comment