電子署名の替わりに Loaded Magic
今、私は「ひとこと」を書くためのウェブ上のエディタを MovableType、SSI、 Javascript を使って作っている。追加的な機能として「ひとこと」に電子署名のようなものを付けて、その偽造がないことを証明できたらカッコイイだろうなぁと考えた。
しかし、ウェブの標準的で本格的な電子署名技術を、上記の枠組みだけから使うことは不可能だろうし、例えば、コメントのハッシュを付すにしても、そのハッシュの偽造もコメントの偽造とほぼ同程度の容易さと考えるべきだから、逆に変な信用を与えて、話をややこしくしてもいけない。
この「セキュリティ」を考慮する以前の段階で、まず、コメントの一部削除をするとき、自分が開いた別のページのデータが先に反映したのを忘れて、意図しないコメントの削除が行われないように、コメントにマジックナンバーを付け、そのマジックナンバーが合っているかどうかをチェックするという、簡単な安全策を取っていた。
このマジックナンバーのテクニックは、昔のウェブ掲示板などではよく使われるテクニックだったと思う。複数のコメントのマジックナンバーが重ならなければよいので、私が使うマジックナンバーは四ケタの数字を乱数的に生成したものでしかない。
これをコメントのハッシュの最初の四ケタなどとする実装も可能だが、それは上記のように電子署名としての意味をもたず、変な誤解を与えかねない。
でも、何かできないかなぁ…と考えているうちにヒラめいた。
「悪者」が、ページの送信途中に人知れず偽造を行える環境なら、「正義の味方」も、ページの送信途中に人知れず細工できるだろう。その可能性があることが、「悪者」を牽制できるのではないか?
function allot_magic(text) { return Math.random().toFixed(5).substr(2,4); } |
var SALT = "ABCD"; var UPLOAD_TEXT_JS = "https://your.server/tools/upload_text.js"; var CLIENT_PERMIT = "XYZ12345"; var pending_texts = new Array(); var pending_timer = null; function allot_magic(text) { chrsz = 16; var hash = dec_sha1(SALT + text); pending_texts.push([text, hash]); if (! pending_timer) { pending_timer = setTimeout(upload_pending_texts, 500); } return hash.substr(0,4); } function upload_pending_texts(res) { if (pending_texts.length) { var p = pending_texts.shift(); var text = p[0]; var hash = p[1]; upload_text(text, hash, upload_pending_texts); } else { pending_timer = null; } } function dec_sha1(s) { var binarray = core_sha1(str2binb(s), s.length * chrsz); var r = ""; for (i = 0; i < binarray.length; i++) { var n = binarray[i].toString(10); r += n.substring(n.length - 1, n.length); } return r; } var scr; scr = document.createElement("script"); scr.src = 'http://oauth.googlecode.com/svn/code/javascript/sha1.js'; document.body.appendChild(scr); scr = document.createElement("script"); scr.src = UPLOAD_TEXT_JS + "?permit=" + encodeURI(CLIENT_PERMIT); document.body.appendChild(scr); |
上は最後で sha1.js と「正義の味方」が管理するセキュアなサーバーのディレクトリ(上の例では https://your.server/tools/)に置いた次の upload_text.js を読み込む。
var LOG_SERVER = "https://your.server/log_text_database.cgi"; function upload_text(text, hash, callback) { var data = "text=" + encodeURI(text) + "&hash=" + encodeURI(hash) + "&uri=" + encodeURI(location.href); var httpobj = requestFile(data, "POST", LOG_SERVER, true, callback); } function createHttpRequest() { if(window["ActiveXObject"]){ try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e2) { return null; } } } else if(window["XMLHttpRequest"]){ return new XMLHttpRequest(); } else { return null; } } function requestFile(data, method, fileName, async, on_loaded) { var httpoj = createHttpRequest(); httpoj.open(method, fileName, async); httpoj.onreadystatechange = function() { if (httpoj.readyState==4 && on_loaded) { on_loaded(httpoj); } } if (data) { httpoj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); } httpoj.send(data); return httpoj; } |
すると、「悪者」が知らずに勝手にマジックナンバーを割り振ったものを見せて、誰かにユーザーの別の印象を与えているとき、「正義の味方」は、それがユーザーの意図ではないという証拠をネット上に保持し、場合によってはユーザーも「悪者」も知らないところで対抗することができる。
もちろん、「悪者」が summoner.js を狙ってくることもあるだろう。しかし、国家や信託銀行など合法的な「正義の味方」はブログのプロバイダを味方に付けられるのだから、最終的に有利なのは「正義の味方」だろう。
……とかなり妄想が入ったことを考えた。セキュリティ的には、「正義の味方」であれ、こういうことあっちゃならんのだろうけど。
(ちなみに、スクリプトを分けたのは、XMLHttpRequest のクロスドメイン制限を避けるため。ユーザーが何者かは https の Cookie などで確かめているとすべきで、SALT はもちろん、CLIENT_PERMIT もセッションごとに変えられるとしても、それはサーバーへの DOS 攻撃を少しだけ難しくするぐらいの意味しかない。あと、sha1.js は外部のサーバーから "include" するのではなく、ライセンスをクリアにした上で、ソースにベタ書きするなり、自分のサーバーに置くなりすべきだし、一般に、関数名が重ならないよう考慮する必要もある。)
(えーっと、私はセキュリティどころかプログラムの専門家ではないことに注意!専門家から見れば、ツっこみどころはいろいろあると思う。例えば、ハッシュの使い方として、SALT なんて今は使わないし、dec_sha1 はかなり反則だと思う。この記事の最初のバージョンではクロスドメイン制限を忘れていたぐらいだし。)
■ |
参考
|
更新: | 2011-05-07,2011-05-09 |
初公開: | 2011年05月07日 22:32:11 |
最新版: | 2011年05月09日 05:29:37 |
2011-05-07 22:32:13 (JST) in セキュリティ JavaScript | 固定リンク | コメント (8) | トラックバック (0)
コメント
投稿: JRF | 2011-05-08 03:12:49 (JST)
逆に、上で script タグで書き込んでいる sha1.js については、セキュアサーバー上にコピーしてから読み込んだり、ライセンス関係をクリアにした上で、ソースの中にベタ書きしたほうがいい。(上で私が書いたソースのライセンスに関してはもちろん Public Domain、数式的利用の範囲ってことでいいです。)
投稿: JRF | 2011-05-08 21:04:06 (JST)
投稿: JRF | 2011-05-09 03:47:12 (JST)
投稿: JRF | 2011-05-09 05:32:43 (JST)
投稿: JRF | 2011-05-10 04:10:39 (JST)
投稿: JRF | 2011-05-10 18:02:44 (JST)
投稿: JRF | 2011-05-13 12:25:55 (JST)
投稿: JRF | 2011-05-19 05:19:19 (JST)