作成 2011.11.27
更新 2011.11.27
Node.js の jsdom に読み込ませる HTML を安全化するノウハウ
Node.js を Proxy サーバー兼レイアウト変更ツールとして使用すると、毎回同じ問題に遭遇するのでそれの回避方法のまとめ。
Node.js-v0.6.2 で確認。いずれ解決されてバッドノウハウになるだろうと思われる。
サンプルコード全体
管理していないサイトから取得した html を jsdom へそのまま突っ込むと、未対応コードの問題で例外が発生する場合があり、期待通りの動作をする保証がない。
現段階のバージョンでは以下の問題が判明している。
  • 正規表現の複数行マッチに対応していない。
  • javascript コードを含んだ HTML 文字列を jsdom に読み込ませると、読み込んだ時点で文字列内の javascript コードが実行される。
従って、これらの問題を回避するには以下のような処理順序となる。
  1. 改行コードを無害な文字列へ変換する
  2. javascript コードを無害な文字列へ変換し、退避する
  3. jsdom で処理
  4. jsdom の処理結果の文字列に元の javascript コードを埋め戻す
  5. 改行コードをもとに戻す
正規表現の複数行非対応をカバーするには改行コードを消してしまうのが簡単だが、単純に消してしまうと // ~~~ のコメント以降のコード全体が無効になってしまう。
また、// ~~~ の行を無差別に削除すると、スクリプトコメント以外の文字列も削除してしまうため、これらの問題を回避するようにしている。
// サンプル HTML データ
var str = '<html><head><script type="a"><!--\n'
        +'a;\n'
        +'//test\n'
        +'aa;\n'
        +'//---></script>\n'
        +'<Script type="b">b;</script>\n'
        +'</head><body></body></html>';
console.log('== 処理前 ==');
console.log(str);
var counter = 0;
var scrs = new Array();
var matches;

// 何度も同じ正規表現を使いまわすのでオブジェクトとして設定
var re = new RegExp('<script.+?</script>','i');

// 無害な文字列へ変換
// 現時点では RegExp.multiline の効果が無いので改行コードを消す必要がある
str = str.replace(/[ \t]*[\r\n]+/g,'<!--BR-->');
while(matches = str.match(re)){
  scrs[counter] = matches[0];
  str = str.replace(re,'<!--SCRIPT'+counter+'-->');
  counter++;
}
console.log('\n== 無害化処理後 ==');
console.log(str);

// jsdom でレイアウト変更などを処理
var jsdom = require('jsdom');
var document = jsdom.jsdom(str);
var res = document.innerHTML;

// 元のコードを埋め戻す。
for(var k in scrs){
  res = res.replace('<!--SCRIPT'+k+'-->', scrs[k]);
}
res = res.replace(/<!--BR-->/g,'\n');
console.log('\n== 埋戻し後 ==');
console.log(res);
実行結果
$ node sample.js
== 処理前 ==
<html><head><script type="a"><!--
a;
//test
aa;
//---></script>
<Script type="b">b;</script>
</head><body></body></html>

== 無害化処理後 ==
<html><head><!--SCRIPT0--><!--BR--><!--SCRIPT1--><!--BR--></head><body></body></html>

== 埋戻し後 ==
<html><head><script type="a"><!--
a;
//test
aa;
//---></script>
<Script type="b">b;</script>
</head><body></body></html>
タグ: Node.js

©2004-2017 UPKEN IPv4