ここ十数年で見聞きした、脆弱なアプリを作ってきたベストプラクティスもどきのアンチプラクティス、をリストアップしてみます。
限定された条件ではベストプラクティスと言える物でも、一般化するとアンチプラクティスになる物は多いです。
コード検査をしていると良く分るのですが、アンチプラクティスは強力な破壊力を持っています。ここに書いているアンチプラクティスを無くすだけでも、致命的な問題/脆弱なコードを多数無くすことができるでしょう。
特徴として原理や原則、適切な構造に合致しない物がアンチプラクティスになっています。
- 原理:コンピュータープログラムは妥当な入力でしか、正しく動作しない
- 原則:Fail Fast原則、失敗するモノはできる限り早く失敗させる、が守られていない
- 構造:セキュリティ対策では境界防御、それも最外周の防御、が欠かせないがアプリケーション境界の防御が不十分・不適切
TOP 10と題しましたが、アンチプラクティスの順序と重要性(悪影響)にはあまり関連性はありません。
SQLインジェクション対策にはプリペアードクエリ/プレイスホルダ”だけ”使っていればOK
SQLの中の変数がパラメーター”だけ”なら「プリペアードクエリ/プレイスホルダ”だけ”使っていればOK」でも構いません。
しかし、一般的なプリペアードクエリ/プレイスホルダは”SQLパラメーターだけ無害化”します。テーブルのソートカラムが変数、抽出カラムが変数の場合、識別子エスケープするかバリデーションしないと完全に無害であることを保証できません。
こういったケースではエスケープが必要であるのは当然の事なのですが敢えて無視して、危険なコードを書かせる習慣を付けること、に何の意味もありません。(脆弱性を作る習慣を作る、という意味がありますが!)
因みに、現在はソースコード検査を実施して見つかるSQLインジェクションに脆弱なコードの第一位は識別子のエスケープ漏れです。サブコンテクストのエスケープ漏れも日常茶飯事と言えるくらい多いです。
この現状は「プリペアードクエリ/プレイスホルダ”だけ”使っていればOK」とするアンチプラクティスの影響です。
「エスケープする」という考え方が古い、間違い、要らない、API”だけ”を使う。
「APIを使う」は「使えるAPIがある場合だけのベストプラクティス」です。
一般に適用可能なベストプラクティスとするなら、以下のような物でしょう。
- 危険な変数を安全に処理可能なAPIであると保証できる場合、そのAPIを利用する。ただし、安全に処理できない変数に十分注意し、他の必要な出力無害化(エスケープ、バリデーション)も併用する。
「APIだけ知って、使っておけばOK」はどう考えてもアンチプラクティスです。
「安全に処理できない変数に十分注意し、他の必要な無害化(エスケープ、バリデーション)も併用する」が必要だからです。
参考: 出力対策の3原則
「危険な変数を安全に処理可能なAPIであると保証できる場合」、当たり前の条件ですが、「安全性の保証が無いAPI」なのにAPIだから安全と誤認しているケースが少なくありません。最近は利用の前にエスケープを検索するのか少くなってきましたが、あまり利用する機会が少ないLDAPやXPathではかなり高確率で誤用がありました。
一般的な出力対策としてエスケープとバリデーションもどう見ても必要です。「エスケープは要らない」はベストプラクティスもどきでさえありません。
危険な変数を安全に処理できるAPIは沢山あります。しかし、危険な変数を安全に処理できないAPIも沢山あります。大抵のAPIの場合、完全なSQLインジェクション対策 で紹介しているようにAPIでは対応できない部分が結構あります。それを知らないと致命的な脆弱性の原因になります。
Webアプリはテキストインターフェースで出来ています。テキストインターフェースを「正しく」(=安全に)扱うには、コンテクストを正しく理解し、コンテクストに対して適切な処理を行って初めてセキュアなコードが書けるようになります。コンテクストを知り、無害な出力にする方法を知る一番の近道は「エスケープを知る」です。エスケープを知ればエスケープが要らないAPIが何故安全なのか理解/確認できます。
参考: そもそもエスケープとは何なのか?
もしプログラミング初心者に「エスケープは要らない、APIだけ使えばOK!」と教育したら、致命的なコードを書くこと間違いなし、のアンチプラクティスです。
入力文字エンコーディングはバリデーションしなくても構わない
これもベストプラクティスもどきでさえありません。不正な文字エンコーディング文字列がシステムに対してどのような悪影響を及ぼすか、ある程度複雑なシステムになると誰にも正確に把握できません。
例えば、データベースシステムが文字エンコーディングをバリデーションし、不正な文字エンコーディングの場合はエラーにする場合でも、入力処理での文字エンコーディングバリデーションは欠かせません。データベースに保存する時点では遅すぎます。データ保存処理の前に大量のコードを実行済みです。不正なデータはどこでエラーを発生させサービス不能状態やその他の想定外の動作を起こさせるか判りません。
「入力文字エンコーディングはバリデーションしなくても構わない」が通用するのは、利用する環境が文字列型が特定の文字エンコーディングであることを”入力処理”で保証する環境である場合のみです。入力データがバイナリの場合もあるので、入力処理で自動的に文字エンコーディングの検証を行う環境は今でも多くありません。1
「入力処理時の自動文字エンコーディングバリデーション」が機能的に存在しないのが普通の状況で、「入力文字エンコーディングはバリデーションしなくても構わない」はあり得ません。
ICUは文字エンコーディング処理を専門とするライブラリですが、ICUにでさえ文字エンコーディング処理の問題が今も時々見つかっています。文字エンコーディング処理の専門性に書けるプログラマが書いたコードを信頼すべきではないでしょう。
参考: ISO 27000の入力妥当性検証 ー 形式検証に文字エンコーディング検証は当然含まれる
出力時の対策”だけ”が重要なセキュリティ対策である
出力時の対策が重要なセキュリティ対策、であることは間違いありません。出力時に完全に無害化するのはベストプラクティスで、セキュアコーディングの第7原則です。しかし、出力対策”だけ”をすれば良い訳ではありません。出力対策”だけ”だとアンチプラクティスです。
セキュアな構造のアプリケーションは、データ処理時の不確実性(リスク)を廃除する為に「妥当なデータのみであることを保証する入力バリデーションを実施」します。アプリケーションは「入力処理 ⇒ ロジック処理 ⇒ 出力処理」の構造をもっており、プログラミングの超基本原則であるFail Fast原則に則り、不正データは1行でも早くエラーとして廃除しなければなりません。
このアンチプラクティスに勘違いさせられた場合はセキュリティ対策の定義から見直した方が良いです。セキュリティ対策とはリスク管理であり、不確実性の管理です。
「出力時の対策こそが重要なセキュリティ対策である」としている同じ人が
”「エスケープする」という考え方が古い、間違い、要らない、APIだけを使う”
とするアンチプラクティスを推奨していたりします。これらを組み合わさると、簡単に致命的な問題の原因になります。
入力バリデーションはセキュリティ的にはどうでも良い
これには「何故なら、出力対策だけで安全性が保証できるから」と続きます。しかし、出力”だけ”のセキュリティ対策はアンチプラクティスです。
既に書いた通り、セキュアな構造のプログラムは、データ処理時の不確実性を廃除する為に「妥当なデータのみであることを保証する入力バリデーションを実施」します。入力バリデーションはセキュアコーディングの第1原則です。
どうでも良いどころか、攻撃者は「不正なデータ」で攻撃してきます。「不正なデータ」を廃除する最初のソフトウェアセキュリティ対策が入力バリデーションです。
参考: ソフトウェアは「入り口ノーガード設計」のままで良いのか?
結局のところほとんどの攻撃、特に致命的な脆弱性への攻撃、は「不正なデータ」によって起きます。このため、セキュアコーディングの第1原則は「入力をバリデーションする」なのです。プログラムが正しく処理できない「不正なデータ」を受け付けるのは「どうぞ攻撃してください」、いわゆるノーガード戦法と同じです。
参考: 3種類バリデーションには3種類のバリデーションがある 〜 セキュアなアプリケーションの構造 〜
ブラックリストも(ホワイトリストと同様に)有効なセキュリティ対策である
流石に最近は聞かなくなりましたが、ブラックリスト方式は仕組み的に脆弱な対策です。
生半可な知識で、仕組み的に脆弱な、ブラックリスト方式のセキュリティ対策でソフトウェアを作ると、サイバー犯罪者とセキュリティ業者の餌食になるでしょう。ブラックリスト方式で確実な安全性を保証するには、全てのダメなモノ、を確実に定義しなければならず、とても困難です。
とは言っても、ブラックリストも有効な対策になる場合もあります。同じIPアドレスから大量に不正なリクエストが送信される場合、IPアドレスをブラックリストに載せてブロックするのは有効な対策です。
だからといって、ブラックリストとホワイトリストを同列に扱うのはアンチプラクティスです。
セキュリティ対策は、原則としてホワイトリスト方式の対策を採用します。
セキュリティ対策はリスクを廃除するモノ”だけ”を一つ一つ積み重ねるものである
一見ベストプラクティスに見えますが、”これだけ”ではアンチプラクティスです。
一つ一つ積み重ねなくても、投網を投げるように一網打尽にする方法もあります。コストが小さく仕組み的に一網打尽にできる方法があるなら採用すべきでしょう。入力対策はその1つで、厳格な入力バリデーションはほとんどのリスクを廃除することが可能です。
参考: ほぼ全てのインジェクション攻撃を無効化/防止する入力バリデーション
ソフトウェアのセキュリティ対策ではないですが、開発環境にあるソフトウェアから全ての脆弱性を廃除するのは無駄です。無駄というより全ての脆弱性を廃除するのは無理でもあります。投網を投げるように、環境として安全であることを保証/検出します。
◯◯”だけ”すれば良い/使えばよい
少し抽象的ですが、”だけ”すれば良い、が出て来たらアンチプラクティス注意信号です。大抵の場合、1つのこと”だけ”すれば良い、が出て来たら赤信号です。
- APIだけ使えば良い
- 出力対策だけしっかりやれば良い
この2つは完全に赤信号、アンチプラクティス、です。
参考1:完全なSQLインジェクション対策
参考2: 「出力対策だけのセキュリティ設計」が誤りである理由
「APIだけ使えば良い」は、かなり整備されたフレームワーク環境だと結構な割合で「APIを使えば良い」場合が多いです。
しかし、サイバー犯罪者やセキュリティ業者との戦いは開発者にとって著しく不利な条件の戦いです。1つでも攻撃可能な問題があったら、開発者の全面的な敗北です。
- APIだけ使えば良い
- 出力対策だけしっかりやれば良い
この考え方だと簡単に”穴”が開いてしまいます。APIだけだと穴が開き、出力対策だけだと対策が遅すぎて管理不可能な不確実性(リスク)を生みます。
セキュアコーディングの第1原則の
- 入力をバリデーションする
”だけ”をしても安全にはならない事と同じです。
”だけすれば良い”、は”特定条件下の時”に、”だけすればよい”のですが、”特定条件下の時”、が丸ごと抜けているアンチプラクティスが多いです。
◯◯は不必要/重要ではない
これも抽象的ですが、どういう訳けかコンピューターサイエンティストがセキュリティ対策の原則とするモノ、を不必要/重要でないとする事をベストプラクティスにする事例が見られます。
- 入力バリデーションは要らない/重要でない – セキュアコーディングの第1原則
- エスケープは要らない/重要でない – セキュアコーディングの第7原則に欠かせない
が代表例です。ここでは説明を省略しますが、どちらも必要かつ重要です。
「◯◯”だけ”すれば/使えばよい」 と「◯◯は不必要/重要ではない」は最悪の組み合わせ
これらのアンチプラクティスをベストプラクティスと信じて「APIだけ使っていれば良い と エスケープは要らない」を思ってしまった開発者は致命的なセキュリティ問題を量産する、サイバー犯罪者とセキュリティ業者にとって都合の良い開発者になります。
このタイプの開発者は、Railsやそれに類似のフレームワークでかなりの高確率でJavaScriptインジェクション脆弱性つくります。代表例が「HTMLヘルパー」です。
HTMLヘルパーを作る作業 = APIを作る作業
なのですが「APIだけ使っていれば良い と エスケープは要らない」と勘違いした開発者は120%、JavaScriptインジェクション脆弱性を作ります。エスケープしないのですから脆弱性を作って当たり前です。
HTMLヘルパーに限らず、「APIだけ使っていれば良い と エスケープは要らない」と勘違いしている開発者は、あらゆる種類のフォーマッター型のAPI作成で脆弱性を量産します。
もっとダイレクトに、出力コンテクスト/APIの中身や仕様を知らずに、危険なAPIの使い方による脆弱性も量産します。
「APIだけ使っていれば良い」 と 「エスケープは要らない」それぞれ単独でもかなり危険ですが、組み合わさると最悪のアンチプラクティスです。
まとめ
ある程度具体的なモノで、これ一つ”だけ”を使えば良い、はほぼ全てアンチプラクティスです。セキュリティ対策には”網羅性”と”完全性”が必要で、”だけ”とは相容れないです。ブラックリストが仕組み的に脆弱な理由は”網羅的”で”完全”な正しさの保証が困難だからです。
原則であるモノでも、これ一つ”だけ”でOKとなるモノはほぼありません。セキュアコーディング原則でさえ、個別の原則”だけ”で十分になるものはありません。個別の原則”だけ”だとアンチプラクティスになります。原則を無視するのは当然アンチプラクティスです。特別な理由が無い限り、原則が一つでも欠けるとアンチプラクティスになります。
これ”だけ”でもで十分となるモノは、セキュリティ対策の基礎概念である”ゼロトラスト”(何も信頼しない、信頼には確実な検証/保証が必要)くらいでしょう。何もかも信頼しないで検証し、確実に信頼できるモノだけでシステムを構築すればかなりセキュアなモノになります。 2
ベストプラクティスでないアンチプラクティスが普通にベストプラクティスとして通用してしまっているのは、セキュリティ専門家の責任によるところが大きいと言わざるを得ないでしょう。
アンチプラクティスを推奨していたケースがあっても、それに対する適切な対応が他のセキュリティ専門家からありませんでした。これでは一般開発者がアンチプラクティスをベストプラクティスと勘違いしても仕方無いでしょう。
広くベストプラクティスと勘違いされている「プリペアードクエリ/プレイスホルダだけ使えば良い」は、識別子エスケープ漏れを量産する考え方であることは最初から分かっていました。
参考: DBMSの脆弱なAPI仕様は何時まで放置されるのか?
SQLインジェクション対策では、エスケープも重要かつ必要、両方使わなければならない、出力コンテクストを意識し、対策の完全性を常に意識しなければならない、としていれば他の出力での間違いも少なくなったに違いありません。
「入力バリデーション」に至っては、明らかに様々な基本原則違反であるにも関わらず「要らない」「セキュリティ対策ではない」など、非論理的/非エンジニアリング的で出鱈目過ぎるアンチプラクティスが10年以上セキュリティ専門家によって啓蒙され続けてきました。3
サイバー犯罪者やセキュリティ業者に都合が良いベストプラクティスもどきのアンチプラクティスは早く切り替えた方が良いでしょう。4
基礎的な原則: ゼロトラストとフェイルファースト
ベストプラクティス: CERT Top 10 Secure Coding Practices
NEWアンチプラクティス: 「脆弱性を局所的に潰す」はアンチプラクティス
参考:
- 昔、Railsで壊れた文字エンコーディングでJavaScriptインジェクションが可能になりそうだった問題がありました。出力時に例外が発生するから問題なし!となりました。しかし、この対応方法はベストプラクティスとは言い難いです。データの検証は入力処理の出来る限り早い時点で行うのがベストプラクティスです。 ↩
- 現実には完全に信頼できるモノだけでシステム構築することは不可能で、必ずリスク/不確実性が残ります。必ずリスクが残るので、どんなに頑張ってもリスク管理が必要になります。 ↩
- エンジニアリングとは構造的/科学的に正しいモノが作れるような原理/原則/指針/設計/実装/運用を構築する物です。職人的に何とかする物ではありません。 ↩
- このエントリを読んで「そうだったのか!?」と感じた方はエンジニアリンス的/科学的に正しい方向に軌道修正するだけセキュアなプログラムを作れるようになると思います。世の中には当たり前と広く思われているモノが、実は出鱈目だった、は少なくありません。不愉快に感じた方は、間違いなく根本的な間違い/勘違いをしています。例えば、セキュリティ対策は攻撃可能な箇所を補強/修正する物、とする認識は脆弱性を作る考え方です。最も難しいのは”理解ろうとしない人”に理解らせることだと言われています。修正されることを切に願います。 ↩
Leave a Comment