体系的に学ぶ 安全なWebアプリケーションの作り方
- 37.8 時間
- Web アプリケーションの脆弱性とその対策について、基本的なものを一通り学べた
- 実習環境で脆弱性サンプルを動作させながら学べるのがよかった
- 若干飽きるところもあったけど…
- 悪用の例
- 個人情報などの秘密情報を勝手に閲覧する
- Web サイトの内容を書き換える
- サイトを閲覧した利用者の PC をウイルスに感染させる
- 別の利用者になりすまし、秘密情報の閲覧、投稿、買い物、送金などを行う
- Web サイトのコンピュータ資源を勝手に使われる(暗号通貨のマイニングなど)
- Web サイトを利用不能にする
- オンラインゲームなどで無敵になることができる、アイテムを好きなだけとれる
- 自分の個人情報を確認したら、他人の個人情報が見えてしまう
- 経済的損失
- 法的な要求
- 利用者が回復不可能なダメージを受ける場合が多い
- Web サイト利用者に嘘をつくことになる
- ボットネットワーク構築に荷担する
- 脆弱性の発生原因は以下の 2 種類に分類できる
- バグによるもの
- チェック機能の不足によるもの
- アプリケーションのセキュリティを確保するためには、バグをなくすだけでは不十分な場合がある
- HTTPS で暗号化していない状態はバグではないが、盗聴される可能性がある
- 積極的に安全性を強化する機能のことを本書では「セキュリティ機能」と呼ぶ
- wasbook をインストールしたが、起動時に「VirtualBox VM quit unexpectedly.」のメッセージでエラーが出た
- Settings > Audio にいって "Enable Audio" のチェックを外したら起動できるようになった
- なぜ HTTP を学ぶのか
- Web の特性に由来する脆弱性を理解するためには、HTTP やセッション管理についての理解が不可欠
- GET と POST の使い分け
- GET
- GET メソッドは参照(リソースの取得)のみに用いる
- GET メソッドは副作用がないことが期待される
- 秘密情報の送信には POST メソッドを用いること
- 秘密情報を POST で送信すべきという理由は、GET の場合は以下の可能性があるため
- URL 上に指定されたパラメータが Referer 経由で外部に漏洩する
- URL 上に指定されたパラメータがアクセスログに残る
- URL 上のパラメータがブラウザのアドレスバーに表示され他人にのぞかれる
- パラメータつきの URL を利用者がソーシャルネットワークなどで共有してしまう
- 以下が 1 つでも当てはまる場合に POST メソッドを用い、1 つも当てはまらない場合には GET メソッドを利用するとよい
- データ更新など副作用を伴うリクエストの場合
- 秘密情報を送信する場合
- 送信するデータの総量が多い場合
- ステートレスな HTTP 認証
- クッキーとセッション管理
- HTTP とういプロトコルはステートレスなもので、サーバー側では状態を保持しない
- しかし、アプリケーションでは状態を保持したい要求がたびたびある
- HTTP 認証を使えば、ブラウザ側で ID とパスワードを記憶してくれるが、HTTP 認証を使わない場合は、サーバー側で認証状態を覚えておく必要がある
- これらアプリケーションの状態を覚えておくことをセッション管理という
- このセッション管理を HTTP で実現する目的でクッキー(cookie) という仕組みが導入された
- クッキーによるセッション管理
- クッキーは少量のデータをブラウザ側で覚えておけるものだが、アプリケーションデータを保持する目的でクッキーそのものに値を入れることはあまり行われない
- 理由
- クッキーが保持できる値の個数や文字列長には制限がある
- クッキーの値は利用者本人には参照・変更できるので、秘密情報の格納には向かない
- セッション ID に求められる要件
- 第三者がセッション ID を推測できないこと
- 第三者からセッション ID を強制されないこと
- 認証後にセッション ID を変更すればよい
- 第三者にセッション ID が漏洩しないこと
- セッション ID をネットワーク盗聴から保護するには TLS(Transport Layer Security) による暗号化が有効
- クッキーの属性
- Domain 属性
- ブラウザがクッキー値を送信するサーバーのドメイン
- Domain 属性を指定しない場合、クッキーを生成したサーバーにのみクッキーが送られる
- Domain 属性を指定しない状態が最もクッキーの送信範囲が狭く、安全な状態といえる
- Domain を不用意に設定すると脆弱性の原因になる
- 例えば example.com がレンタルサーバー事業者であり、foo.example.com と bar.example.com がレンタルサーバー上で運営されている Web サイトであるとする。foo.example.com サイトが発行するクッキーに Domain = example.com と指定してしまうと、このクッキーは bar.example.com にも漏洩してしまう
- Domain 属性は通常は設定しない
- Secure 属性
- Secure という属性をつけたクッキーは、HTTPS 通信の場合のみサーバーに送信される
- 一方、Secure 属性のついていないクッキーは、HTTPS 通信かどうかに関係なく、常にサーバーに送信される
- クッキーの Secure 属性は、クッキーの HTTPS 送信を保証する目的で指定される
- HttpOnly 属性
- HttpOnly 属性は、JavaScript からアクセスできないクッキーを設定する
- クッキーとして格納されたセッション ID を盗み出す攻撃の典型例は、クロスサイト・スクリプティング攻撃により JavaScript を悪用してクッキーを盗み出すというもの
- クッキーに HttpOnly 属性をつけておくと、JavaScript によりクッキーを盗み出すことができなくなる
- HttpOnly 属性をつけることによる悪影響は通常ないので、セッション ID には HttpOnly 属性をつけるようにするといい
- SameSite 属性
- 最近のブラウザには、主にクロスサイド・リクエストフォージェリ(CSRF)脆弱性対策を目的として、SameSite 属性が機能として追加されている
- SameSite 属性は値として Strict、Lax、None のいずれかを指定できる
- Strict: 他のドメインへのリクエストを送る場合、Strict が指定されたクッキーはセットされない
- Lax: top-level navigation でかつ GET メソッドであれば、他のドメインへのリクエストであってもクッキーをセットする
- None: 従来どおりの動作(クッキーを送る)
- 能動的攻撃(active attack)
- 能動的攻撃とは、攻撃者が Web サーバーに対して直接攻撃すること
- 能動的攻撃の代表例として、SQL インジェクション攻撃がある
- 受動的攻撃(passive attack)
- 受動的攻撃とは、攻撃者がサーバーを直接攻撃するのではなく、Web サイトの利用者に罠を仕掛けることにより、罠を閲覧したユーザを通してアプリケーションを攻撃する手法
- 単純な受動的攻撃
- 罠サイトに利用者を誘導する
- 典型例として、いわゆる「怪しいサイト」を閲覧してマルウェア(ウイルスなどの不正プログラム)に感染させる
- 正規サイトを悪用する受動的攻撃
- 攻撃の手順
- あらかじめ正規サイトを攻撃してコンテンツに仕掛けを仕込む
- 正規サイトの利用者が仕掛けを含むコンテンツを閲覧すると、マルウェア感染などが起こる
- 攻撃する側のメリット
- 罠サイトに誘導する手間がいらない
- 正規サイトは利用者が多いので被害が拡大する可能性が高い
- 正規サイトの機能を不正利用することにより攻撃者にメリットが得られる
- 利用者の個人情報を盗むことにより攻撃者にメリットが得られる
- 正規サイトに罠を仕込む手法。よくある 4 種類
- FTP などのパスワードを不正入手してコンテンツを書き換える(8.1 節参照)
- Web サーバーの脆弱性をついた攻撃によりコンテンツを書き換える(8.1 節参照)
- SQL インジェクション攻撃によりコンテンツを書き換える(4.4 節参照)
- SNS など利用者が投稿できるサイト機能のクロスサイト・スクリプティング脆弱性を悪用する(4.3 節参照)
- サイトをまたがった受動的攻撃
- 手順
- 利用者が罠サイトを閲覧する
- 罠サイトから、仕掛けを含む HTML をダウンロードする
- HTML の仕掛けが発動して、正規サイトに攻撃のリクエストを送信する
- 正規サイトから JavaScript などの仕掛けを含むレスポンスが返る
- ブラウザはどのように受動的攻撃を防ぐか
- ここまで説明した受動的攻撃に対しては、ブラウザと Web サイトそれぞれで対 策を行う必要がある
- 本書の 4 章以降では Web サイト側について説明するが、それはブラウザのセキュリティに問題がないことを前提としている
- サンドボックスという考え方
- ブラウザ上では JavaScript や Java アプレットなどサイトを閲覧した状態でプログラムを実行する機能が提供されている
- 利用者のブラウザ上で悪意のあるプログラマが動かないように、JavaScript などは安全性を高めるための機能を提供している
- 基本的な考え方 2 種類
- 利用者に配布元を確認させた上で、利用者が許可した場合のみ実行する
- プログラムの「できること」を制限するサンドボックスという環境を用意する
- 前者は ActiveX コントロールや署名付きアプレットで採用されている考え方だが、一般的なアプリケーションを用いるには利用者の負担が大きく、セキュリティ上の問題が出やすいため、提供元からのサポートが終了しつつある
- サンドボックスは JavaScript や Java アプレット、Adobe Flash Player などで採用されている考え方
- JavaScript のサンドボックスでは以下のように機能が制限される
- ローカルファイルへのアクセス禁止
- プリンタなどの資源の利用禁止(画面表示は可能)
- ネットワークアクセスの制限(同一オリジンポリシー)
- 同一オリジンポリシー
- JavaScript などのクライアントスクリプトからサイトをまたがったアクセスを禁止するセキュリティ上の制限であり、ブラウザのサンドボックスに用意された制限の 1 つ
- JavaScript による iframe アクセスの実験
- ホストが同一であれば、iframe の外側から、iframe の内側の HTML の内容を JavaScript により参照できる
- 同一オリジンである条件
- 以下のすべてを満たす場合
- URL のホストが一致している
- スキーム(プロトコル)が一致している
- ポート番号が一致している
- 同一オリジンポリシーによる保護対象は iframe 内のドキュメントだけでなく、例えば Ajax の実現に使用される XMLHttpRequest オブジェクトでアクセスできる URL にも同一オリジンポリシーの制約がある
- ただし、XMLHttpRequest については、相手側の許可があれば同一オリジンでなくても通信できる CORS という規格が策定された
- アプリケーション脆弱性と受動的攻撃
- ブラウザは同一オリジンポリシーにより受動的攻撃を防止しているが、アプリケーションに脆弱性があると、受動的攻撃を受ける場合がある
- JavaScript 以外のクロスドメインアクセス
- JavaScript 以外のブラウザ機能で、クロスドメインのアクセスが許可されているものについて説明する
- frame 要素と iframe 要素
- クロスドメインのアクセスができるが、JavaScript によってクロスドメインのドキュメントにアクセスすることは禁止されている
- img 要素
- img 要素の src 属性はクロスドメインの指定が可能
- script 要素
- CSS
- form 要素の action 属性
- CORS は従来の同一オリジンポリシーに依存するアプリケーションとの互換性を保ちながら、異なるオリジンとのデータ交換を可能にする
- シンプルなリクエスト
- Access-Control-Allow-Origin
- Access-Control-Allow-Origin とは、クロスオリジンからの読み出しを許可するための仕掛けで、情報の提供元が HTTP レスポンスヘッダとして出力する
- シンプルなリクエストの要件
- HTML フォームから送られるリクエストを基準として、HTML フォームの場合に比べて過度にリスクが増加しない範囲 で条件が選択されている
- 以下の条件をすべて満たすもの
- メソッドは下記のうちのいずれか
- GET
- HEAD
- HTTP の HEAD メソッドは、指定されたリソースを HTTP GET メソッドでリクエストした時に返されるヘッダーをリクエストする
- POST
- XMLHttpRequest オブジェクトの setRequestHeader メソッドで設定するリクエストヘッダは以下に限る
- Accept
- Accept-Language
- Content-Language
- Content-Type
- Content-Type ヘッダは以下のいずれかであること
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
- プリフライトリクエスト
- クロスオリジンアクセスにおいて「シンプルなリクエスト」の条件を満たさない場合、ブラウザはプリフライトリクエスト(pre-flight request)という HTTP リクエストを送信する
- 認証情報を含むリクエスト
- デフォルトでは、クロスオリジンに対するリクエストには HTTP 認証やクッキーなどの認証に用いられるリクエストヘッダは自動的に送信されない
- クッキーなど認証用のヘッダを伴うクロスオリジンリクエストは、下記の両方を満たす必要がある
- XMLHttpRequest オブジェクトの withCredentials プロパティを true にする
- レスポンスヘッダとして Access-Control-Allow-Credentials: true を返す
- 脆弱性はどこで発生するのか
- 脆弱性の例
- HTML の出力(クロスサイト・スクリプティング)
- HTTP ヘッダの出力(HTTP ヘッダ・インジェクション)
- SQL 文の呼び出し(発行)(SQL インジェクション)
- シェルコマンドの呼び出し(OS コマンド・インジェクション)
- メールヘッダおよび本文の出力(メールヘッダ・インジェクション)
- Web アプリケーションの機能と脆弱性の対応
- 脆弱性には処理に起因するものと出力に起因するものがある
- 入力に起因する脆弱性はない
- 出力に起因する脆弱性には「インジェクション」という単語がつくものが多い
- インジェクション系脆弱性とは
- データの中に引用符やデリミタなど「データの終端」を示すマークを混入させて、その後の文字列の構造を変化させる
- Web アプリケーションの「入力」では何をするか
- 入力処理では入力値に対して以下の処理を行う
- a: 文字エンコーディングの妥当性検証
- b: 文字エンコーディングの変換(必要な場合のみ)
- c: 入力値(パラメータ文字列)の妥当性検証
- 文字エンコーディングの検証
- PHP では mb_check_encoding 関数が利用できる
- 文字エンコーディングの変換
- 文字エンコーディングの変換手段は言語によって異なる
- 入力値の検証
- 目的
- 入力値検証がないと以下のような現象が起こってしまう
- 数値のみを受け付ける項目に英字や記号を入力して、データベースのエラーになる
- 更新処理が途中でエラーになり、データベースの不整合が発生する
- 利用者が多数の項目を入力して実行ボタンをクリックしたら内部エラーとなり、入力を最初からやり直すはめになる
- メールアドレスの入力を忘れているのにアプリケーションがメール送信処理を実行する
- 目的は以下
- 入力値の間違いを早期に発見して再入力を促すことにより、ユーザビリティを向上する