MD5 in ActionScript

ここで使っているswfとソース、連動するランキングCGI一式をダウンロード(9/4 解説を追加, rankingcheck.swfのバグを修正, ranking.cgiの些細なミスを修正)

Table of Contents

  1. はじめに
  2. MD5 hash作成

    適当な文字列から、MD5 hashを生成する

  3. パスワード管理

    swfからパスワードを得ることが限りなく困難なパスワード認証の方法

  4. CGIとの連携

    ランキングCGIでMD5を使う意味と基本的な仕組みの解説

  5. 文字コードについて
  6. Macでのバグ
  7. 参考

はじめに

まえがき

MD5ハッシュを算出するASで書かれたスクリプトは、Flash Experimentsのものがありますが、2バイト以上の文字に対応してないようで、少し他にも探したのですがそれっぽいのが見つかりませんでした。それで自作してみましたが、もし先人の作られたものを知っている方がおりましたら教えていただけると幸いです。

MD5とは

MD5とはハッシュ関数です。ハッシュ関数とは、原文(例えばパスワード)を元にあるアルゴリズムでユニークな乱数を生成するものです。

同じ原文であれば常に同じ乱数が生成されます。また、生成された乱数から元の文を復号することは不可能です。MD5の場合、原文の長さに関わらず常に128bitの固定長のハッシュが算出されます。

この性質を利用して、パスワードの照合やファイルが改変されていないかの確認に利用されています。

MD5 hash作成

適当な文字を入れて>ボタンを押してください。MD5でハッシュを作ります。2バイト文字も可です。(UTF-8として算出します)

パスワード管理

悪いケース

パスワードが正しいか確認する仕組みを考えます。

最も単純な方法は、パスワードを確認する側が、正しいパスワードを保管していて、それと照合する方法です。保管される場所はサーバーだったり、swfの中だったりします。

しかし、この方法ではサーバーの中が覗かれたり、swfの解析(これは簡単にできてしまう!)を行われるとパスワードが丸分かりになってしまいます。

管理を良くするには

そこで、MD5のようなハッシュ関数を使います。

例えばパスワードが morinokumasan だとするとMD5で生成したハッシュは7178457dc5bab22f79ec3b8277adc3c4になります。(上にあるFlashで作成してみて下さい。)

照合する側は、この 7178457dc5bab22f79ec3b8277adc3c4 だけを保管しておきます。パスワードを照合するときは、受け取ったパスワードからMD5でハッシュを算出してみて、もしこれと同じになれば、パスワードが正しいことになります。

もしパスワードが一文字でも違っていると、ハッシュは全く違うものになります。ハッシュが一致するということは、元になったパスワードも一致しているということなのです。

実例

実際にやってみます。正しいパスワード、morinokumasan を入れると OK! と出ます。

このswfを解析しても、パスワードはどこにもありません。7178457dc5bab22f79ec3b8277adc3c4 というハッシュだけがありますが、これから元の morinokumasan というパスワードを得ることは不可能です。

#include "md5.as"
var passHash = "7178457dc5bab22f79ec3b8277adc3c4";
//
function send() {
    var currentHash = MD5(seed.text);
    if ( passHash == currentHash ) {
        disp.text = "OK !!";
    } else {
        disp.text = "bad password";
    }
}

CGIとの連携

特定のswf以外からはアクセスされたくないCGIがあるとします。例えば、ゲームFlashとランキングを記録するCGIの関係がそうです。

ただ単純に、swfからCGIにランキングのデータを流すとします。悪意のユーザーが嘘のスコアを送信すれば、簡単にランキングに登録されてしまいます。GETならURL欄に入力するだけで出来てしまいます。

もうちょっと捻って、CGIにスコアと一緒にパスワードを送信するとします。これでも、パスワードは見つかってしまいます。送信された内容を見ることは、たとえPOSTであったとしても容易です。

そこで、何か変化のある文字列と、パスワードの両方をつかってハッシュを算出します。スコアとパスワードの文字列を連結したものを元にハッシュを作成する例を考えます。スコアが 1000 で、パスワードが morinokumasan の場合、1000morinokumasan という文字列からハッシュを作成します。4c60138c16d88325e5b32e9f43e6e106 というハッシュが生成されるはずです。

この場合、CGIに送るデータはこんな感じになります。
pass=4c60138c16d88325e5b32e9f43e6e106&score=1000&name=nanasi・・・
この中に、生のパスワードは含まれていません。

CGIはこれを受け取ると、swf側が送信するときにしたのと同じ方法で、ハッシュを作ってみます。スコアは送信されてきて分かっているので、それとパスワードをくっつけて 1000morinokumasan からハッシュを算出します。同じ文字列から作られたハッシュは同じ物になるので、送られてきたハッシュが正当なものであれば、CGI側で作成したものと一致するはずです。

この方法であれば、スコアが変われば毎回送るハッシュも変わります。

しかし、この方法でも穴はあります。この方法の場合、CGIもswfも自分の手元にパスワードを持っていなければ、正当なハッシュを作成できません。CGIはともかくとしても、swfを解析されてパスワードが漏れてしまうと、無意味なものになってしまいます。

なんとかして、見つかりにくいようにパスワードの文字列を細切れにでもして埋め込むしかありません。それでも生で流すよりは良いかもしれません。

文字コードについて

Flashは外部に送信する時に、文字列をUTF-8にしてURLエンコードします。"あ"であれば、%E3%81%82 になります。これをCGI側で受ける際、特に必要が無ければ文字コードは考えないことにします。Flashが送ってきた %E3%81%82 をデコードしたビット列が何コードであれ、単純にビット列と見てそれをもとにハッシュを作成すれば、Flash側のハッシュと一致するはずです。

しかし、やってみるとそのままでは一致しないことがわかります。原因は、Flashの内部文字コードが送信時のUTF8ではなく、UCS-2 or 4を使っているためです。

対処方の一つとしては、UTF-8で送られてきた文字列をCGI側でUCF-2に戻してハッシュを作って照合するという方法がありますが、UCF-2が使える環境やブラウザが少なかったりして面倒くさいです。

そこで、MD5.as内で、UCF-2 -> UTF-8 の変換を簡易的に行っています。結果的にMD5.asで得られるハッシュはUTF-8で書かれた文字列のビット列から作成したハッシュになっているはず(?)です。(ならない文字列とかあったら教えてください・・・)

Macでのバグ

当方にMacが無いので確認できないのですが、MacのFlashプレイヤーにビット演算の際のバグがあるそうです。これはハッシュ算出のプログラムにとっては致命的なバグです。Flash Experiments内のTrouble on the MacにあるASによるMD5ハッシュ算出スクリプトは、このバグを回避しているものです。

今回作ったものにもバグ回避を入れても良かったんですが(関数だけは用意しておいた)上記サイトにあるスクリプトが作成されてから2年程経過しているので、いまでは修正されているかもと思い、バグ回避を入れていません。もしMacユーザーの方がいましたら、テストしてみて頂けると幸いです。

参考

check