# 04. Web Application Security ________________________________________ 安全なウェブサイトの作り方 改訂第7版 https://www.ipa.go.jp/security/vuln/websecurity.html Webアプリにおける11の脆弱性の常識と対策 https://www.atmarkit.co.jp/ait/articles/0909/01/news158.html 体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 2刷 https://www.sbcr.jp/products/4797393163.html (電子書籍は1刷。未改善正誤表あり) ________________________________________ ## 1. 脆弱性のポイント ________________________________________ ### 1.1. 大前提:リクエストは改ざんできる ```text サーバ側で管理するべき情報をリクエストから取得しない - Httpヘッダ、Referer、Cookie、hidden値、Httpボディのすべては改ざんできる サーバ側で管理するべき情報の例 - 個人情報(アプリケーションセッションで管理されるべき) - EC注文時の商品情報(毎回サーバ内で取得するべき) - 問い合わせページのメール送信先(サーバ側の値を利用すること) - ダウンロードファイルリンク等(別の利用者のファイルにアクセスする危険) - Referer(信用してはならない) ``` ________________________________________ ### 1.2. getパラメータ ```text 1. お作法として、副作用のある操作はGETにしない 2. getパラメータは拡散する。第三者に見られたくない情報を載せない - 具体的には、referer、アクセスログ、ショルダーハック、リンクで漏洩する - 当然、SessionIDをURLに含めてはならない 3. URLはEdgeで2083文字制限があるので、巨大なデータはGETにしない ``` ________________________________________ ### 1.3. basic認証、digest認証 ```text basic認証は利用禁止 base64でエンコードされた平文が送信されている ``` ________________________________________ ### 1.4. CookieとSessionID Cookie ```text サーバ側のCookie応答仕様は取説を読む - secure属性 - cookieの盗聴は防げる(保存されたcookieはhttpsでないと送信されない) - cookieの改変・固定化は防げない - secure属性でもhttpリクエストに対してcookie値は返してしまう - サーバ側がhttpリクエストを拒否する設定でも、そもそも罠経路が任意のサイトに行き先を変えれば、改変されたレスポンスを返却できる - secure属性がないと、ブラウザがhttp送信時に平文送信する - cookie値はhttpsとhttpで共有する。よってsecure属性がない値はhttpのURLを踏ませれば中間経路で盗み見ることが可能 クッキーの不適切な利用を防ぐには、以下の3つが要点 - 利用者に改ざんされて困る値はクッキーにしない - SessionIDの場合は後述の通り - 可能ならばすべてのクッキーにsecure属性を付与 ``` Session ID ```text SessionIDは、Cookieに保存される Cookieの有効期限が未指定の場合、ブラウザの場合はブラウザを閉じるまでCookieが有効となる SessionIDは、以下の要件を満たす必要がある 1. 第三者がSessionIDを推測できないこと(適切な乱数生成とSALTで予測不能にすること) 2. 第三者からSessionIDを強制されないこと(認証成功(ログイン)後に必ず再発行すること) 3. 第三者にSessionIDが漏洩しないこと(urlSessionID禁止、適切なCookie属性、ssl必須) また、Cookieの仕様により、SessionIDは事実上以下である 4. domain属性は設定しない(サブドメイン間でcookieを共有したい場合以外は使用禁止) 5. expire属性は設定しない(ブラウザが終了した時に破棄されるようにする) - 自動ログイン機能を使う場合、これをサーバ側で判断するのが簡単 6. secure属性を有効にする(httpで要求された際にブラウザが保存・再送信しなくなる) 7. サーバ側はhttpを常にhttpsにリダイレクトさせる(secure属性でもレスポンスは返すため) 8. httponly属性を有効にする(jsアクセス不可化) ``` ________________________________________ ### 1.5. CORS ```text corsを許可しすぎないこと corsはセキュリティリスクでもある ``` ________________________________________ ### 1.6. script要素とオリジン ```text script要素のjsはドキュメントのオリジン扱いになる これを応用したハックがjsonp ※ なお、SPAなどXHRを別オリジン間で行う必要がある場合、CORSとプリフライト対応が必須である ``` ________________________________________ ### 1.7. XSS対策 概要 ```text XSSを防ぐには、表示する内容をエスケープする - XSSは別名htmlインジェクション - 入力では完全には防げない。出力で対応する - 可能ならば全てのパラメータ表示箇所でエスケープするのが確実 - コンテントは<&、属性は""で囲み<"&をエスケープ - phpならhtmlspecialchars($string, ENT_QUOTES, $charset) ``` 対策詳細 ```text 1. 入力値検証(保険) 1. 入力文字コードの検証 2. 入力文字コードの変換 3. 入力値の検証 - ヌルバイトチェック - 制御文字チェック - 文字数チェック - 数値に対する最大値・最小値チェック - 可能な限りバイナリセーフな関数を使用 2. 表示処理検証(メイン) - 表示パラメータをエスケープまたは検証する - url表示 :url検証 - イベントハンドラ :jsエスケープかつhtmlエスケープ - スクリプト内リテラル:jsエスケープかつhtmlエスケープ - url以外 :htmlエスケープ - htmlを動的パラメータしたい場合 - 必ずライブラリを使用する ``` htmlエスケープ ```text <>&'"の5文字を文字参照に phpなら、htmlspecialchars($src, ENT_QUOTES, "UTF-8") ``` jsを動的に生成する必要がある場合のエスケープ ```text \, ", ', 改行 を\エスケープし、さらにhtmlエスケープ 動的出力対象がJSONだけで良いなら、phpならjson_encode($array, JSON_HEX_TAG | JSON_HEX_AMP) ``` ________________________________________ ### 1.8. SQLインジェクション対策 ```text SQLインジェクションを防ぐには、プレースホルダを利用する - プレースホルダにしないと、SQLインジェクションが発生してしまう ``` ________________________________________ ### 1.9. CSRF対策 ```text CSRFを防ぐには、CSRFトークンを採用すること - フレームワークのcsrftoken機能を有効にするページを規定する - httpはどの画面からリクエストが送られたかを検証する仕組みがない * ワンタイム値の保存先がsecure & httponlyキャッシュとアプリケーションセッションの2パターンがある ``` ________________________________________ ### 1.10. クリックジャッキング対策 ```text クリックジャッキングを防ぐには、X-Frame-Options: SAMEORIGIN - cssを駆使すると、iframeを透明した上で別のコンテンツを重ねて表示できてしまう ``` ________________________________________ ### 1.11. オープンリダイレクト脆弱性の回避 ```text オープンリダイレクト脆弱性を防ぐには、リダイレクト先のドメインをチェックする - パラメータ改ざんによりフィッシングサイトに誘導される危険がある - 自サイトに飛ぶことを想定している場合は改ざんされていないことを保証する必要がある - 外部サイトに飛ぶことが想定される場合はクッションページを採用する ``` ________________________________________ ### 1.12. Httpヘッダインジェクション対策 ```text Httpヘッダインジェクションを防ぐには、入力パラメータをそのままレスポンスヘッダに使用しない - 攻撃例 - Locationヘッダを2重出力させてリダイレクト先を改ざん - Set-Cookieヘッダを出力させて任意のCookie値を設定。セッション固定攻撃など - 2重改行と偽ボディを出力 ``` ________________________________________ ### 1.13. メールヘッダ・インジェクション対策 ```text メールヘッダ・インジェクションを防ぐには、ライブラリを使いさらに注意する - PHPMailerならSenderに脆弱性があるため外部パラメータを設定しない ``` ________________________________________ ### 1.14. ディレクトリ制御・ファイル制御・コマンド制御 ディレクトリトラバーサル対策 ```text ディレクトリ・トラバーサルを防ぐには、以下のいずれかを実施する - サーバ上のファイル名を直接公開せず指定もさせない - 許可するファイル名を英数字のみにして制御文字やスペースなどを許容しない ``` 意図しないファイル公開 ```text 意図しないファイル公開を防ぐには、公開ディレクトリの仕様を理解する - 公開ディレクトリ(DocumentRoot)がどこかを把握する ``` OSコマンド・インジェクション ```text OSコマンド・インジェクションを防ぐには、シェル実行用の関数を利用しない - php:system()、pctrl_exec() ``` スクリプトファイル・アップロード ```text スクリプトファイル・アップロードを防ぐには、公開ディレクトリをアップロード先にしない - アップロードファイルのアップロード先は非公開ディレクトリが鉄則 ``` ファイルダウンロードXSS ```text ファイルダウンロードXSSを防ぐには、以下の2つを必須対処する - Content-Typeの適切な指定 - X-Content-Type-Options:nosniffを付与 ``` ________________________________________ ### 1.15. その他 PDF FormCalc問題対策 ```text PDF FormCalc問題を防ぐには、PDFをブラウザで直接閲覧させない - 主にEdgeで発生 ``` ファイルインクルード攻撃対策 ```text ファイルインクルード攻撃を防ぐには、外部パラメータに基づいた動的インクルードをしない ``` evalインジェクション対策 ```text evalインジェクションを防ぐには、eval相当機能を使わない - evalは何かと問題になる ``` 安全でないデシリアライゼーション ```text 言語独自のシリアライズは使わず、外部パラメータはJSONやXMLからDTOへの変換を徹底する ``` XXE ```text XMLでやり取りをする場合、外部実体参照機能をオフにする ``` その他 ```text - Javaサーブレットフィールド事故 - キャッシュ事故 ``` Web API設計 ```text Jsonエスケープの不備 対策:適切なライブラリでJSONを生成すること Json直接閲覧XSS 対策:XHRレスポンスのContent-Typeを「application/json; charset=utf-8」にすること 古いブラウザに対処するため、htmlエスケープ対象文字をUnicodeエスケープすること JsonpコールバックXSS 対策:レスポンスのContent-Typeを「text/javascript; charset=utf8」にすること Web API CSRF 対策:CSRFトークンが可能ならばそれ Jsonハイジャック 対策:X-Content-Type-Options:nosniffを常に付与 Jsonpの不適切な利用 対策:Jsonpの利用をやめる。可能な限り別の方法を模索する CORSの検証不備 対策:API提供時、不適切なCORSを設定しない ``` ________________________________________ ## 2. セキュリティに関わる実装 ________________________________________ ### 2.1. 認証 ログイン ```text 仕組み 1. ID/PWDを受け取る 2. PWDはソルトを付与してからハッシュ化する(passowrd_hash()など)。最近ではストレッチに対応したものを使う 3. DBと照合して一致するかどうか調べる UI 1. サインアップとログインを分けるか、ID入力後に判断する 2. 同一画面の場合 1. IDのみを入力する状態0 > User存在確認判断 2. loginモードの状態1、パスワード初期設定の状態2のどちらかに移行 3. 状態1からならログインAPI、状態2ならパスワードリセットAPI呼び出し>ログインとする 要件 1. POSTリクエストとする 2. SQLインジェクション対策されていること(前述参考の事) 3. SessionIDが安全であること(前述参考の事) 4. リダイレクト機能も連動する場合、オープンリダイレクト脆弱性が対処されていること 5. リダイレクト機能も連動する場合、httpヘッダーインジェクション対策されていること 6. 辞書攻撃対策・joeアカウント対策されていること - (以下のようなパスワードの禁止) - password - qwer - asdf - zxcv - 1234 - ユーザー名と同一 7. ショルダーハックの防止 - (入力画面のパスワードはマスクすること) 8. ブルートフォース対策されていること - 試行回数制限10回 - ロック時にメール送信 - 成功時/30分後にはリセット 9. ログインエラーのメッセージは詳しくしないこと - (ID、パスワードのどちらが間違っているのか報告しないこと) 10. 必要に応じて2段階認証を実装すること 11. 必要に応じて10分間当たりのログイン失敗率を監視すること 12. パスワードはSALT付与しPBKDF2などでハッシュ化して保存すること ``` ハッシュ関数補足 ```text 現在では利用禁止 SHA1 MD5 余り推奨されない SHA256 推奨 bcrypt PBKDF2 Argon2 ``` 自動ログイン ```text SessionIDのexpire属性を設定するのが早い ``` ログアウト ```text 1. POSTリクエストとする 2. アプリケーションセッション(SessionID)を破棄すること ``` ________________________________________ ### 2.2 アカウント管理 アカウント登録の例 ```text 1. ワンタイムパスワードをメール送信 2. ユーザは画面にそれを入力 ``` アカウントの考慮事項 ```text - 重複の防止 - 自動登録クラッキングの防止(CAPTCHAなどの利用) - パスワード変更UIの用意 - メールアドレス変更UIの用意 - 管理者用パスワードリセットUIの用意 - 利用者用パスワードリセットUIの用意 - 管理者用アカウント停止機能の用意 - 利用者用アカウント停止機能の用意 ``` ________________________________________ ### 2.3. 認可 - 表示権限、操作権限を制御する仕組みを作る事 - 認可情報は、アプリケーションセッション内に保持し、クライアントに持たせないこと - SPAの場合、ページ遷移ごとに再チェックすること ________________________________________ ### 2.4. ログ出力 典型的なロギングを行うこと ________________________________________ ### 2.5. 文字コード あらゆる文字コードは明示的に指定しておくこと PHPの場合 1. ソースコードをutf8で保存する 2. httpリクエストのパラメータは、全てチェック(PHPは入力チェックがないため) - mb_check_encoding($var, $encoding) 3. php.ini - default_charset = UTF-8 - 上記で統一するため、以下は設定してはいけない - mbstring.internal_encoding - mbstring.http_input - mbstring.http_output 3. htmlspecialchars($string, ENT_QUOTES, $charset) - $charsetを明示する(default_charsetが使用されるが、念のため) 4. その他、DB、メール送信等でも明示的に指定する ________________________________________ ## 3. 各種補足 ________________________________________ ### 3.1. SSL & TLSの詳細 要約:現在SSLと呼ばれているものは、実際にはTLSである ```text 1995 : SSL3.0まで改定されたが、脆弱性があった 1999 : TLS1.0。現在まで続く主流 2006 : TLS1.1。脆弱性改定 2008 : TLS1.2。脆弱性改定 2017 : TLS1.3。脆弱性改定 ```