XSS対策のセキュアJSについて(基本編)
社内ブログでまとめた内容をこっちでも書いてみる。ただ、フレームワーク使ったりしている中級者以上にはあんま参考にならないと思いますが基本編みたいなノリで御覧ください。
エスケープをする
すぐできる対策であり、割と効果的なものがエスケープ処理です。これは問題を起こしうる特殊文字を文字参照に変換して、実行させないようにする処理です。対応する特殊文字は「&」「<」「>」「”」「’」の5つです。
function escapeHTML(str) { str = str.replace(/&/g, '&'); str = str.replace(/</g, '<'); str = str.replace(/>/g, '>'); str = str.replace(/"/g, '"'); str = str.replace(/'/g, '''); return str; }
ちなみにES6であればテンプレートリテラル(=Template strings)を使用して下記処理もできます。
function escapeHTML() { var raw = String.raw.apply(null, arguments) var safe = raw.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); return safe } var input = "<img src=# onerror=alert(1)>"; var html = escapeHTML`<div>${userinput}</div>`;
テンプレートリテラルの使い方は「`」(バッククオート)で囲むだけです。
URLはhttpおよびhttpsスキーム
URLを取り出し、それをデータとして扱う際に気をつけたいことは、javascriptスキームなど複数のスキームを介在させないために、リンク先がhttp://かhttps://なのかを判断して挿入するようにすることです。
var url = decodeURIComponent(location.href); if( url.match( /^https?:\/\// ) ){ var elm = document.getElementById("link"); elm.setAttribute("href" url); }
javascriptスキームはいわゆるブックマークに登録して便利な機能として使うというのが一般的ですが、それをXSSとして使用されるのを防ぐための策であります。
JSONPの取り扱い注意とJSON.parse
JSONPはクロスドメイン、つまり同一のドメインのデータでなくても引っ張ってこれるもので便利なものではありますが、同時に他者が仕組んだ悪意のあるJSONPデータも入り込む可能性があります。
JSONPをまったく使用するな、ということではないのですが使用する際は信頼のあるデータであることを証明した上で使用するのが吉です。
そしてJSONほか外部データを使用する際は必ず JSON.parse関数 を使用してください。これは文字列を JSON として解析するためのものなので、eval関数だとJSONの中に実行文が紛れていると実行されてしまう恐れがありますので意識して使いましょう。
レンダリングメソッド使用廃止
主に下記のソースになります。これは上述したエスケープ処理がない場合、確実にXSSの標的になり得ますし、この部分を使って悪用されるおそれがあるかもしれないからです。
element.innerHTML = “…”; element.outerHTML = “…”; document.write(…); document.writeln(…);
jQueryに頼り過ぎるな
jQueryはjsライブラリの1つですが、煩雑なコードを容易に記述しやすくして、デザイナーさんにも扱える簡単なもので今も制作会社で広く使用されていると思われます。
実はjQueryでもXSS対策はできるものがあり、それは text() です。受け取る値をテキストデータにエスケープしてくれる代物なのでinputでの入力を即出力するものには適しています。
$('input').on('keyup', function(e){ $('.hoge').text($(this).val()); });
しかしながら、jQuery自身の長所でもあり弱点としても見られる「DOMの直接操作」部分が割とネックとなってきています。つまりDOM操作によるXSSの介入が起こりうる可能性があるのです。
以前のver.1.6では $(location.hash) の脆弱性が指摘されており、要素の検索や作成などを引数の文字列で判断するため、$()内でスクリプトを記載すると#idで指定したものがそのまま要素として判断されることがありました(ver1.6.3にて修正済み)
http://my-web-app/#<img onerror="alert(1)" src="xxx"> $('#<img onerror="alert(1)" src="xxx">') //ハッシュ値で判断すると左の値で取得する //<img>タグで処理されるようになってしまう。
jQuery自身もver3.0の更新(2016年5月)があったように、日々使いやすくかつ脆弱性を潰してきて、簡単な装飾程度であれば使用する面では問題無いかもですが、セキュリティ面を考えた上でwebサイト・アプリに使い続けるというのは難しい選択のようにも思えます。
なのでjQueryのみに頼り続けるのは危ないかも、というのを念頭に置いておくと良いかもしません。(※使用をやめろ!と固く禁じる訳ではないです)
こちらからは以上です。つまりReact.jsについてちゃんとやれということでした。