ソフトウェアにバグがあった場合、そのバグは”ただのバグ”で”単純に意図しない余計な動作をする箇所を直す”、で万事OK!でしょうか?
ここでは”ただのバグ”とは”問題が起きた時に問題が起きた箇所を直せばOK”なバグとして話を進めます。
よくある”ただのバグ”修正
インジェクション脆弱性もバグの一種です。多くの場合、インジェクション脆弱性は”ただのバグ”として修正されています。
以下はPHPのPostgreSQLモジュールを使った脆弱なSQLクエリ例です。クエリパラメーターの$_GET[‘id’]はそのまま渡されているとします。adminはシステム管理者フラグとします。
pg_query($db, ‘UPDATE myusers SET admin = true WHERE id = ‘, $_GET[‘id’]);
これは明らかにSQLインジェクションに脆弱なコードです。”ただのバグ”として以下のような修正をすることが多いでしょう。
pg_query_params($db, ‘UPDATE myusers SET admin = true WHERE id = $1’, [$_GET[‘id’]]);
または
pg_query($db, ‘UPDATE myusers SET admin = true WHERE id = ‘. pg_escape_literal($db, $_GET[‘id’));
アプリケーションによってはこれでも十分となるケースもあるでしょう。しかし、これで万全!万事OK!でしょうか?
この”ただのバグ”修正の問題点
例に挙げたSQLインジェクション脆弱性の修正はSQLインジェクションは防ぎます。しかし、この修正”だけ”では問題が残ります。
- 不正な入力によるSQLエラーは防げない
- 任意のユーザーをシステム管理者にしてしまうことを防げない
システム開発の基本的な原則に
- 他のシステムに命令やデータを送る場合はそのシステムで誤作動したり、エラーとなるような命令やデータは送らない
があります。妥当な命令/データを送る責任は、命令/データを送る側にあります。この原則を一言で言い表せる単語は無いですが、CERTセキュアコーディング原則の7番目(出力を無害化する)がこれになります。
参考:
脆弱性があった、”ただのバグ”として修正、ではNG
プリペアードクエリ/エスケープだけでは、PostgreSQLがクエリを実行した際にエラーにならないようにするには明らかに不十分です。
- IDが整数型の場合、整数以外の$_GET[‘id’]はSQL構文エラーになる
例のクエリは、コードによっては
- IDが文字列型の場合、$_GET[‘id’]が出鱈目でも保存する
となってしまう事があります。
SQLiteのようにデータ型に関係なく文字列を保存できるデータベースも存在します。
“ただバグ”として修正してもOKな場合とは?
この例のクエリを”ただのバグ”として修正してOKな条件は
- 入力データが妥当である場合
- SQLクエリを送信するユーザーが修正権限を持っている場合
に限られます。つまり、この条件を満たしていない限り”ただのSQLインジェクション脆弱性バグ”として修正してもダメということです。条件を満たすには入力データの妥当性、ユーザー権限の検証が必須です。
データベースのレコードIDは整数であるとします。この場合、入力データが”整数として妥当”であることをバリデーションする必要があります。
このクエリを実行するユーザーがシステム管理者フラグを編集可能であることをバリデーションする必要もあります。
どのようにバリデーションすべきなのか?
余程単純なアプリケーションでなければ、開発者みんなが大好きな構造化、が必要になります。”ただのバグ”に見えるセキュリティ問題バグでも同じです。
アプリケーションのデータベースレコードIDは整数のみである場合が多いでしょう。この場合、整数以外のレコードIDをアプリケーションが受け入れる必要はありません。
Fail Fast原則に従い、エラーとなる出鱈目な入力はできるだけ早くエラーにします。MVCアーキテクチャーならコントローラーが最適でしょう。OWASP Source Code Review Guideでいうことろのアプリケーション入力データのバリデーションをコントローラーで行う構造が最適です。無条件にエラーにできる物はFail Fast原則でできるだけ早くエラーにしてしまう方がリスクが少なくなる、よい構造になるからです。
SQL文を実行するユーザーの権限、対象となるユーザーIDが管理者権限を持っても構わないのか、これらのバリデーションも必要になります。
これらはOWASP Source Code Review Guideでいうところのビジネスロジックバリデーションになります。MVCアーキテクチャーならモデルでバリデーションするのが最適です。
実行中のユーザーが編集権限を持っていない、編集対象のユーザーが管理者となることを許可されていない、といったビジネスロジックのエラーが発生した場合に対話的なUIが必要、つまり無条件にエラーとなるリクエストとして拒否できない場合もあります。ビジネスロジック処理、対話的UIが必要な処理はモデルで行うのが良いMVC構造になります。
良い構造にすると自然とセキュアコーディングの構造になる
セキュアコーディングの構造は単純かつプログラムの動作原理に則っています。この為、原理や原則に従い論理的にプログラムを構造化すると、バリデーション構造はセキュアコーディングのプログラム構造に自然となってしまいます。
”ただのバグ”として修正して構わない条件
アプリケーションの出力時のセキュリティ問題を”ただのバグ”として修正して構わない条件は出揃いました。
プログラムが”セキュアな構造”を持ち、必要なバリデーション処理が全て行われていること。
これが、セキュリティ問題を”ただのバグ”としても修正しても構わない条件となります。
この条件を満たしていない場合、遅すぎるセキュリティ対策(=フェイルセーフ対策)に頼る構造的に脆弱なアプリケーションのままになってしまいます。
OWASP TOP 10 A10:2017は攻撃者やセキュリティ検査ツールからの攻撃を検出/記録/対応できないアプリケーションは脆弱なアプリケーションである、としています。適切なデータバリデーションを構造的に適した形で行っていないと、体系的な対策は難しいです。
出力時のセキュリティ問題を”ただのバグ”として修正しても不十分なアプリケーションが大半です。構造的な問題はさっさと直してしまうのが得策です。
PHPの場合、入力処理/ロジック処理/出力処理、全てに使えるバリデーションフレームワークを公開しています。セキュアなアプリケーション構造にすることはアドホック(=アプリケーション機能の構造化と無関係)に行えます。
アプリケーション構造をセキュアにするのは、体質改善と同じような物です。体質を改善すると、セキュアな状態を維持することが容易になります。