アプリケーションとライブラリでは作り方/設計が大きく異なります。この違いを理解していないとセキュアなアプリケーションの構築が困難、というより不可能になります。
ライブラリの作り方 – 汎用性重視
ライブラリを作る場合、汎用性を重視した設計で作ります。機能は決まっていますが、実際に使う場合にできる限りデータに制限がないように作ります。
例えば、正規表現ライブラリなら正規表現の長さやマッチ対象の文字列の長さに制限は設けないでしょう。普通は「メモリが許す限り」の正規表現と文字列を許可します。
数値計算を行うライブラリでも、可能な限りの範囲、つまりできる限り大きな最大値/最小値となる入力パラメーターを許容するように作ります。
ファイルやデータベースを取り扱うライブラリ関数も、基本的には無制限(ファイルシステム/データベースシステムが許容する物なら全て)に作ります。
ライブラリの場合は汎用性、つまり色々な用途に使えるように可能な限り制限を設けない様にします。エラーも極力発生しないようにする場合もあります。ライブラリを作ったことがある方なら、この方針で作っている/作られていると理解ると思います。
エラーを発生させない為にサニタイズを行うのは本当はNGですが、そうしているライブラリも少くありません。Railsセキュリティガイドを見れば分りますが、Railsのライブラリの多くがこの方針で作られています。
アプリの作り方 – 専用の用途重視
アプリケーションは特定/専用の用途のために作ります。アプリケーションの作り方はライブラリの作り方と正反対の設計思想で作ります。
アプリケーションは特定/専用の用途なので、データを無制限に許容しません。普通の設計なら
- 氏名に1GBのデータを保存できる
- 電話番号に1000桁の番号を保存できる
- 正規表現のマッチ対象に1GBのデータが使える
- 1TBのアップロードファイルを保存できる
といった馬鹿馬鹿しいほど大きなデータを許容しません。現実的/仕様的に”ライブラリ関数が許容する制限より、遥かに小さい制限”を設定します。
Webアプリで注意しなければならない点は、Webフォームなどで直接アプリが使うデータ以外のデータバリデーションが甘い事です。Webフォームなどで直接アプリが使うデータ以外もフレームワークやライブラリで使われていることが少くありません。
- HTTPヘッダーに余計かつ出鱈目なデータ(攻撃データ)が設定される
- POSTの値に余計かつ出鱈目なデータ(攻撃データ)が設定される
- GETの値に余計かつ出鱈目なデータ(攻撃データ)が設定される
攻撃者はこういった攻撃行為を普通に行います。ライブラリの多くは汎用的に作られており、アプリケーションでバリデーションしないと出鱈目なデータでもそのまま処理してしまいます。フレームワークであっても入力データをバリデーション無しで使っていたりします。
以下のブログではContent-Typeのみに言及していますが、Struts2ではContent-Type、Content-Length、Content-Dispotisionといった本来プログラム(OGNL)が書けないハズの入力値にプログラムが書けてしまった脆弱性がありました。
こういった事例はフレームワークに限らず、バッファーオーバーフローまで含めると枚挙にいとまがありません。無制限(データバリデーションなし)の入力値はリスクの塊であると言えます。
この事例は極端な例ですが、もっと身近で一般的な問題はおかしな文字エンコーディングです。
ライブラリの多くはバイナリデータも普通に扱えるように作ってあります。所謂バイナリセーフなライブラリにする事が多いです。しかし、普通のアプリケーションが扱うデータの多くは文字列です。普通は壊れた文字エンコーディングのデータをバイナリとして扱い、何もなかったように処理すべきではありません。
丁度良い最近の実例があります。GPGのファイル名の取り扱いがバイナリセーフ型であったことが原因で、署名の安全性が保証できない問題がありました。GPGの作者は、利用者が使っているファイルシステムは分らないし、ファイル名の付け方も分らないのでできる限り”無制限”にした、のだと考えられます。GPGは”アプリケーション”なので、”アプリケーションの目的/仕様”に合ったファイル名に制限すべきであったのに、していなかった事が問題の原因になりました。
ライブラリの多くは”データサイズ”に制限を設けていません。アプリケーションがデータサイズに制限を設けていないと、様々なDoS攻撃が可能になってきます。
ライブラリが制限無しだとは言っても、様々な制限をライブラリやフレームワーク/言語が持っていて”何処か”でエラーになるから大丈夫!でもありません。エラーになって攻撃できないだけではセキュアなアプリケーションとは言えません。OWASP TOP 10では、攻撃者が送ってくるような出鱈目なデータに適切に対処できないアプリケーションを”明確に脆弱なアプリ”だとしています。
アプリケーションはデータに対して”制限的”でないと問題を生む原因になります。
データを”制限”するのは誰の責任か?
アプリケーションのデータは”制限”されていなければなりません。
ではアプリケーションのデータを制限するのは”誰の責任”でしょう?
基本的にライブラリにデータを制限する責任はありません。無制限でコンピューターのリソースを使い尽くすまで動作するような仕様でも構いません。セキュリティ対策と言えるような仕様でさえ、ライブラリには実装する責任はありません。ライブラリレベルなら”仕様”としてしまえば何でもありです。
フレームワークには多少の責任がありますが、フレームワークもライブラリの一種です。基本的には”汎用的”に作られています。基本的にフレームワークはアプリケーションで制限(バリデーション)すべきデータを制限する責任を持ちません。
”誰の責任”の答えは既に明らかです。汎用的なライブラリを使用して作られた、何らかの目的専用に作らたアプリケーションにデータを制限する責任があります。
脆弱性をライブラリ/フレームワークの責任にしてしまえば良い、と考えている方も少なからず居ます。しかし、ライブラリは基本的に”できる限り無制限”に作る物です。
画像を読み込んだだけで任意コード実行が出来てしまう画像処理ライブラリのセキュリティ問題のように、ライブラリの責任にできる/せざるを得ない場合もありますが、基本的には”アプリケーションにデータを制限する責任”があります。
アプリケーションがセキュアコーディングの第一原則と第七原則を実践すると、自然とこの責任を果たせます。
コンピューターの動作原理とセキュリティ
セキュアコーディング原則を持ち出さなくても、コンピュータープログラムは出鱈目なデータではどうやっても正しく動作しません。
そもそもプログラムが正しく動作する為には妥当(正しい)データが必須です。これがプログラムの動作原理です。
出鱈目なデータはセキュリティリスクでしかなく、Fail Fast原則に則りできる限り早い段階で廃除しなければなりません。
どこかでエラーになる、データ利用の最終段階といえる出力でエラーになる、だから問題ないとはなりません。
妥当でないデータが出力時にエラーとなり、不正なデータが処理/保存されないならまだ良いのですが、処理/保存してしまう「脆弱性」を持っているアプリケーションはとても多いです。
関連: ライブラリ等のセキュリティ関連APIを利用することは、ソフトウェアセキュリティとイコールではない。