[go: up one dir, main page]
More Web Proxy on the site http://driver.im/

Security

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

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

投稿日:2016年6月30日 更新日:

JavaScript や HTML5 を安全に使うため、気を付けるべきポイントについて書いています。

1. 基礎知識

発生しやすい脆弱性

  • DOM based XSS (Cross-Site Scripting)
  • Stored DOM based XSS (Cross-Site Scripting)
    • window.localStorage を経由する, etc.
  • Client-side Open Redirect (クライアント側のオープンリダイレクト)
  • CSRF (クロスサイト・リクエスト・フォージェリ)
  • アクセス制御や認可制御の欠落
  • Ajaxデータの漏えい
  • 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
  • Web Storage
  • IndexedDB
  • JSON services
  • XMLHttpRequest.responseText
  • 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属性値を編集する全ての処理
  • イベントハンドラを編集する全ての処理
  • Functionコンストラクタ
  • jQuery関連
    • jQuery()
    • $()
    • $.html()
  • etc.

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

  • location.href
  • location.assign()

(4) <script>タグが効力をもつ文脈への値の出力

  • 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>タグの中に直接 JavaScriptコードを書く場合

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

URLに関する処理

URL()コンストラクタの使い方

処理対象となるURL文字列を URLオブジェクトにしたい場合、以下のように書けばよい。

  • 処理対象となるURL文字列 は、相対URLでもよいし、”//” から始まるURL文字列であってもよい。もちろん絶対URLでもよい。
var url_string = 処理対象となるURL文字列; // どんな形式のURLがセットされているか分からない。
var url_base = "http://www.example.com"; // ベースとなる絶対URL, 通常は現在のURL(location.href や document.baseURI)をセットすればよい。
var urlObject = new URL( url_string, url_base );
console.log( urlObject.href ); // hrefプロパティで 絶対URLが取得できます。

参考

URLのスキーマやドメインをチェックする処理

URLのパース処理

  • URLをパースする処理を書く場合は、自前のコードで行うのではなく、ブラウザの機能(URLオブジェクトや a要素オブジェクト)を使う。
  • URL()コンストラクタを使う。

URLSearchParams()コンストラクタの使用例

以下は、URLSearchParams() – Web APIs | MDN からの引用です(コメントは書き換えています)。

// url.search をコンストラクタに渡し、URLSearchParamsのオブジェクトを生成します
const url = new URL('https://example.com?foo=1&bar=2');
const params1 = new URLSearchParams(url.search);

// URLオブジェクトから直接 URLSearchParams を取得します
const params1a = url.searchParams

// 文字列から URLSearchParamsオブジェクトを生成します
const params2 = new URLSearchParams("foo=1&bar=2");
const params2a = new URLSearchParams("?foo=1&bar=2");

// 配列から URLSearchParamsオブジェクトを生成します
const params3 = new URLSearchParams([["foo", "1"], ["bar", "2"]]);

// 連想配列から URLSearchParamsオブジェクトを生成します
const params4 = new URLSearchParams({"foo": "1", "bar": "2"});

参考

2. オープンリダイレクト

対策

  • 遷移先のURLを固定リストで持ち、この中からURLを取り出して使用する。
  • 遷移先URLを生成する際、先頭に自サイトのドメインを付加する。
  • Chrome,FirefoxではURLオブジェクトを利用してオリジンを確認する。

参考

3. DOM based XSS (Cross-Site Scripting)

ウェブブラウザ側において、JavaScript コードが生成した DOM を HTMLレンダリングする際に問題(XSS)が発生する脆弱性。

そのため、ウェブサーバーとウェブブラウザ間のHTTP(S)通信内容を単純に読み取るだけでは、問題を検知することはできない。

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.

参考

対策

  • HTMLの要素を生成する時は、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://"で始まっている場合のみ処理する
      var urlObj = new URL( URL文字列, ベースとなる絶対URL文字列 );
      if (urlObj.protocol.match(/^https?:/)){
          var elm = document.getElementById("link");
          var text = document.createTextNode(urlObj.href);
          elm.appendChild(text);
          elm.setAttribute("href", urlObj.href);
      }
  • JavaScriptライブラリの問題の場合は、ライブラリをアップデートする。
  • jQueryを使う場合、デフォルトでは、.html()ではなく .text() を使う(こちらだとHTMLエスケープしてくれる)。
  • underscore.js を使う場合、デフォルトでは <%= %> ではなく <%- %> を使う(こちらだとHTMLエスケープしてくれる)。
  • 使用しているテンプレートフレームワークのエスケープ処理を確認する。(必要な文字全てにエスケープがされているか?)
  • フレームワークのセキュリティ機能ではデフォルト設定を使用する。
  • 正しい Content-Types を指定し、適切な JSONエスケープを行う。
  • evalのような危険な関数に信用できない値を渡さないようにする。
  • jQueryのようなAPIで信用できない値を使用する場合は気を付ける。テストを行う。
  • XSS攻撃を伴った自動テストを行う。悪意を持ったデータを使って、コードとテンプレートの自動テストを行う。
  • 複雑なコンテキストには、jQuery encoder を使う。
    • 未検証
  • Content Security Policy を利用する。

ウェブサーバから取得したHTML文字列を使用したい場合の対策

  • 接続するサーバを限定する。
  • そのために、接続先URLの生成処理で対策を行う。方法は「オープンリダイレクト」の対策と同じ。

クライアント側で HTML文字列を生成して表示する場合の対策

方法1: HTML文字列をサニタイズしてくれるライブラリを使う

方法2: HTMLをパースし、生成されたDOMツリーから許可した要素のみ取り出すコードを書く

以下のページにコード例が載っています。

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

  • サーバー側
    • Content-Type ヘッダを指定する。
      Conetnt-Type: application/json; charset=utf-8
    • X-Content-Type-Options ヘッダを指定し、IEにコンテンツスニッフィングをやめさせる。
      • 但し、IE7までは効かない。
      X-Content-Type-Options: nosniff
  • クライアント側
    • 受け取った文字列から、JSON.parse()を使ってJSONを生成する。

サーバー側で出力された文字列内に、JavaScriptコードとして実行されてしまう文字が含まれている場合

例えば Vue.js では、{{}} で囲まれた文字列を JavaScriptコードとして実行してしまう。

参考

対策その1

  • data属性(通常、ここならJavaScriptコードとして実行されないため)にHTMLエスケープした文字列を出力しておき、クライアント側で JavaScriptを使ってその文字列を取得し目的の位置に出力する。

対策その2

  • JavaScriptを実行する仕組みを持ったライブラリ(例えば Vue.js)が持っているHTMLエスケープ手段を利用する。もしくは、そのライブラリのJavaScript実行機能を無効にする。

HTMLエスケープ方法

  • jQueryのtextメソッドを使う。
  • HTMLエスケープ関数を自作する。HTMLエスケープ関数の例1
    function escapeHTML(s) {
      return s.replace(/&/g, "&amp;")
              .replace(/</g, "&lt;")
              .replace(/>/g, "&gt;")
              .replace(/"/g, "&quot;" )
              .replace(/'/g, "&#39;" );
    }

    HTMLエスケープ関数の例2

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

参考

4. HTML5関連

WebSocket

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

セキュリティ

  • 受信したデータは信用しない。チェックする。
  • 重要な情報はTLS(wss://)を使用する。
    var ws = new WebSocket("wss://example.com/");
  • Cookie
    • secure属性がない場合
      • http://example.com/ で発行されたCookieは、
        • https://example.com/
        • ws://example.com:8080/websocket
        • wss://example.com:8081/websocket
        • などで共有される
    • 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を利用する。

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

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

6. メモ

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

7. 参考

関連

こちらの記事もご覧ください。

Web Security

PHPのセキュリティ対策

2016.06.30
PHP

Laravel 5 でのセキュリティ対策 (PHP)

2016.06.30
Web Programming

Webプログラミングのためのリンク集

2017.06.20

スポンサードリンク

📂-Security
-, ,

執筆者:labo


  1. […] ラボラジアン外部リンク  9 Users 39 PocketsJavaScript とHTML5のセキュリティ対策https://laboradian.com/sec-js-html5/JavaScript や HTML5 を安全に使うため、気を付けるべきポイントについて書いていま […]

comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

PHP

Laravel 5 でのセキュリティ対策 (PHP)

目次1. ユーザーによる入力値の検証入力パラメータ値のエンコーディングが正しいことをチェックするMiddlewareの例制御文字を禁止するバリデーションルールを追加する例2. XSS対策Bladeを使 …

OpenSSHOpenSSH

SSH接続の認証時に~/.ssh/id_* の公開鍵情報が全てサーバーに送られる?

SSH接続の認証時に~/.ssh/id_* の公開鍵情報が全てサーバーに送られるらしいという話と疑問点です。

Web Security

パスワードに使える記号は明記して欲しい

パスワードに使える記号は明記して欲しいという話です。

no image

パス名パラメータの未チェック/ディレクトリ・トラバーサル by IPA「安全なウェブサイトの作り方 第7版」

安全なウェブサイトの作り方:IPA 独立行政法人 情報処理推進機構の、「パス名パラメータの未チェック/ディレクトリ・トラバーサル」から一部抜粋する。(この資料はPDFでしか提供されていない) …

OpenSSHOpenSSH

Bitbucket の SSHホスト鍵を更新しました

Bitbucket の SSHホストキーを更新する際の注意点です。