脆弱性コラム - 第4回

サイト脆弱性をチェックしよう! -- 第4回:開発工程におけるレビューやテストのコツ (その2)

今回も安全なアプリケーションを作るためのチェック方法について説明していきたいと思う。前回から引き続き、各開発工程におけるレビューと適切なテストについてを紹介する。

コードレビュー

まずは、コードレビューでどういったことに注意して確認する必要があるか説明しよう。コードレビューのコツを理解するには、まずアプリケーションの動作について理解する必要がある。従って、最初にアプリケーションの動作について説明する。

多くのアプリケーションでは、何らかの入力(ユーザーにより入力されたデータだけでなく、ファイルや、時刻なども含む)を必要とし、入力されたデータの加工などの処理を行い、結果を出力する。出力された結果は、別のアプリケーション(ブラウザやデータベースなど)で入力データとして利用されることがある。

たとえば、あるECサイト用のアプリケーションの検索処理では、「category」というパラメータがあり、この値が「携帯電話」のとき、携帯電話関連商品の検索結果を返し、値が「PC」のときにはPC関連商品の検索結果を返すといったように、入力データの内容によって開発者が意図したとおりの処理をするといったことができる。

従って、適切な入力をアプリケーションに渡したり、適切なページにアクセスすることで、アプリケーションの処理を利用者が開発者の想定している範囲内で制御することができる。多くの脆弱性は、この入力データに想定外のデータを渡されることによって発生する。

このため、コードレビューでは、まずデータフローに注目する。データフローでは特に以下のことを確認してほしい。
  • 入力データが設計段階で規定したパターンのみで構成されているかを確認する処理が適切に実装されているか?
  • 設計段階で規定したエスケープ処理が出力直前に行われているか?
これらの確認を行うには、入力データと出力データを取り扱っているコードを順番にすべて追跡し、適切な処理がなされているかどうかを確認する。

これらは「第2回:“正しい”ウェブアプリのコーディングとは」で詳しく説明しているので、そちらも参考にしてほしい。

先にも述べているが、多くの脆弱性は入力データの検証と出力時のエスケープ処理を適切に行うことで防ぐことができる。適切にこれらの処理を実装するには、設計の段階で、正しい入力データの条件と、データの出力先アプリケーションの仕様を確認し、明確にしておく必要がある。

正しい入力データの条件と出力データのエスケープ処理内容が明確になっていない場合、設計レベルから検討しなおさなければならない。その他にも以下の点について確認を行うことで、より安全なアプリケーションにすることができる。
  • エラー処理が適切になされているか?
    実行時にデータベースサーバとの通信ができなくなったなどの実行時エラーの処理を実装していない場合、コードの一部などを漏えいしてしまう危険性がある(ファイルの読み書きや、データベースアクセスが常に正常に処理を終了するわけではない)。
    また、すべてのエラーケースを詳細設計時に洗い出すことは、多くの場合、大変困難な作業となる。従って、コーディング時にすべてのエラーケースを洗い出し、コードレビューでそれらに対する処理を適切に行っているか確認する必要がある。
    さらに、ファイルの読み書き、データベースアクセス処理など、実行時にエラーが発生する個所で、処理の失敗を確実に管理しているかどうかも確認する必要があるだろう。
  • ループなどの処理で境界となる条件に間違いはないか?
    発見される脆弱性の幾つかは、ループ処理終了条件や、入力データの境界条件のミスに起因している。特にループ処理の終了条件を間違っていると永久にループを繰り返してしまうことがある。境界条件が正しいか確認する必要がある。
  • 同時に処理が発生した場合にファイルへのアクセスなどに影響が無いか?
    発見される脆弱性には、同時にファイル、データベースへの読み書きなどの処理が要求された場合に発生するものがある。これは、ウェブアプリケーションでは、同時に処理が実行されることは無いという仮定のもとに作成されているためだ。
    しかし、実際には複数の同じ処理が同時に実行されることがある。ファイルへのアクセスは通常OSによりロックがかかるので問題が発生することはまれだが、たとえばECサイトの注文番号などのように、処理ごと(あるいはユーザーごと)に新しい番号を連番で発行している場合には、適切なロック処理を行っていない場合、同じ番号が別の処理(あるいは別のユーザー)に割り振られることがある。
    このように同時に処理が行われるとファイルなどの整合性が取れなくなることが無いかを確認する必要がある。

コーディング規約

これらのチェックをコードのすべてに対して行うのは非常に大変な作業となる。そこで、レビューしやすいように以下のようなコーディング規約を決めることをお勧めする。

  • 安全でない変数: <関数のプレフィックスに「us」を、安全な変数、関数のプレフィックスに「sf」を命名規約で指定する。たとえば、「sfHoge=unHuga」といったようにプレフィックスが異なると脆弱性の可能性があるとすぐに判断できる(間違ったコードは間違って見えるようにするを参照)。
  • 安全なライブラリ:
    フレームワークの機能を使用する。すでに存在しているライブラリや、フレームワークの機能を自作すると、バグが混入する危険性がある。既存の安全なものがあれば、それを利用するようにしてほしい。この結果、コードレビューの手間も減らすことができる。
これら以外にも、言語の仕様上脆弱性となりうることについて対策がとれるようなコーディング規約を決める必要がある。たとえばPHPであれば、使用する変数はすべて初期化するなどが挙げられる。

また、コーディング規約を適切に定めることで、メンテナンス性が向上し、結果的にバグの少ないコードを作成することが可能となる。

コードレビューを行うことで、コードに存在する問題を発見し、早期に対処することが可能。また、コードレビューだけでなくペアプログラミングという2人1組でコードを書くという手法もコーディング中のケアレスミスを防ぐことが可能となるので、脆弱性を防ぐには効果的だ。

テスト

最後にどういった点についてテストをするのか説明する。

脆弱性は意図していない動作をしてセキュリティ上の問題を発生させることなので、脆弱性の確認のためのテストでは、異常系の確認を行うことが主となる。先に述べたように、入力データの内容によってアプリケーションの動作をある程度制御できる。

また、HTTPはステートレスなプロトコルであるため、ウェブアプリケーションではページ単位で処理が行われる。このためページ遷移を無視して、処理を行わせることができる。


アプリケーションに意図しない動作をさせるには、入力データに想定していなかったデータを使用するか、想定していないタイミングでページの呼び出しを行う必要がある。従って脆弱性の確認テストでは入力データに想定していなかったデータを使用するか、想定していないタイミングでページを呼び出す以下のようなチェックを行う。

  • 入力データのテスト
    想定していないデータ種類のテスト
    • 数値項目に英字や記号を入れるなどのテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。メタキャラクタのテスト。
    • 「'」「"」「<」「>」「&」「;」「|」「\」など特別な意味を持つデータを入力するテストを実施。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
    選択肢以外のデータのテスト
    • ラジオボタン、チェックボックス、セレクトリストで指定している選択肢以外のデータを入力するテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。Hiddenフィールド、Cookie書き換えテスト。
    • Hiddenフィールド、Cookieを他の値に書き換えるテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
    非常に大きな桁数のデータのテスト
    • 通常10桁程度しかないパラメータに1000桁のデータなどを入れるテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
    境界値前後のテスト
    • たとえば、0~100の値をとる項目に対し、「-1,199,101」といったデータを入力するテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
    非常に大きな数字、非常に小さな数字のテスト
    • 数値項目に99999999999999999999、-99999999999999999999、0.0000000000000001、-0.0000000000000001といったデータを入力するテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
    パラメータの削除のテスト
    • 正常なリクエスト時に送信されるパラメータを削除するテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
    パラメータ値削除のテスト
    • 正常なリクエスト時に送信されるパラメータの値を削除するテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
  • 呼び出しのテスト
    処理フローを無視した呼び出し
    • たとえば、画面遷移が「ページA」→「ページB」→「ページC」という処理で、ページAにアクセスした直後にページCへアクセスするテストを実施する。
    • 設計どおりの処理(エラー処理など)がされているかを確認する。
    同時呼び出し
    • 異なるユーザーで同時にログインしてみるなど、異なるユーザーで同時に同じ処理を行うテストを実施する。
    • 同時に行った処理が別の処理として行われているかを確認する。

入力データのテストでは、すべてのパラメータ、Cookieをテスト対象とする必要があるが、Java Scriptでエラーチェックを行っていた場合や、Hiddenフィールドやラジオボタンなどはブラウザでは値の変更ができない。このような場合、「Paros」のようなリクエストを書き換えられるツールを使用したり、保存したHTMLファイルを書き換えてテストを実施する。具体的な方法についてはツールのマニュアルなどを参照してほしい。

テスト項目は、ごく普通の異常系のテストだ。しかし、対象となるパラメータが、CookieやHiddenフィールド、ラジオボタン、チェックボックス、セレクトリストが含まれるのが普通の異常系のテストと異なるかもしれない。

残念ながら、多くの開発者はHTTPヘッダ、CookieやHiddenフィールドなどが変更可能であるという認識が少ないように思う。実際、幾つかの検査で、Textフィールドには問題が無くても、Hiddenフィールドやラジオボタンなどのパラメータには脆弱性があるということが多々あった。ブラウザから渡されるものは、HTTPヘッダも含めてすべて変更可能であるという認識でテスト項目を作成し、テストを実施してほしい。

まとめ

今回説明したようなレビューとテストを行うことで、より安全なウェブアプリケーションを構築することができる。今回のまとめは、以下のとおり。


設計レビュー
  • 各機能、データのリスク分析が適切に評価されているか?
  • リスクに対する対策が適切に行われ、明確になっているか?
コードレビュー
  • データフローを確認し、入力時の検証と出力時のエスケープ処理がなされているか?
  • エラー処理が適切に実装されているか?
  • 境界条件が正しいか?
テスト
  • 異常系のテストを十分にする。
  • TextフィールドやTextAreaフィールドだけでなく、Hiddenフィールドなどでも意図しないデータを入力データとしたテストを実施する。


これらは、アプリケーション開発で当たり前にやるべきことだ。しかし、実際の現場ではこれらが行われていないことが多い。実際にこれらすべてのチェックを実施しようとすると、現在の短納期での開発では工数がかかりすぎてしまうため、現実的ではない。

そのような場合でも、少なくとも、異常系のテストを十分に行うことで、存在する脆弱性をリリース前に発見し、修正することで安全なアプリケーションを提供することが可能になる。

今回は、チェックの一般論について説明した。次回以降は、実際の脆弱性のチェックをどのようにするのか具体的に説明していこうと思う。

AppScanに
関するお問い合わせ

  • テクマトリックス株式会社
    東京本社

    ネットワークセキュリティ事業部
    第3営業部
    セキュリティプロダクツ営業2課

    03-4405-7814

メールでのお問い合わせ
watchfire@techmatrix.co.jp

お問い合わせ

製品についてやテクマトリックスについてなど、
こちらよりお気軽にお問い合わせいただけます。