2009-05-08

無線LANの「最強暗号化キー」を作ろう

無線LANを使用するのに欠かせない「暗号化キー」(WPAキー)。
できる限り最高レベルのセキュリティを目指すキーを自動的に生成するツールを、JavaScriptで作ってみました。
以下のボタンを押すと、なるべく最強の暗号化キーをランダムに生成します。


(※WPA/WPA2-PSKのパスフレーズ)


簡単な説明


無線LANの暗号化方式がWPA/WPA2-PSK(つまりTKIPやAES)で共有鍵を使う場合に、指定する暗号化キー(パスフレーズ)を、ランダムに生成します。
WPAで定められている仕様等の範囲で、できる限り最高のセキュリティレベルを目指したパスフレーズを生成します。

  • 無線ルーターによって使用可能な記号類の種類が違うようなので(詳しくは後述)、「どんな文字を使わないか」を自由に設定できるようにしました。
    いくつかのルーターで拒否される文字はデフォルトで用意してあります(詳細後述)。お使いの無線ルーターにそれでも受け付けてもらえない場合は、「使いたくない文字」の種類を増やして調整してみてください。
  • 「英大文字」「英小文字」「数字」「記号」の4文字種とも必ず含む、半角63文字のパスフレーズを生成します。
    (自動生成した結果、上の4種のうち1種でも欠けていたら、それはボツにして作り直しています)
  • 生成されるのはWPA用のキーであって、WEP用の暗号化キーではありません。長さ(63文字)がWEPには合いませんし、それ以前にWEPはもはやセキュリティ的にあまり信頼できないので、そもそも利用をオススメしません。
  • 最高レベルのパスフレーズと言っても、「このツールで生成したパスフレーズであれば永遠に破られない」、という意味ではありません。WPAの仕様や無線ルーターの実装等の制約の中での最高レベルを目指しただけです。科学や技術の進歩とともに容易に破られうる日がいつかは来ます。
  • 「ランダム」と言ってもJavaScriptのMath.random()の疑似乱数をそのまま使っています。どのくらい疑似なのかはブラウザ(JavaScriptエンジン)の実装に依存します。
  • 「使いたくない文字」の設定を増やせば増やすほど、セキュリティ強度は弱まり、最高でも何でもないレベルのパスフレーズが生成されやすくなります。

以下、背景と技術的解説が長々と続きます。興味のある方はどうぞ。

背景(どうして生成ツールを作ったか)


ことの始まりは、先日買ったバッファローの無線ルーターWZR-AGL300NHでした。

ここでCMですが、この機種は5GHz帯(11a)と2.4GHz帯(11g/b)の同時利用ができるうえに有線ポートはギガビット対応、なのにアマゾンでは単体でも¥1.2万強とリーズナブルなお値段で販売されています。さらにUSBアダプタとのセットだと¥1.5万弱イーサネットコンバータとのセットに至っては¥1.7万弱と大変お買い得な品となっております。…CMここまで。

この無線ルーター、AWSやWEPといった暗号方式別に複数のSSIDを吹いて、異なるセキュリティレベルのネットワークを混在させられるので便利です(詳しくは、先日の記事で)。
ただ、AESがあるからTKIPは要らないのに、TKIP用のSSIDも吹かれるのは残念です。(WEP用SSIDは不要なら停止できるようになっているのですが、TKIPのネットワークは止めることができません)

そこで、TKIPを止めることができないのなら、せめて使わずに放置しておいても安心なように、セキュリティのレベルを最高まで上げてしまおうと考えました。仮に今後TKIPの脆弱性みたいなものが見つかっても、それなら簡単には破られないでしょう。……たぶん。

セキュリティレベルを上げるためには、何をいじればよい?

さて、「TKIPネットワークのセキュリティを最高レベルに引き上げる」と言っても、この無線ルーターのTKIPの項目で設定できるのは、次の3項目だけです。
SSID
暗号化キー
キーの更新間隔
(なお、キー更新間隔はAESと共通)

ちなみに「ルーターのセキュリティレベル」と言えば「MACアドレス制限」を思い出す人も多いかもしれません。ところがこのルーターでは、AOSS機能がオンになっているとMACアドレスの制限はできないのです。
じゃあAOSSはオフにすべきかと言うと、そうもいきません。AOSSを無効にすると、マルチSSID機能が使えなくなってしまうのです。

一見「使えないルーターだなあ」と思っちゃいそうですが、実はMACアドレス制限は無意味(高木先生の記事)だとか。
MACアドレスは通信の過程で暗号化されないため簡単に傍受できます。そのためMACアドレスで制限したとしても、その気になった人がMACアドレスを偽装して制限をすり抜けるのは、難しいことではありません。

もう一つ、「意味のないセキュリティ対策」としてはSSIDのステルス化があります。
ステルスにしたところで、SSIDは簡単に入手できるツールで簡単に発見できます。ステルスにしてもセキュリティ的に安全が得られるわけではありません。
それを踏まえてか、このルーターではAOSSをオンにしているとSSIDのステルス化もできません。

というわけで、セキュリティレベルを上げようと思うなら「暗号化キーをいかに複雑にするか」に全てが掛かってくることが判明しました。

(ここから延々解説)WPAの「暗号化キー」とは?

そもそも暗号化キーとは何でしょう……という話を始めると「そもそも暗号とは」という数学的な話に入ってしまうので、ここでは暗号化するための「鍵」、という文字通りの意味で納得したことにします。
暗号について詳しく学びたい方は『新版暗号技術入門 秘密の国のアリス』(結城 浩/ソフトバンククリエイティブ )(アマゾンで購入)などが評判良いようです。

さて、鍵は長ければ長いほどがよく、できるだけいろんな文字(記号とか)を使ったほうがいいと言われています。では、
(1)鍵の長さは最大で何文字までで、
(2)鍵にはどんな文字が使える
のでしょう?

このルーターのヘルプを見ると、「8~63文字の半角英数字記号が指定できます」とあります。
最大は63文字らしいことが分かりましたが、「英数字記号」の「英数字」はともかく「記号」とは何でしょう
使っていい記号と使ってはいけない記号があるのでしょうか。ピリオドとかハイフンは何となく経験的に大丈夫そうですが、「\」(半角バックスラッシュというか円記号というか)はどうだろう、半角スペースは…スペースってそもそも「記号」に入るのだろうか? 等々、疑問が湧きます。

「WEPキー」のASCII文字列と16進数

ここでちょっと昔話。WEPの場合を回想してみます。
…「昔話」って今もWEPはあるんですけど? と思われるかもしれませんが、繰り返しますがセキュリティ的に使うべきでないとされているので、過去の話として進めます。

WEPキーの長さは、
「64bitの場合、ASCIIで5文字か16進数で10桁」
「128bitの場合、ASCIIで13文字か16進数で26桁」
でした。

WEPキーにおける「16進数」というのは、ASCII文字列を16進表記したものでした。そのため、設定可能なキーの種類はASCII文字列のほうが少ないことになります。
なぜならASCII文字列は全て16進で表記できますが、逆は成り立たないからです。例えば16進数で0x00とか0x7Fとか0x99といった値は、ASCII文字(厳密に言うと印字可能文字と呼ばれる0x20~0x7E)では表現できません。
そのため、WEPキーは「ASCII文字に含まれない値も使って16進で指定したほうが多少は安全」というような話もありました。今となっては五十歩百歩ですが。

さて、なぜそんな昔話をしたかというと、その「暗号化キーとしてASCII文字列を指定するのってイマイチ」という話は、WPAにも当てはまるのか? と思ったからです。
ルーターによっては16進数64桁での設定も可能な機種があるようですが、ウチのWZR-AGL300NHは違います。ASCIIでしか指定できないというのはセキュリティ的にはどうなのでしょう?

だいたい、暗号化キーの長さが「8~63文字」という可変長なのはどういうわけでしょう?
ASCIIの1文字が8bit分ですから、「8~63文字」だと「64bit~504bit」?
鍵長がそんな自由なはずはないですよね……?

WPAの暗号鍵はハッシュ関数で生成される

困ったときのWikipedia。WPA(Wi-Fi_Protected_Access)の項によると、ハッシュ関数で256bitの鍵を生成しているそうです。
なーんだ! 道理で暗号化キーがたった8文字でも許されるわけです。

そして、これまで「暗号化キー」と呼んできたものは実は暗号の鍵そのものではなく、「鍵をハッシュ関数で生成するための材料」だったようです。それもあって「パスフレーズ」と呼んでいるのですね。

ちなみに16進数64桁での入力が可能な機種の場合は、ハッシュ関数を通さず、それをそのまま暗号鍵として使うとのこと。
16進数なら1桁4bitですから、確かに64桁でちょうど256bitです。

なお、いまさっき「パスフレーズは8文字でも許される」と書きましたが、それはあくまで仕様上の話。
現実的には、総当たり攻撃などを考えると、安心なのは「13文字以上」とWikipediaにあります。

複数人で共用する無線LANの場合、パスフレーズをMAXの63文字(しかも記号とか含んだランダム文字列)にするのは運用上、現実的ではないかもしれません。
13文字くらいなら何とかなりそうですが、人間的に意味のない文字記号63個の羅列だと、入力ミスが頻発します。あげく、情報システム管理者はしょっちゅう「無線LANの設定ができない! ルーター落ちてんじゃないの?」という問い合わせに「いえ落ちてません、暗号化キーの入力間違いなのでもう一度よーく確認して入力してください」「もう3回も確認したよ!」「でもルーターは安定稼働中ですし…」というお決まりのやり取りを繰り返させられるのだろうと想像できます。
63文字と言わず、20文字程度でも職場で本当によく見る光景です。

しかし今回は「放置するネットワークのために利便性とか無視で最強レベルのパスフレーズを考える」という目的ですので、長さは当然、MAXの63文字で決定です。

パスフレーズが十分に長ければ、SSIDは何でもいい?

ちょっと脱線しますが、暗号鍵を生成するためのハッシュ関数には、このパスフレーズに加えてSSIDも与えます
パスフレーズが十分に長ければ、SSIDはどうせAOSSをオンにしている限りステルスにはできないし、適当に付けておけばいいや……と考えるかもしれません。

ところが。
世の中には「Church of Wifi WPA-PSK Rainbow Tables」というものがあります。
ありがちなSSIDの上位1000件それぞれについて、ありがちなパスフレーズ約100万件を使った場合の暗号鍵がすべて収録されているデータベースだそうです。
このDBに収録されているSSIDとパスフレーズの組み合わせを使っている無線LANは、セキュリティ的に少々不安があります。

なお、この「パスフレーズランダム生成ツール」は、今のところ「ありがちなパスフレーズ約100万件」に収録されているありがちなパスフレーズは生成しません
100万件をダウンロードして確認しましたが(正確には996,358件でした)、その中には長さが63文字のパスフレーズは1つも含まれていなかったからです。(目視じゃなくて、カウントするスクリプトを書いて確認しました。いちばん長かったのは41,372件目の“No_Lord_Shall_Stand_Before_Myself,I_am_Deicide”で44文字)
そのため、現状、この「ありがちなSSID」に収録されているSSIDを使ったとしても、ありがちな暗号鍵は生成されません。

ただし、今後もしこのデータベースが拡張され、63文字のパスフレーズが収録された場合、そのパスフレーズが当ツールでランダム生成される(または過去に生成されていた)可能性はゼロではありません。
(ランダムに生成された63文字のパスフレーズが偶然に収録される確率は天文学的と言っていいほど低いと思いますが……)

とはいえ「そんなデータベースにSSIDが収録されていると気持ち悪い」という方は、よくあるSSIDのリストで確認してみてもよいかと。なお、海外の統計のため日本ではおなじみの「YBBナントカ」などは含まれていません。

【追記】
なお、「ありがちなSSID」として収録されるのを避けるため、ユニークな値を含めばいいかとMACアドレスを入れる――というのは逆に危険な行為です。
参考:Windowsの無線LANが放送するSSIDからPlaceEngineで自宅の場所を特定される恐れ(高木先生の記事)
【追記ここまで】

パスフレーズに利用可能な文字種は95文字のはずだけど…?

閑話休題。パスフレーズの最大長は63文字と判明したので、次はもう1つの問題、「一口に“記号”と言っても具体的にはどんな記号が使えるのか」です。

前掲のWikipediaにはサラリと「総当り攻撃への対策としては、13文字のランダムなパスフレーズ(文字種は95文字)で十分と言われている」という記述がありました。ASCIIで95文字ということは、0x20~0x7E (正規表現で書くと /[ -~]/ )でしょうか?
日本語版のWikipediaを読んでもよく分かりませんでしたが、英語版WikipediaのWPAの項 を見たら次のように明記されていました。

Each character in the pass-phrase must have an encoding in the range of 32 to 126 (decimal), inclusive. (IEEE Std. 802.11i-2004, Annex H.4.1)
The space character is included in this range.

というわけで、ピリオドやハイフンはおろか、< も " も \ も、半角スペースさえ使っていいことが分かりました。他方、0x00(NULL)や0x7F(DEL)などは使えないようです。ASCIIに含まれない半角カタカナ(JIS X 0201)などもNG。
もっとも、ハッシュ関数に通す以上、WEPキーのときと違って「非ASCII印字可能文字」を敢えて使うべき理由もないです。

これで文字種の問題も解決……と思いきや、実際に記号類をふんだんに使ったパスフレーズを無線ルーターに設定しようとすると、「その文字は使えません」的なエラーが出ることが分かりました。
ルーターによっては、必ずしも95文字種すべてを利用可能とはいかないようです。

ルーターによって使用可能文字は異なる

たとえば私が買った機種、バッファローのWZR-AGL300NHでは「スペースは使えない」と明記されています。その他の記号類は何が使えて何が使えないのか、説明はありませんでした(全部使えるのかも)。
他メーカーでは、たとえばNEC機種のマニュアルによると、半角スペースの他に「?」がなぜか使えないようです。
coregaのとある機種のマニュアルによれば、半角スペースの他に「=」「~」「`」(バッククォート)の3文字が使えないようです。

というわけで、当ランダムパスフレーズ生成ツールでは、「特定の文字を含まずにパスフレーズを生成する」という指定ができるようにしました。
とりあえず、いま出てきた5文字を除外した90字種から生成するようにしています。お使いの無線ルーターの仕様に合わせて自由に足し引きしてください。

なお、できる限り、63文字長のパスフレーズ中に「英大文字」「英小文字」「数字」「その他(記号)」の4グループすべてを必ず含むものを生成しようとします。
ただ、完全ランダムだと、偶然「77777(中略)777」という「7」が63個連続するだけの、運は良さそうですがセキュリティ面ではよろしくなさそうなパスフレーズが生成される確率も、天文学的な確率とは言えゼロではありません。
そういうイマイチなパスフレーズが運悪く(運良く?)生成された場合は、破棄して自動的に再生成します。

ただし、除外文字が多ければ多いほど、どれだけ繰り返しても「4グループすべてを含むパスフレーズ」を生成できない確率が上がります。数百回繰り返しても生成できなかった場合は、「4グループすべてを含んでいるわけではない」という但し書きとともに、セキュリティ的には最高レベルとは言えないパスフレーズを表示します。
「いつまで経っても生成処理が終わらない」というループを防ぐための措置ですので、ご了承ください。

暗号鍵として16進数64桁を直接設定できるルーターもあるけれど

ところで、「無線ルーターによっては、パスフレーズの代わりに64桁の16進数で256bitの暗号鍵を直接設定できる場合もある」と先に書きました。
「除外文字とかややこしいことを考えなくても、ランダムで64桁の16進数を出力するツールを作ればいいのに」と思われた方もいるかもしれません。
そうしなかった理由は単純で、ウチのWZR-AGL300NHは16進入力に対応していなかったからです……。

もっとも、上述のようにWEPキーの時と違って、「ASCII文字でのパスフレーズ入力」が「16進数入力」と比べてセキュリティ的に劣るわけではないということが分かったのは大きな収穫でした。

実は「16進でランダムに“暗号化キー”を生成する機能」はあらかじめこのルーター(Web管理画面)に一応備わっているのですが、実行すると「0~Fの16文字種しか使わない63桁のパスフレーズ」が生成されるのです。それはちょっと……。

それでも、ついでなので16進数版も作っておきました。
「ウチのルーターは、ASCII入力だと英数字のみで記号を受け付けないので不安」という方はこちらをお使いください。


(※WPA/WPA2-PSK)


こちらは解説するよりJavaScriptのソースを見ていただいたほうが早いかもしれませんが、単に16面体のサイコロを64回振るだけです。(サイコロと違って疑似乱数ですが)

まとめ

これでとりあえず、ウチのルーターWZR-AGL300NHが使いもしないTKIPの電波を吹きっぱなしでも、セキュリティ的に人事を尽くせた感があり、まあそれなりに安心して放置できそうです。
ただ、常用するAESのほうは覚えやすいパスフレーズを設定してあるので、「パスフレーズ的には常用するネットワークのほうがセキュリティレベルが低い」という逆転現象が起きています。いささか複雑な気分です。

5 件のコメント:

  1. Math.random()は暗号学的に安全ではない(十分な乱雑性が存在しない)
    ので、このコードで生成した鍵には十分な乱雑性がないです。

    返信削除
  2. コメントありがとうございます。ご指摘のとおりでして、実際に文中に下記のように注意書きをしています。
    “「ランダム」と言ってもJavaScriptのMath.random()の疑似乱数をそのまま使っています。どのくらい疑似なのかはブラウザ(JavaScriptエンジン)の実装に依存します”

    厳密には「暗号学的に安全なMath.random()を実現したECMAScript実装」というものが存在すれば、いちおう十分に乱雑性のある鍵が生成できるのではと思います(屁理屈ですが…)。

    現実的には、本文中で説明しておりますとおり、英大文字/英子文字/数字/記号の4種が必ず入っているかどうかのチェックはしていますので、ブルートフォース対策としては、最低限の強度は持っているかと…という点、ご理解いただければ幸いです。

    返信削除
  3. 多く見積もってseedの乱雑性が64bitあったとしても、
    WPA2が使用する鍵長256bit?には到底届かないので、
    十分な強度がないということです。

    ほかの表現すると、このコードで生成される"最強の鍵"は、
    見た目が最強なだけで、総当する側から考えると
    わずか64bit分調べれば十分ということが弱さの本質です。

    返信削除
  4. ご指摘の点(やっと)理解いたしました。再度のご説明、恐縮です。
    64bitごと(おそらくはそれより短い周期)でseedを作り直す必要があるのですね。と思ったらECMAScriptにはsrand()がないようです。一工夫必要になりますね…。
    勉強になりました、ありがとうございます。

    返信削除
  5. こんにちは

    無線LANのWPA/WPA2-PSKをGPUで超高速解析してパスワードを見つけるフリーのオープンソースソフト「Pyrit」
    http://gigazine.net/news/20110707_pyrit/

    というGIGAZINEのニュースを見て「はて,無線LANの鍵は何ビットかな?」と思ってググっていたら,こちらの記事を見つけました.
    パスフレーズをKDFに通して256bitの鍵を生成していたんですね.
    勉強になりました.

    GIGAZINEでは,こちらで紹介されてるデータベースをさらに強力にしたようなツールが取り上げられていました.
    でも,鍵長が256bitあれば,毎秒10万通りの鍵候補を生成できたとしても,全数探索には2^256/10万≒10^77/10^5=10^72秒ほどかかることになりますね.
    けっきょく十分な強さのパスフレーズさえ使っていれば大丈夫ということかー.一安心.

    返信削除