第 6 回の目次 前回: JavaScript その 2 今回: Web アプリにおけるセッション Web アプリの脆弱性 HTTPS 【参考書】 ・徳丸: 安全な Web アプリケーションの作り方 ・高木: 安全な Web アプリ開発の鉄則, Internet Week チュートリ アル 1 / 23 本日のソースコード 今回はなし 2 / 23 Web アプリケーションとセッション Web アプリ: 種々の方法でセッションを作る HTTP: メッセージベース (セッションなし) TCP: セッションあり IP: パケット通信 (セッションなし) Web アプリにおいては, 一つのアプリは状態遷移で構成される 各ページは一つの状態に相当する その一連の状態遷移をセッションと呼ぶ 一般の脆弱性対策に加え, 「セッションに関する脆弱性対策」 が必要 3 / 23 セッションの実装方法 Basic 認証でログイン 毎回ユーザ名とパスワードが (base64 で) 送信される.サーバ側で 紐付ければセッションを作れる (同時アクセス不可). Digest 認証でログイン md5 でハッシュ化して送る.Basic 認証と同様. ハッシュ値を盗まれれば,セッションハイジャックは可能. ハッシュ化のタネ (nonce) を毎回サーバーが変えればかなり安全. hidden パラメタ 次のページに進むためのボタンの中に, <input type="hidden" name="sessionid" value="NNNNN"> と入れておく.POST の中に,sessionid=NNNNN が入る. Cookie 最初にログインした時に,サーバからブラウザへ Set-Cookie: sessionid=NNNNN と送る.以降ブラウザは毎回 sessionid を送ってくる. 4 / 23 Web アプリにおける脆弱性の例 セッションハイジャック XSS (Cross Site Scripting) CSRF (Cross Site Request Forgery) 5 / 23 セッションハイジャック 危険なセッション実装法の例 URL のクエリにユーザ名→最悪 http://..../?user=yamazaki クエリにセッション ID →盗まれたら終わり http://..../?sessionid=NNNNN Referer から漏洩.URL に入れては駄目. セッション ID が固定値,または推測しやすい hidden → POST のボディなので Referer 漏洩は防げる パケット盗聴には対しては同じ弱さ Cookie → POST と同じ Cookie はしょせんヘッダの一部なので,簡単に偽装できる. ユーザ名にハッシュ(MD5) をかけても駄目.MD5っぽいこ とは見ると分かるので,後は盗みたいユーザ名を MD5 にか けるだけでよい. 6 / 23 安全なセッションの実現方法 Cookie はブラウザに残るため盗まれることを前提に設計する Cookie にパスワードをそのまま入れない.例えば,ユーザ 名+パスワードをハッシュして入れる. まあまあの方法: 毎回セッション ID を変更する. セッションからログアウトしてしまえば盗まれても構わない. ログイン中のセッションハイジャックの可能性は残る セッション ID にまともな乱数を使って推測困難にする ただしセッション ID,ユーザ,認可をきちんと紐付ける Set-Cookie: secure cookie 値が https 通信でしか送れないことを強制 中途半端に考えるより,フレームワーク (rails 等) を使った 方が安全 7 / 23 XSS (Cross Site Scripting) 予期せぬスクリプトをブラウザ上で動作させる脆弱性 動的にページを生成するサイトに,そのようなスクリプトを生成 させる その結果: ・Cookie が盗まれる ・嘘の画面が表示される ・JavaScript 用 API を悪用 動的プログラム生成では,常に同種の脆弱性が生じうる → SQL インジェクション (後の回で) 8 / 23 XSS の例 http:/.../?comment=good としてコメントを投稿するサーバがあったとする. サーバ側のコード (JSP): <% String comment=request.getParameter("comment"); %> <div> <%= comment =%> </div> 悪意のある者が次のように投稿: http:/.../?comment=<script src="http:/.../"> 次のようなページが生成される: <div> <script src="http:/.../"> </div> このページを見たら上記の script が実行される. 対策は,外部入力が入り込むルートによる. HTML を動的生成する時にメタ文字が入らないようにする DOM を経由するパターンでは,適切なライブラリを使う 9 / 23 CSRF (Cross Site Request Forgery) 脆弱性のあるサイトにおいて利用者がある操作をしたかのように 見せかける Session Riding とも言う その結果: ・商品購入 ・掲示板への書き込み ・パスワードの変更 サイトが元々提供していないサービス,許していない操作はでき ない 10 / 23 CSRF の例 脆弱性のあるサイト A: 次の GET でコメントを投稿できる http://サイト A/?comment=good cookie で認証をしている 罠のサイト B: あるページに次のようなリンクを入れておく <a href="http:/.../?comment=bad"> → クリックしたら bad というコメントが投稿. JavaScript を使えば POST も可能.onload で自動投稿も可能. セッションがハイジャックされているわけではない Cookie が有効な時間帯を利用しているだけ 11 / 23 CSRF の対策 全体の作り方から考えないと駄目 まず守りたい操作 (大概は POST を含むはず) を決める 次のいずれかを行う POST の hidden にセッション ID や秘密トークンを埋め込む (ただし,hidden だって偽造はできる.) 利用者にパスワードを再入力させる (OK ボタンを表示する程度では駄目) 12 / 23 参考: JSON ハイジャック 悪意あるページを次のようにする. —– JavaScript の定義をいじる.onerror ハンドラとか setter とか. <script src=...>と書く. —– 利用者がそのページを参照する. script タグにより src へアクセス(正しいクッキーが付く) 返事が JSON で返ってくる(JSONP ではなく) JS がパースをする setter がいじってあれば,代入時に値が見える. エラーを起こさせて onerror で見る手もある. 13 / 23 公開鍵暗号の基本 一方向性関数: <f,g>の組.f(g(x))=x. [公開鍵暗号] A 1 公開鍵 g と秘密鍵 f の生成 2 公開鍵 g の公開 B 3 公開鍵 g で文書を暗号化 4 送信 (←) 5 秘密鍵 f で復号化 [電子署名 (身元確認,改竄防止)] A B 1 公開鍵 f と秘密鍵 g の生成 2 公開鍵 f の公開 3 秘密鍵 g で本文を暗号化=署名 4 本文+署名を送信 (→) 5 公開鍵 f で署名を復号化 6 本文と一致か判定 14 / 23 通信内容の秘匿化: HTTPS HTTPS = HTTP over TLS (Transport Layer Security) HTTP -----TLS -----TCP 普通の HTTP identify したサーバとの間の安全なコネクション 普通の TCP 相手から利用者への通信内容 (Web ページ) が改竄されていない (http での GET は改竄の可能性あり.秘密入力の手前から https を) 利用者から相手への通信内容が外部に洩れない 本当に通信したい相手なのかの保証ではない CA は相手の社会的信用度等を評価しているわけではない 利用者がサーバの identification を見るしかない 15 / 23 TLS の特徴 安全な相互認証 安全なネゴシエーション プライベート (他人に見られない) で信頼 (偽造できない) で きるコネクション 汎用的なトランスポートとして使える セッションの再接続を高速化するためのセッション ID の キャッシング 16 / 23 TLS プロトコルの概要 RFC 2246 (v1.0), 4346 (v1.1), 5246 (v1.2) 1 暗号強度の決定 サーバ/クライアント双方で使用できる暗号の強度を決定 2 サーバの身元の確認 クライアントは,サーバからサーバ証明書 (= CA が電子署 名したもの) を受け取り身元を確認する. 3 暗号化通信の準備 クライアントが共有鍵を生成し,サーバ証明書に含まれてい たサーバ公開鍵で暗号化して,サーバに送信 4 HTTP ベースの通信の開始 以後は,この共有鍵で暗号化されたコネクションで通信する 17 / 23 典型的なフロー (オプション多数あり) クライアント ClientHello ClientKeyExchange ChangeCipherSpec Finished Application Data サーバ --------> <-------<-------<---------------> --------> --------> <-------<-------<-------> ServerHello Certificate ServerHelloDone ChangeCipherSpec Finished Application Data ClientHello: 可能な暗号・圧縮方式の一覧,Session ID ServerHello: 受理可能なアルゴリズム,Session ID Certificate: サーバ証明書と公開鍵 ClientKeyExchange: プリマスターシークレット (乱数) ChangeCipherSpec: 暗号化への移行を示す Finished: 今までの通信内容を暗号化して送信 (双方で確認) 18 / 23 キャッシュが効いたときのフロー クライアント ClientHello ChangeCipherSpec Finished Application Data サーバ --------> <-------<-------<---------------> --------> <-------> ServerHello ChangeCipherSpec Finished Application Data Session ID を ClientHello,ServerHello で指定する 双方が Session ID の再利用に合意することが必要 合意したら継続再開となるため高速 Session ID の生存時間は最大でも 24 時間 キャッシュが効いたとしても http 並みとはならない 19 / 23 POODLE(Padding Oracle On Downgraded Legacy Encryption) 脆弱性 SSLv3 で発見. 前提: 1 平文をブロック P1 ...Pn に分けて暗号ブロック C1 ...Cn を生成 2 最後に Padding 長+Padding が付く 3 サーバでの復号方法: Pi =Decrypt(Ci ) ⊕ Ci−1 攻撃方法: 1 最後のブロック Cn が Padding 長+Padding だけになるように調整 2 解読したい部分のブロック Ci をコピーして最後のブロック Cn に上書き 3 Pn =Decrypt(Ci ) ⊕ Pn−1 をサーバが実行 4 Pn の第 1 バイトは Padding 長.何回もやると一致. (確率 1/256) 5 一致すればサーバ処理が続くので観測すれば分かる. 6 Decrypt(Ci ) = Pn 7 Pi =Decript(Ci ) ⊕ ⊕ Pn−1 [ただし第 1 バイトの部分だけ] を入手! Pi−1 [ただし第 1 バイトの部分だけ] が分かる!! 8 Pi の第 1 バイトに来る文字を他をいじって調節すればよい 20 / 23 HTTPS 化の問題と高速化手法 問題 1:TCP 3-way handshake + TLS 3-way handshake 問題 2:https になるとキャッシュが効かなくなる.必ずエンド-エンド. → サーバー重い.利用者には遅延.CSP はやりたくない. 改善策: Session Resumption → 前のスライド (Session Cache) Session Tickets: クライアント側にチケットを保存.サーバーは Stateless. OCSP (Online Certificate Status Protocol) Stapling: 証明書失効確認をサーバーが行い結果を付ける(従来はブラウザ が確認). False Start: データを一つ前のパケットから送り始める (→今は ALPN?) 21 / 23 最近の話題 EV 証明書 (Extended Validation): 指針を満たしたサイトにのみ発行される証明書(ブラウザの 表示で分かる) https デフォルト化 HSTS (HTTP Strict Transport Security): 次の接続からは HTTPS 接続を強制 Upgrade Insecure Requests: ページ内の http リソースを全部 https にする(コンテンツの 書き換え不要) Opportunistic Security for HTTP: HTTP で暗号化だけを利用する(積極的攻撃には無力) Let’s Encrypt: 証明書の取得を容易化する組織. 22 / 23 参考: HSTS を使った super cookie あるページに次のようなリソースを入れる. sub_domain00.domain/ sub_domain01.domain/ sub_domain02.domain/ ... sub_domain1F.domain/ これで 32 回のアクセスが起きる. サーバーは全部 HTTP だったら,32bit の数字を生成する. そのビットが 1 の sub domaini だけ,HSTS を使って https にセット. 次回は 32 回のアクセスから,http → 0,https → 1 として数値を復元. 23 / 23
© Copyright 2025 ExpyDoc