今回は「とあるネットワーク技術者がいかにしてネットワークシステムのセキュリティを守っているのか?」という話です。
「え?!」と思うハズですが、最後までお読みください。
入力処理と出力処理はプログラムの基本処理です。当たり前の処理ですが、Webアプリケーションでは基本的な事が知られているようで知られてないと思います。
入力処理と出力処理の両方ともがデータに対する責任を持っています。データに対する責任といっても、その責任は異なります。このエントリはプログラムの基本機能、入力処理/ロジック処理/出力処理が持っている責任を紹介します。
脆弱性がないプログラムを作るには、コードからの視点、データからの視点、両方が必要です。ソフトウェアセキュリティの専門家は両方の視点からのセキュリティ対策を啓蒙しているのですが、上手く伝わっていないです。
このエントリはデータからの視点で見たデータセキュリティの話です。

手っ取り早くリモートのLinuxマシン(私の場合はVMwareの仮想マシン)からDockerイメージを作りたくなったのでその手順です。
最低限の手順なのでサービス(nginxやhttpd)を動かしたい場合はカスタマイズが必要です。不必要なファイルもコンテナに入るので、サイズを小さくする場合は適当にファイルを削除してください。
きっちり作るにはベースOSに必要最低限のパッケージだけ入れたコンテナを作り、そのコンテナに必要なパッケージを入れて作ります。しかし、コンテナが大きくても構わない、アップデートをベースのコンテナとDockerfileのビルドで行わなくても構わない、といった際にサクっと作るには便利です。
入力バリデーションでほぼ全てのインジェクションリスクを回避/防止できるケースは以前書いたブログで紹介しています。
今回は趣向を変えて、入力バリデーションで許可してしまった文字で発生するリスクをざっと紹介します。
※ ここで紹介する考え方は脆弱なブラックリスト型です。より安全な方法はホワイトリスト型の考え方です。参考: ほぼ全てのインジェクション攻撃を無効化/防止する入力バリデーション
ソフトウェアのセキュリティ問題の多くは「ソフトウェアの誤作動」です。つまり、正しく動作するソフトウェアならセキュリティ問題の大半は発生しません。1
コンピュータープログラムが正しく動作するには「正しいコード」と「正しいデータ」の両方揃う事が必須条件です。これはプログラムの動作原理に基づく条件なので、誰にも変えることは出来ません。
ソフトウェアを開発している時に困るのは、ソフトウェアが正しく動作しないケース、に対応する事です。
実用的なソフトウェア作るよりもプロトタイプを作る方が簡単であるのは、ソフトウェアが正しく動作しないケース、に対応する必要がないことが大きな理由です。ソフトウェアが正しく動作しないケースに対応するには、様々な例外的な状態(入力データとソフトウェアの内部状態)全てに対応する必要があります。
例外的な状態を定義するには”ダメな状態”を定義する必要があります。”ダメな状態”を漏れ無く定義するのは結構大変です。ソフトウェアバグの多くは”ダメな状態”を漏れ無く定義することに失敗した為に生まれています。
参考:データセキュリティの概念がないと「正しく動作するプログラム」は作れない。
https://blog.ohgaki.net/learning-security-from-rdbms-data-security
https://blog.ohgaki.net/crude-software-security
先日、ソフトウェアセキュリティ対策が”雑”である、という話題になり「どのようなソフトウェアセキュリティ対策が”雑”なのか?」を説明しておかないとならないと感じました。
[名]いろいろなものが入りまじっていること。区別しにくい事柄を集めたもの。「雑の部」「雑収入」
[形動]大まかで、いいかげんなさま。ていねいでないさま。粗雑。粗末。「雑な仕事」「雑に扱う」
プログラムが正しく(=脆弱性がなく)動作するには
の両方が”必ず”必要です。このため、丁寧なソフトウェアセキュリティ対策には「コード」と「データ」の両方を丁寧に扱う必要があります。
今あるWebアプリの多くは「データ」の取り扱いが”雑”です。 (さらに…)
インジェクション攻撃、とは言ってもそのインジェクション対象によって影響が異なります。インジェクション攻撃の対象によって2種類、コードとデータ、に分類できます。Webシステムの場合、リクエストのインジェクションを別のインジェクション攻撃と考えた方が解りやすいので、大まかに分類して3種類に分類できます。
インジェクション脆弱性は絶対に避けなければならない脆弱性である、と認識されていると思います。しかし、3種類あるインジェクション攻撃のうちデータのインジェクションに対するリスク認識が極端に甘いように感じています。
※1 Webシステムを前提としていますが、大抵のシステムは「コード」と「データ」のインジェクションを考慮すれば十分である場合が多いです。
※2 そもそもコードを実行する機能そのものに”コード”を実行させるのは”正当な機能”です。eval( $_GET[‘code’] ) など、これらの機能の基礎的使い方間違いは考慮しません。ここでは本来コードではないデータを介するコードインジェクション、例えば eval( ’var = ‘. $myvar. ‘;’) など、及び、データその物で攻撃するデータインジェクションを取り上げています。
RDBMSはデータセキュリティを学ぶには良い題材です。RDBMSはできる限り正しいデータを保存する仕組みがあるからです。RDBMSからどのようにデータのセキュリティを保証するのか紹介します。
プログラムが正しく動作する為には
が絶対条件です。どちらが欠けてもプログラムは正しく動作しません。
SQLiteは手軽で良いのですが、組み込み型であるためデータセキュリティを学ぶには機能的に問題があります。SQLiteにはデータ型を保証する機能もデータの内容を検証する機能もありません。1
利用するRDBMSはPostgreSQLを用い、データセキュリティに関連性の強い機能のみを対象とします。
ソフトウェアのセキュリティを向上させよう!と日々奮闘している方も多いと思います。しかし現在、ソフトウェアセキュリティは十分、と言える情報からは程遠い状態です。
なにが足りないのか?なぜ不十分になるのか?基本的な部分の見落としに原因があります。
脆弱性を呼ばれた側の責任にする、は常に通用する考え方ではありません。
は一見正しく見えます。しかし、通用しません。
理由は簡単で、セキュリティ上困った動作をするライブラリやプログラムであっても、それが仕様で脆弱性ではないことがあります。
そもそもライブラリやユーティリティプログラムは幅広い用途に利用できるよう、用途を限定しないよう汎用的に作ってある場合が多いです。こういった仕様の場合、脆弱性を呼ばれた側の責任にする、ではセキュリティ問題を作ってしまいます。
ビルドを繰り返して時間があったので、ついついとても長いデータの話を書いてしまいました。今さら聞けない「コード(機能)」と「データ」の話として、もっと単純化してみます。
当然の話なのですが、現実には当たり前ではなかったりします。
「データ」のセキュリティを考慮しないセキュリティ対策はあり得ないのですが、多くのプログラムは「コード(機能)」のセキュリティに偏重した構造と対策になっています。
安全なプログラムの作成には「コード(機能)」と「データ」のセキュリティを分けて考える必要があります。
SQL文やコマンド実行には命令と引数を分離するAPIがあります。便利なAPIなのですが、安全性について根本的な勘違いが多いです。
これらは大きな勘違いです。
SQLのデータもコマンド実行のデータも、そのデータ出力の安全性は出力先(コンテクスト)でどのように処理されるかに依存します。
SQLインジェクションもコマンドインジェクションも1つでも間違いがあると大問題となる脆弱性ですが、大きな勘違いはかなり広く浸透しています。どこからこんな”とんでもない誤解”が生まれて、広まるのでしょうか?
コード”だけ”に着目すると脆弱性が量産されます。それはコードだけでは片手落ちであることが理由です。
言い換えると
プログラムの目的は「特定の機能」をコードによって実装することが目的ですが、動作する為には「データ」が欠かせません。
「データ」にも着目するとソフトウェアセキュリティは向上します。今実装されているソフトウェアセキュリティ対策は「コード(機能)」に偏重しているために脆弱になっています。
例えば、JSONPが危険な理由は「本来データであるJSONをプログラムとして実行している」ことにあります。
https://blog.ohgaki.net/stop-using-jsonp
X-Content-Type-Options: nosniff を指定するのは「本来データであるJSONをJavaScriptとして実行させない」ようにすることを目的です。X-Content-Type-Options: nosniff の導入は「データ」に着目したセキュリティ対策と言えます。「データ」として完全に分離し、「コード」として実行できないようにします。
開発者の多くはソフトウェアセキュリティを考える際、「どのようなコードで攻撃を防止するのか?」を考えていると思います。これだけでは何時まで経っても信頼性が高い(=セキュアなソフトウェア)物は”原理的”に作れないです。
※ 「コードだけに着目するセキュリティ対策」とは「攻撃が発生する箇所(コード)だけに着目するセキュリティ対策」を指しています。
プログラムを作っているとOSコマンドを実行したくなる時があります。OSコマンドの実行に問題があり、不正なコマンドをインジェクションされると大変な事になります。
どのようなセキュリティガイドラインでも「OSコマンドの実行に注意する」と大抵書かれています。
多くの場合、「コマンド実行時、コマンドと引数を分離すれば安全に実行できるAPIを利用すれば安全に実行できる」としています。
この記述は間違ってはいないです。しかし、正しくは
です。
コマンドと引数を分離するAPI利用だけでは完璧とは言えないセキュリティ対策です。