パスワード問合せシステムを作る (clojureのreducers)

  • 128
    Like
  • 4
    Comment
More than 1 year has passed since last update.

JALパスワード運用

現在のパスワードを教えてくれるからといって、「平文で保存してる!くぁwせdrftgyふじこlp‎」と脊髄反射してはいけません。

JALの6桁数字パスワードがどう格納されているか?
古いシステムなのでMD5でハッシュ化していると想定しますが、もちろんsaltは付けているでしょう。

さて、そんなパスワード保管方式で、現在のパスワード問合せに応答するシステムを作ってみます。

パスワードを「567890」、saltを「hoge」として、データベースには"hoge$567890"のMD5値"4b364677946ccf79f841114e73ccaf4f"が格納されているとします。

総当りしてみましょう。

(ns six-length.core
  (:require [clojure.core.reducers :as r])
  (:import [java.security MessageDigest]))

(defn hexdigest [s]
  (let [digester (MessageDigest/getInstance "MD5")]
    (. digester update (.getBytes s))
    (apply str (map #(format "%02x" (bit-and % 0xff)) (. digester digest)))))

(defn find-password [salt pw]
  (->> (range 0 1000000)
       (map #(format "%06d" %))
       (map (fn [_] [_ (hexdigest (str salt "$" _))]))
       (filter #(= pw (second %)))))  

こんな感じで、(range 0 1000000)で生成した6桁数字のパスワードを総当りで計算します。salt付きなので予め辞書を作っておくことはできません。

実行すると、

=> (time (into [] (find-password "hoge" "4b364677946ccf79f841114e73ccaf4f")))
[["567890" "4b364677946ccf79f841114e73ccaf4f"]]
"Elapsed time: 35719.659129 msecs"

… 36秒近くかかってしまいました。ちょっと使えないですね…

ちなみに実行環境は、Corei7-4770 Windows 7 です。

せっかくのマルチコアを活かせてないので、Clojureのreducersを使って、並列化してみます。

(defn find-password [salt pw]
  (->> (vec (range 0 1000000))
       (r/map #(format "%06d" %))
       (r/map (fn [_] [_ (hexdigest (str salt "$" _))]))
       (r/filter #(= pw (second %)))))

先ほどのfind-password関数はこのようになります。
vectorでないと並列実行されないので、(range 0 1000000)をvecで変換しています。(次のバージョンではrangeもreducers対応されるという噂があります…)

あとは元のmapfilterをreducersに置き換えるだけです。

=> (time (r/fold (fn ([] [])
                  ([x y] (into x y)))
              (find-password "hoge" "4b364677946ccf79f841114e73ccaf4f")))

["567890" "4b364677946ccf79f841114e73ccaf4f"]
"Elapsed time: 11248.795464 msecs"

11秒になりました! これならSalt付きMD5で一昔前のふつうのパスワードの保存方式で作ってあるシステムでも、現在のパスワードを通知することができそうですね :stuck_out_tongue: