Web Application Security Memo

ウェブセキュリティに関するメモ書き

JavaScript (とHTML5)のセキュリティ対策

※当サイトにはプロモーションが含まれています。

公開日: 更新日:

基礎知識

発生しやすい脆弱性

  • DOM based XSS (Cross-Site Scripting)
  • Stored DOM based XSS (Cross-Site Scripting)
    • window.localStrage を経由する, etc.
  • Client-side Open Redirect
  • CSRF (クロスサイト・リクエスト・フォージェリ)
  • アクセス制御や認可制御の欠落
  • etc.

信頼できない値はどこからくるのか?

  • windowオブジェクトやdocumentオブジェクトのプロパティ
    • コンテンツをロードした際のURIの構成要素
      • document.URL *
      • document.location.pathname *
      • document.location.search *
      • document.location.hash
      • document.location.href *
    • コンテンツのURIが書かれていたリンク元ページのURI
      • document.referrer *
    • Cookie
      • document.cookie
    • ウィンドウ間/フレーム間の値受渡しに使われる変数
      • window.name
      • postMessage(arg) 受信側の関数引数 f(arg) のarg.data
      • etc.
  • input fields
  • JSON services
  • etc.

※ * がついたプロパティは、URLエンコードされた値がセットされる。

信頼できない値が出力されうるところ(シンク)

(1) 信頼できない第三者スクリプトを含むおそれのあるコンテンツのロード

  • iframe.src = html-uri
  • script.src = script-uri
  • etc.

(2) 文字列がコードとして実行されるところ

信頼できない文字列を使って以下の処理を実行させないこと。

  • eval(string)
  • execScript (IE10まで?)
  • new Function(string)
  • setTimeout(string, time)
  • setInterval(string, time)
  • location.replace/location.assign
  • script.innerHTML = code-string
  • scriptタグのsrc属性値を編集する全ての処理
  • イベントハンドラを編集する全ての処理
  • etc.

(3) オープンリダイレクトに使用されうるところ

  • location.href

(4)
  • document.write(text)

参考

いろいろなエスケープ処理

HTMLエスケープ

  • 「DOM based XSS (Cross-Site Scripting) 対策」の項目を参照。

URLエンコードの例

GETパラメータ値に対するエンコード
  • RFC 3986 に従ったエンコードを行う。

    // encodeURIComponent関数を使用する(コロンやスラッシュも変換してくれる)
    var param_value = encodeURIComponent(param_value);
  • より厳密に RFC 3986 に従ったエンコードを行う場合は、以下の関数を用意して使用する。

    function fixedEncodeURIComponent (str) {
      return encodeURIComponent(str).replace(/[!'()*]/g, function(c) {
        return '%' + c.charCodeAt(0).toString(16);
      });
    }
    var param_value = fixedEncodeURIComponent(param_value);
参照元
POSTパラメータ値に対するエンコード
  • W3C による application/x-www-form-urlencoded の仕様に従う。

    // encodeURIComponent関数の適用に加えて、%20 を + に変換する
    var param_value = encodeURIComponent(param_value).replace(/%20/g, '+');

文字列リテラルのエスケープシーケンス

  • 以下の Table 4 にエスケープが必要な記号が書いてあるので、これに従ってエスケープする。

  • 文字列リテラル内に “</script” という文字列を記述する場合、”<\/script” と記述する必要がある。

参考

DOM based XSS (Cross-Site Scripting)

HTMLを生成するメソッド・処理

  • .innerHTML, .outerHTML に文字列をセットする。
  • createElement
  • document.write/document.writeln
  • jQuery’s selector, $()
  • jQuery’s .html() メソッド

jQueryでHTMLエスケープされない出力メソッド

以下のメソッドに渡す文字列は事前にHTMLエスケープ処理が必要である。

  • .after()
  • .append()
  • .appendTo()
  • .before()
  • .html()
  • .insertAfter()
  • .insertBefore()
  • .prepend()
  • .prependTo()
  • .replaceAll()
  • .replaceWith()
  • .unwrap()
  • .wrap()
  • .wrapAll()
  • .wrapInner()
  • .prepend()
  • etc.

参考

対策

  • DOM操作用のメソッドやプロパティを使用する。

    • createElement(), createTextNode(), appendChild(), insertBefore(), setAttribute(), etc.

    テキストノードをDOM APIを介して操作することで安全にHTMLを生成

    var div = document.getElementById("msg");
    var text = document.createTextNode( some_text );
    div.appendChild( text );

    DOM APIを経由して属性値を設定する

    var f = document.getElementById("form");
    var elm = document.createElement( "input" );
    elm.setAttribute( "type", "text" );
    elm.setAttribute( "value", some_text );
    f.appendChild( elm );

    textContentを使う例

    document.querySelector('#foo').textContent = 信用できない文字列;
  • コンテキスト(文脈)に応じたエスケープ処理を行う。

    • URLを指定する属性値は、http あるいは https に限定する。

      // URL が"http://"または"https://"で始まっている場合のみ処理する
      if (url.match(/^https?:\/\//)){
          var elm = document.getElementById("link");
          var text = document.createTextNode(url);
          elm.appendChild(text);
          elm.setAttribute("href", url);
      }
  • JavaScriptライブラリの問題の場合は、ライブラリをアップデートする。

  • jQueryを使う場合、デフォルトでは、.html()ではなく .text() を使う(こちらだとHTMLエスケープしてくれる)。

  • underscore.js を使う場合、デフォルトでは <%= %> ではなく <%- %> を使う(こちらだとHTMLエスケープしてくれる)。

  • 使用しているテンプレートフレームワークのエスケープ処理を確認する。(必要な文字全てにエスケープがされているか?)

  • フレームワークのセキュリティ機能ではデフォルト設定を使用する。

  • 正しい Content-Types を指定し、適切な JSONエスケープを行う。

  • evalのような危険な関数に信用できない値を渡さないようにする。

  • jQueryのようなAPIで信用できない値を使用する場合は気を付ける。テストを行う。

  • XSS攻撃を伴った自動テストを行う。悪意を持ったデータを使って、コードとテンプレートの自動テストを行う。

  • 複雑なコンテキストには、jQuery encoder を使う。

    • 未検証
  • Content Security Policy を利用する。

サーバーからJSONデータを返す場合の注意点

  • サーバー側

    • Content-Type ヘッダを指定する。
    Conetnt-Type: application/json; charset=utf-8
    • X-Content-Type-Options ヘッダを指定し、IEにコンテンツスニッフィングをやめさせる。
      • 但し、IE7までは効かない。
    X-Content-Type-Options: nosniff
  • クライアント側

    • 受け取った文字列から、JSON.parse()を使ってJSONを生成する。

HTMLエスケープ方法

  • jQueryのtextメソッドを使う。

  • HTMLエスケープ関数を自作する。

    HTMLエスケープ関数の例1

    function escapeHTML(s) {
        return s.replace(/&/g, "&")
                .replace(/</g, "<")
                .replace(/>/g, ">")
                .replace(/"/g, """ )
                .replace(/'/g, "'" );
    }

    HTMLエスケープ関数の例2

    function escapeHTML(str) {
         str = str + "";
         var out = "";
         for(var i=0; i<str.length; i++) {
             if(str[i] === '<') {
                 out += '<';
             } else if(str[i] === '>') {
                 out += '>';
             } else if(str[i] === "'") {
                 out += ''';
             } else if(str[i] === '"') {
                 out += '"';
             } else if(str[i] === '&') {
                 out += '&';
             } else {
                 out += str[i];
             }
         }
         return out;
    }

参考

HTML5関連

WebSocket

  • JavaScriptにおける双方向通信機能
  • ws:, wss: というスキームを使用する

セキュリティ

  • 受信したデータは信用しない。チェックする。

  • 重要な情報はTLS(wss://)を使用する。

    var ws = new WebSocket("wss://example.com/");
  • Cookie

    • secure属性がない場合
    • secure属性がセットされている場合
      • https://example.com/ で発行されたCookieは、
        • wss://example.com:8081/websocket
        • と共有される
  • どのサーバーにでもWebSocketの接続を行うことができ、HTTPリクエストは偽造できてしまうため、何らかの認証処理を独自に追加する必要がある(しかも、Cookieまで送られてしまう)。

Web Messaging (Cross Document Messaging) (XDM) (window.postMessage())

  • ウィンドウ間でメッセージのやり取りを行う機能
  • 異なるオリジンの間で通信が可能

セキュリティ

  • 受信側
    • 送信側のオリジンをチェックすること。
    • 受信したデータをバリデーションすること。

Web Storage

  • 同一生成元が一致しないとデータにアクセスできない。
    • IEは port を無視するらしい。
    • IE8は更にschemeも無視するらしい。

Web Storage の種類

  1. sessionStorage
    • ウィンドウ、タブ単位のみ有効なストレージ
    • 一時的なデータの保存に利用される。
  2. localStorage
    • データを永続的に保存するストレージ

セキュリティ

  • 機密情報を保存しないこと。
  • 永続させる必要がないデータは sessionStorage に保存する。
  • データが必要なくなったら、ちゃんと削除する(特にlocalStorage)。
    • ログアウト時など
  • ユーザー別にデータを保存し、そのデータを他のユーザーと共有しない場合、意図しないアクセスができないようにする。
    • setItemメソッドの keyパラメータに、ユーザーIDを含める。
    • 未ログイン状態のアクセス時には、データを削除するようにする。
  • JavaScriptからの読取りを防ぐ仕組みがない。そのような場合は、CSPなどを使う。
  • 1つのオリジンで複数のアプリケーションをホストしないこと(データが共有されてしまう可能性があるため)。

Web Database (Client-side databases)

以下の2種類がある。

  1. Indexed Database
    • key-value型
    • JavaScriptのオブジェクトを保存する
  2. Web SQL Database
    • リレーショナルデータベース

セキュリティ

  • 機密情報は格納しないこと。
  • 格納されているデータが安全であると仮定してはいけない。

Web Workers

  • バックグラウンドでJavaScriptを動かす仕組み。

セキュリティ

  • 意図しないJavaScriptコードが実行されないように注意する。
  • メイン処理側から受信したデータは信用しない。チェックする。
  • Data URL Scheme が使える?
    • new Worker, importScripts

XMLHttpRequestによるクロスドメイン通信

  • サーバー側でCORS(Cross-Origin Resource Sharing)の仕様を実装しておくことにより、クロスドメイン通信できる。
  • クロスドメイン通信を許可するドメインを制限することができる。

セキュリティ

  • 認証の仕組みではないことに注意する。認証する場合は、別の仕組みが必要である。
  • CRSF(クロスサイト・リクエスト・フォージェリ)への対策は、トークンを使った通常の方法で問題ない。

参考

Canvasタグの crossorigin属性によるクロスドメイン通信

  • 「XMLHttpRequestによるクロスドメイン通信」と同様

オフライン Webアプリケーション

  • オフラインでもアプリケーションが使えるように、リソースをキャッシュする機能

セキュリティ

  • 重要な情報を扱う場合は、HTTPSを利用する。

HTTPレスポンスヘッダーでのセキュリティ対策

  • CSP(Content Security Policy)
  • X-Frame-Options
    • Clickjackingを防ぐ。
  • X-Content-Type-Options
    • ウェブブラウザ (IE) にコンテンツスニッフィングをやめさせる。
  • X-XSS-Protection(XSS Filter)

メモ

  • コンテキストに応じたエンコーディング(エスケープ)を行う。
  • 文字列からコードを生成しないこと。

参考