安全なWebアプリケーションの作り方_脆弱性が生まれる原理と対策の実践_第2版
理解したこと
書籍情報
-
著者
- 徳丸浩:EGセキュアソリューションズ株式会社代表、OWASP Japanアドバイザリーボード、独占行政法人情報処理推進機構(IPA)非常勤研究員
-
定価
- 3,520円
-
発売日
- 2018/06/22
-
ISBN
- 978-4-7973-9316-3
Webアプリ開発者が知っておくべき攻撃と防御の知識を徹底解説。脆弱性が生まれる原理と具体的な対処法が学べる。実習用仮想マシンのダウンロードサービス付き。OWASP Top10−2017対応。【「TRC MARC」の商品解説】
日本中の現場で支持されたベストセラーが、最新環境にあわせて全面刷新+大増ページ!
Webアプリケーションにはなぜ脆弱性が生まれるのか? 脆弱性を解消するにはどうプログラミングすればよいか? PHPサンプルへの攻撃を通して脆弱性が生まれる原理と具体的な対処方法が学べる!
Webアプリ開発者の必読書、待望の改訂版! OWASP Top 10 - 2017対応
<主な改訂内容> ・HTML5の普及に対応してWeb APIやJavaScriptに関する解説を新設 ・OWASP Top 10 - 2017に対応して、XXEや安全でないデシリアライゼーションなどを解説 ・脆弱性診断に対する関心が高まっていることから、脆弱性診断の入門の章を親設 ・IE7のサポート終了など現在のソフトウェアの状況に対応 ・実習環境をWindowsに加えてMacにも対応【商品解説】
1章 Webアプリケーションの脆弱性とは
1.1 脆弱性とは、「悪用できるバグ」
脆弱性(vulnerability)とは:バグの中でも悪用できてしまうもの
1.2 脆弱性があるとなぜ駄目なのか
- 経済的損失
- 法的な要求
- 利用者が回復不可能なダメージを受ける場合が多い
- Webサイト利用者に嘘をつくことになる
- 攻撃インフラ(ボットネットワークなど)構築に荷担してしまう
1.3 脆弱性が生まれる理由
- 理由
- (A)バグによるもの:XSS
- (B)チェック機能の不足によるもの:ディレクトリトラバーサルなど
1.4 セキュリティバグとセキュリティ機能
1.5 本書の構成
1.6 セキュリティガイドラインとの対応
- ガイドライン2種類
- 安全なウェブサイトの作り方
- OWASP Top 10
本書の項目との対応表の説明
2章 実習環境のセットアップ
2.1 実習環境の概要
-
サンプルを動かす環境
- Linux
- nginx 1.10
- Apache 2.4
- PHP 5.3 / PHP 7.0
- Tomcat 8.5
- MariaDB 10.1
- Postfix 3.1
-
インストールするプログラム
- Firefox
- VirtualBox
- 仮想マシン
- OWASP ZAP(脆弱性診断に用いるツール)
- FoxyProxy-Standard(プロキシを切り替えるFirefoxアドオン)
-
実習用仮想マシンのダウンロード
2.2 Firefoxのインストール
- XSSフィルタが標準では実装されていないのでクロスサイトスクリプティングという脆弱性の体験がやりやすい
- ダウンロードURL
2.3 VirtualBoxのインストール
- ダウンロードURL
2.4 仮想マシンのインストールと動作確認
wasbookのOSイメージをインポート ホストネットワークマネージャを設定 ゲストOSの設定>ネットワークより以下を確認 アダプター1にNATが設定されている アダプター2にホストオンリーアダプター、名前に先程ホストネットワークマネージャより設定した名前が選択されていること
ゲストOSを起動してログイン
ip aでアドレス確認
ホストOSからping疎通
2.5 OWASP ZAPのインストール
-
OWASP ZAPとは
- OWASP ZAP(OWASP Zed Attack Proxy)は、OWASP(The Open Web Application Security Project)が開発・公開しているWebアプリケーション脆弱性診断用ツールで、無償で公開されている
- OWASP ZAPはWindowsPC/Mac上でプロキシとして動作し、HTTP通信を観察したり、変更したりすることができる
- 同種のツールに、Burp SuiteやFiddlerなどがあるが、本書では以下の理由でこちらを使う
- 完全に無償で利用できる
- 自動の診断ツールが手軽に利用できる
- WindowsとmacOSの両方で利用できる
-
JREのインストール
- OWASP ZAPはJavaで記述されているので実行環境が必要
- ただしmacOS版のOWASP ZAPはJRE版が同梱されているのでJREをインストールする必要はありません
- Windowsは必要があればJREをインストール
java -versionで確認
-
OWASP ZAPのインストール
- https://owasp.org/www-project-zap/
- JRE実行環境とbit数をあわせること
オプションの設定を行っておく
2.6 Firefoxの拡張FoxyProxy-Standardのインストール
- ForxProxy Satandard
アドオン内のプロキシ設定を修正
2.7 OWASP ZAPを使ってみる
実際にFireFoxからアクセスして記録されるかどうかを確認する
2.8 Webメールの確認
本書では、メール処理に伴う脆弱性でメールを送信したり、 脆弱性に対する攻撃でメールを送信したりする場合があるので、ブラウザからメールを確認できるように、RoundcubeというWebメールソフトが導入されている
実際にログインして確かめましょう
3章 Webセキュリティの基礎 ~ HTTP、セッション管理、同一オリジンポリシー
3.1 HTTPとセッション管理
-
なぜHTTPを学ぶのか
- Web固有の特性に由来して、脆弱性が発生するため
-
一番かんたんなHTTP
-
Referer
- セキュリティ上の問題になるのは、URLが秘密情報を含んでいる場合
- 典型的にはURLにセッションIDを含んでいる場合、Referer経由で外部に漏洩し、なりすましに悪用される可能性がある
-
hiddenパラメータの書き換えを体験
- hiddenパラメータ部分に脆弱性がある場合に、OWASP ZAPのようなプロキシツールを用いてhiddenパラメータを改変し、攻撃することが可能であることを実感してもらうため
-
hiddenパラメータのメリット
- hiddenは利用者自身からは書き換えできるものの、情報漏洩や第三者からの書き換えに対しては堅牢だということ
- hiddenパラメ―tあと比較する対象には、後述するクッキーやセッション変数がある
- 特にログイン前の状態でかつ、地域型JPドメイン名や都道府県型JPドメイン名を使っている場合、後述するクッキーモンスターバグの影響により、セッション変数の漏洩に対する効果的な対策がない
- このため利用者自身によっても書換えられては困る認証や認可に関する情報はセッション変数に保存するべきだが、それ以外の情報はまずはhiddenパラメータに保存することを検討するとよい
- 特にログイン前の状態では認証認可に関する情報はないはずなので、原則としてセッション変数の使用は避け、hiddenパラメータを使うことが情報漏えいなどに対して安全
-
ステートレスなHTTP認証
- HTTPには認証機能がサポートされている
- HTTP認証と総称されるが
- Basic認証
- NTLM認証
- Digest認証
- などが存在する
- HTTPがステートレスなプロトコルであることから、HTTP認証もステートレス
-
Basic認証を体験する
- Basic認証は認証が必要なページにリクエストがあると、いったん「401 Unauthorized(認証が必要なのにされていない)」というステータスを返します。これを受けてブラウザはIDとパスワードの入力画面を表示し、入力されたIDとパスワードを追加したリクエストをあらためてサーバーに返信します。
-
認証と認可
- 認証とは:利用者がたしかに本人であることをなんらかの手段で確認すること
- Basic認証のほか、HTMLフォームでIDとパスワードを入力させるフォーム認証、TLSクライアント証明書を用いるクライアント認証など
- 認可とは:認証済みの利用者に権限を与えること
- 具体的には、データの参照・更新・削除や、預金の振り込み、物品の購入などを「できるようにする」こと
- 認証とは:利用者がたしかに本人であることをなんらかの手段で確認すること
-
クッキーとセッション管理
- クッキー:サーバー側からブラウザに対して、「名前=変数」の組を覚えておくように指示するもの
- クッキーはセッション管理という機能の実現に使われる
- クッキー:サーバー側からブラウザに対して、「名前=変数」の組を覚えておくように指示するもの
-
アプリケーションが
Set-Cookieというレスポンスヘッダーを返却して、ブラウザに対してクッキーを覚えるように指示する -
クッキーには有効期限が設定できるが、有効期限が設定されていないクッキーはブラウザ終了時まで有効になる
-
クッキーによるセッション管理
- アプリケーションデータを保持する目的でクッキーそのものに値を入れることはあまり行われない
- その理由は以下の通り
- クッキーが保持できる値の個数や文字列長には制限がある
- クッキーの値は利用者本人には参照・変更ができるので、秘密情報の格納に向かない
- このためクッキーには「整理番号」としてセッションIDを格納しておき、実際の値はサーバー側で管理する方法が広く用いられている
- これをクッキーによるセッション管理と呼ぶ
-
セッションIDに求められる要件
- 要件1:第三者がセッションIDを推測できないこと
- 暗号論的擬似乱数生成器を用いて生成https://www.cryptrec.go.jp/list.html
- しかし実際にはセッションIDを自作するのではなく、Webアプリケーション開発ツールで提供されるものを利用すべき
- セッション管理機構は自作しないことが重要
- 要件2:第三者がセッションIDを強制されないこと
- セッションIDの固定化攻撃(Session Fixaction Attack)を受けないように、認証が完了したタイミングでセッションIDを新しく払い出そうという話
- 要件3:第三者にセッションIDが漏洩しないこと
- セッションIDが漏洩する主な原因
- クッキー発行の際の属性に不備がある
- ネットワーク的にセッションIDが盗聴される
- XSSなどアプリケーションの脆弱性により漏洩する
- PHPやブラウザなどプラットフォームの脆弱性により漏洩する
- セッションIDをURLに保持している場合は、Refererヘッダから漏洩する
- セッションIDが漏洩する主な原因
- 要件1:第三者がセッションIDを推測できないこと
-
クッキーの属性
| 属性 | 意味 |
|---|---|
| Domain | ブラウザがクッキー値を送信するサーバーのドメイン |
| Path | ブラウザがクッキー値を送信するURLのディレクトリ |
| Expires | クッキー値の有効期限。指定しない場合、ブラウザの終了まで |
| Secure | HTTPSの場合のみクッキーを送信 |
| HttpOnly | この属性が指定されたクッキーはJavascriptからアクセスできない |
-
このうちセキュリティ上重要な属性
- Domain
- Secure
- HttpOnly
-
Domain属性:通常設定しない
- 複数のサーバに送信されるクッキーを生成したい場合に使うが、Domain属性を指定しない状態がもっともクッキーの送信範囲が狭く、安全な状態と言える
-
Column:クッキーモンスターバグ
- 筆者が所属する会社のドメイン名は
eg-secure.co.jpなので、クッキーを発行する際のドメイン指定の最短はeg-secure.co.jpとなるはず - ところが、古いブラウザを使っていると
.co.jpのドメインのクッキーが作れてしまうという問題があった - このバグを「クッキーモンスターバグ」という
- 筆者が所属する会社のドメイン名は
-
クッキーのセキュア属性
- HTTPS通信の場合のみにサーバーに送信されるので、HTTPS通信を保証することができる
-
クッキーのHttpOnly属性
- Javascriptからアクセスできないクッキーを設定するもの
- XSS攻撃でJavascriptを悪用してクッキーを盗み出すというもので、クッキーにHttpOnly属性をつけておくとJavascriptによりクッキーを盗み出すことができなくなる
- HttpOnly属性をつけることで悪影響はないのでつけておくことをおすすめ
3.2 受動的攻撃と同一オリジンポリシー
-
能動的攻撃と受動的攻撃
- 能動的攻撃:攻撃者がWebサーバーに対して直接攻撃すること
- 受動的攻撃:攻撃者がサーバーに直接攻撃するのではなく、Webサイトの利用者に罠を仕掛ける事により、罠を閲覧したユーザを通してアプリケーションを攻撃する手法
- 罠サイト経由でCSRFやXSSの攻撃を仕掛ける
-
ブラウザはどのように受動的攻撃を防ぐか
- 受動的攻撃に対しては、ブラウザとWebサイトそれぞれで対策を行う必要がある
- Webサイト側の対策を説明する前に、この節でブラウザのセキュリティ機能について説明する
-
サンドボックスという考え方
- 利用者に配布元を確認させた上で、利用者が許可した場合のみに実行する
- プログラムの「できること」を制限するサンドボックスという環境を用意する
- ローカルファイルへのアクセス禁止
- プリンタなどの資源の利用禁止(画面表示は可能)
- ネットワークアクセスの制限(同一オリジンポリシー)
-
同一オリジンポリシー
- JavaScriptなどのクライアントスクリプトからサイトをまたがったアクセスを禁止するセキュリティ上の制限であり、ブラウザのサンドボックスに用意された制限の1つ
- iframeを題材にして、同一オリジンポリシーがなぜ必要かを説明していきます
-
同一オリジンである条件
- 「同一オリジン」であるとは、以下のすべてを満たす場合
- URLのホスト(FQDN
Qualified Domain Name)が一致している - スキーム(プロトコル)が一致している
- ポート番号が一致している
- URLのホスト(FQDN
- クッキーに対する条件はスキームとポート番号は関係ないのでJavaScriptの制限のほうが厳しい
- 一方JavaScriptにはディレクトリに関する制限はない
- iframeだけでなく、Ajaxの実現に使用されているXMLHttpRequestについても成約がある
- ただし、XMLHttpRequestについては相手側の許可があれば同一オリジンでなくても通信できるCORSという規格が策定されました
- 「同一オリジン」であるとは、以下のすべてを満たす場合
-
アプリケーション脆弱性と受動的攻撃
- ブラウザは同一オリジンポリシーにより受動的攻撃を防止しているが、アプリケーションに脆弱性があると受動的攻撃を受ける場合がある
- その代表がクロスサイトスクリプティング(XSS)攻撃
- iframeの外側から内側の要素へアクセスすることはできないが、何らかの方法で内側の要素にスクリプトを埋め込むことができれば、同一オリジンポリシーの制約は受けないのでドキュメント情報にアクセスできる
-
JavaScript以外のクロスドメインアクセス
- frame要素とiframe要素
- img要素
- script要素
- CSS
- クロスドメインで読み込み可能
- IEにはCSSXSSと呼ばれる脆弱性があった
- HTMLやJavaScriptをCSSとして呼び出した場合、これらCSSでないデータが部分的に読み出せるジェイ弱製があった
- form要素のaction属性
- form要素のaction属性もクロスドメインの指定が可能
- またformの送信(submit)はJavaScriptから常に(action先がクロスドメインであっても)操作できる
- この仕様を悪用した攻撃手法がクロスサイトリクエストフォージェリ(CSRF)攻撃
- CSRF攻撃では、ユーザの意図しないformを送信させられ、アプリケーションの機能が勝手に実行される
3.3 CORS(Cross-Origin Resource Sharing)
-
シンプルなリクエスト
- 特定の王権を満たす「シンプルなリクエスト」の場合、XMLHttpRequestを用いて異なるオリジンにHTTPリクエストを送ることが相手側の許可なしに可能
-
Access-Control-Allow-Origin
- クロスオリジンからの読み出しを許可するための仕掛けで、情報の提供元がHTTPレスポンスヘッダとして出力
http://example.jpに対してXMLHttpRequestなどのアクセスを許可する場合は下記のHTTPレスポンスヘッダを送信Access-Control-Allow-Origin: http://example.jp- 情報提供元がこの許可をすればJavaScriptから参照できるようになる
-
シンプルなリクエストの要件
- メソッド
- GET
- HEAD
- 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リクエストを送信する
- 上記のシンプルなリクエストの要件に合致しない場合、ブラウザは以下のようなリクエストヘッダを送信
Access-Control-Request-Method: POSTAccess-Control-Request-Headers: content-typeOrigin: http://example.jp
-
MEMO: CORS制約に引っかかる場合でもリソース提供元のソースは走る
- 返却されたリソースを読み込むときに、レスポンスヘッダが存在しないと読み込むことができない様になっている
-
認証情報を含むリクエスト
- デフォルトでは、クロスオリジンに対するリクエストにはHTTP認証やクッキーなどの認証に用いられるリクエストヘッダは自動的に送信されません
- これらを用いるには、XMLHttpRequestのプロパティ
withCredentialsをtrueにセットする必要がある withCredentialsプロパティをtrueにしたリクエストに対しては、Access-Control-Allow-Credentials: trueというレスポンスヘッダを返す必要がある- まとめると
- クライアント側:XMLHttpRequestオブジェクトのwithCredentialsプロパティをtrueにする
- サーバー側:レスポンスヘッダとしてAccess-Control-Allow-Credentials: trueを返す
4章 Webアプリケーションの機能別に見るセキュリティバグ
4.1 Webアプリケーションの機能と脆弱性の対応
-
脆弱性はどこで発生するか
- Webアプリケーションの出力処理で発生
- ここでいう出力は、ブラウザ表示、RDB処理、シェル実行、メール処理、ファイル処理など
- まとめると
- 脆弱性には処理に起因するものと出力に起因するものがある
- 入力に起因する脆弱性はない(ミドルウェアの範囲を広げると、入力時の検証処理に脆弱性が入り込む余地はある)
- 出力に起因する脆弱性には「インジェクション」という単語がつくものが多い
- Webアプリケーションの出力処理で発生
-
インジェクション系脆弱性とは
-
まとめ
- 脆弱性の説明を始めるにあたり、脆弱性の発生箇所と脆弱性の種類の関連性について説明しました
- また「出力」で発生する脆弱性は、インジェクションと呼ばれる共通の原理で発生することを紹介
4.2 入力処理とセキュリティ
-
WEBアプリケーションの「入力」では何をするのか
- 処理に入る前の準備段階
- 文字エンコーディングの妥当性検証
- 文字エンコーディングの交換(必要な場合のみ)
- 入力値(パラメータ文字列)の妥当性検証
- 処理に入る前の準備段階
-
文字エンコーディングの検証
- PHPの場合、文字エンコーディングの検証にはmb_check_encoding関数が利用できる
-
文字エンコーディングの変換
- 言語によって文字エンコーディングの変換手段は異なる
- おおまかな分類として、文字エンコーディングを自動的に変換する言語と、スクリプトで変換ロジックを明示する言語がある
-
入力値の検証
- 入力値の検証がないと…
- 数値のみを受け付ける項目に英数字や記号を入力して、データベースのエラーになる
- 更新処理が途中でエラーになり、データベースの不整合が発生する
- 利用者が多数の項目を入力して事項ボタンをクリックしたら内部エラーとなり入力を最初からやり直す羽目になる
- メールアドレスの入力を忘れているのにアプリケーションがメール送信処理を実行する
- 入力検証はこのような悪いことが起きる可能性をへらす!
- 目的
- 入力値の間違いを早期に発見して再入力を促すことにより、ユーザビリティ(使いやすさ)を向上する
- 間違った処理を継続することによるデータの不整合などを防ぎ、システムの信頼性を向上させる
- 入力値の検証がないと…
-
バイナリセーフという考え方とヌルバイト攻撃
- バイナリセーフとは:入力値がどんなバイト列であっても正しく扱えること
- ヌルバイトは、バイナリセーフでない関数の場合終端文字として扱われるため、後ろの文字列を評価対象としない
-
入力値検証だけでは対策にならない
- 入力段階で不正な入力をチェックしておけば、セキュリティ対策は終わりにできないか?
- アプリの仕様のためすべての文字を許容するという仕様の場合は入力時点でも何も防げない
- 入力段階で不正な入力をチェックしておけば、セキュリティ対策は終わりにできないか?
4.3 表示処理に伴う問題
-
クロスサイトスクリプティング(基本編)
- 外部からの入力値を画面に表示するしてHTMLを生成する処理に問題があると生じる脆弱性
- 影響
- サイト利用者のブラウザ上で、攻撃者の用意したスクリプトの実行によりクッキー値を盗まれ、利用者がなりすましの被害にあう
- 同じくブラウザ上でスクリプトを実行させられ、サイト利用者の権限でWebアプリケーションの機能を悪用される
- Webサイト上に偽の入力フォームが表示され、フィッシングにより利用者が個人情報を盗まれる
-
攻撃手法と影響
- クッキー値の盗み出し
- 罠サイトを用意してiframeで標的サイトを表示し、script実行!
- その他のJavaScriptによる攻撃
- ワーム攻撃
- 画面の書換え
- JavaScriptを使わない攻撃
- formを表示する画面で入力画面と編集画面を兼ねているようなもので、各入力項目の初期値が設定できるようになっているページに脆弱性があると実現する
- MEMO: 最初わからなかったが腑に落ちた!ただこんなページを実装しているサイトは無いと思う。。
- クッキー値の盗み出し
P129まで 2020-08-27 02:33:29
-
反射型XSSと持続型XSS
- 反射型:攻撃用JavaScriptが攻撃対象サイトとは別のサイトに有る場合
- 持続型:攻撃用のJavaScriptが、攻撃対象のデータベースなどに保存される場合
-
COLUMN: TRACEメソッドの無効化とXST
- 古いブラウザに対して飲み有効なクロスサイト・トレーシングという攻撃がある
- JavaScriptによりHTTPのTRACEメソッドを送信する事によりクッキーやBasic認証のID・パスワードを盗み出す手法
- XST攻撃はブラウザで対策が2006年に完了しており、リスクはほぼないと考えられる
-
対策まとめ
- 必須対策(個別の対策)
- HTMLの要素内容
- htmlspecialchars関数によりエスケープ
- 属性値
- htmlspecialcharsカンスによりエスケープしてダブルクォートで囲む
- HTMLの要素内容
- 必須対策(共通対策)
- HTTPレスポンスに文字エンコーディングを明示する
- 保険的対策
- X-XSS-Protectionレスポンスヘッダの使用
- 入力値の検証
- クッキーにHttpOnly属性を付与
- TRACEメソッドの無効化(コラム参照)
- 必須対策(個別の対策)
-
4.3.2 クロスサイトスクリプティング(発展編)
-
href属性やsrc属性のXSS
- 対策
http:またはhttps:で始まる絶対URLチェック- スラッシュで始まる相対URLチェック
- 対策
-
JavaScriptの動的生成
- イベントハンドラのXSS
- Javascriptのメソッド引数にサーバ側で動的に生成したスクリプトが注入されている場合
- 対策
- JavaScript文字列リテラルとしてエスケープする
- 結果をHTMLエスケープする
- 対策
- Javascriptのメソッド引数にサーバ側で動的に生成したスクリプトが注入されている場合
- script要素のXSS
- イベントハンドラのXSS