谷本 心 in せろ部屋

はてなダイアリーから引っ越してきました

セキュアなオンラインテキストエディタのための暗号化

世の中にオンラインのテキストエディタはいくつかあるけど、
「IDやパスワードなど、機密性の高いテキストを保存する」
なんて用途に使おうなんて、ちょっと思えない。


この危険極まりない行為を、いかにすれば安全にできるか、ちょっと考えてみたい。


ただし、立場はあくまでWebアプリケーション開発者だとして、
アプリケーション設計/実装の方法を考えるだけであって
プロトコルの設計なんかは考えないことにする。

SSLによる通信経路の暗号化

まず真っ先に思いつくのが、SSLの利用。
通信経路を暗号化していれば、たとえ通信を盗聴されても
データ本文が暴露することはない。


しかし、SSLはあくまで通信経路を暗号化するだけである。


サーバ側にデータが平文で保存してしまえば
サービス提供者やサーバ管理者がソノ気になれば、
あるいは、セキュリティホールがあってデータが流出すれば
あっという間に本文が暴露してしまう。


だから、サーバ側のデータを暗号化することは必須である。

サーバに保存するデータの暗号化

サーバ上のデータは、当然ながら復号可能な暗号化をする必要があるため、
秘密鍵方式でも公開鍵方式でも)復号鍵は、どこかに保存する必要がある。


では、この復号鍵はどこに保存すべきだろうか。

 a) アプリケーション本体
 b) ブラウザのクッキー
 c) ブラウザのローカルストレージ
 d) 毎回ユーザが入力する


aのように、復号鍵をアプリケーション本体(あるいは設定ファイル)に保存する、
という方式をとるアプリケーションも多いが、それは危険である。
やはりサービス提供者やサーバ管理者の悪意、あるいはセキュリティホールによって
復号鍵そのものが流出してしまう可能性があるからだ。


また、bのクッキーに保存するというアイデアも、同様に危険だ。
クッキーも通信経路に乗ってしまう以上、アプリケーションの作り方次第で
クッキーに乗った復号鍵が流出してしまう可能性がある。


そうすると、考えられるのはcかdである。


クライアントの安全性が保たれている以上は、
cのブラウザのローカルストレージが安全である。
最近のブラウザや、HTML5対応ブラウザであれば、
ローカルストレージが利用できるはずである。


それすら危うい場合には、面倒だがdのように
ユーザに毎回、復号鍵を手入力をしてもらう必要がある。
cにするかdにするかを、ユーザが(あるいはブラウザ環境により)
選択できるようにするのも良いだろう。


つまり、暗号化と復号は常にクライアント側(ブラウザ上のJavaScript)で行い、
通信経路とサーバ側のデータには、常に暗号化されたデータを流す、ということだ。


ここまで決まれば、もう問題は解決、、、と言いたい所だけど
もう一つ、考えるべきことがある。


それは「正しい鍵であることを、誰が、どうやって判定するか」である。

正しい復号鍵の判別(ユーザによる判定)

入力した鍵が正しいかどうか、
アプリケーションが判断すべきか、それともユーザ自身が判断すべきか。


ユーザ自身が判定する場合、もし入力した(あるいは保存されていた)復号鍵が間違っていたら、
恐らく意図しないデータが表示されるだろうから、復号鍵を入力し直せば良い。


具体的な流れで言うと、、、

 a → (暗号化) → xxx
 xxx → (誤った復号鍵を入力) → b
 xxx →(正しい復号鍵を再入力)→ a


これで一見問題はないように見える。
ただ、怖いのは、誤った復号をした後に
ウッカリ「文字化けしたデータ」をサーバに保存してしまうことだ。


つまり、、、

 a → (暗号化) → xxx
 xxx → (誤った復号鍵を入力) → b
 b → (暗号化) → yyy


こうして、サーバに「yyy」が保存されてしまうと
「xxx」や「a」に相当するデータには、二度と戻せない。


「yyy」から「b」を復号することは出来ても
「b」から「xxx」を導出することはできないためだ。


このウッカリを防ぐためには、復号した時点で
「正しく復号できましたか?」とダイアログなどで確認すれば良いものの、
それはそれで面倒である。


やはり鍵が正しいかどうかは、アプリケーションに判定してもらいたい。

正しい復号鍵の判別(アプリケーションによる判定)

アプリケーションで判定するためには、復号鍵とは別のフレーズを使えば良い。


フレーズを暗号化したものをサーバ側(もしくはアプリケーション内)に保存しておき
ユーザは復号鍵とフレーズを入力する。


そしてサーバ側に保存したデータを、復号鍵で復号し、
それがフレーズと一致するかどうかをアプリケーションが判定すれば良い。


たとえば、フレーズを「a」だとすると、、、

 a → (暗号化) → xxx(サーバに保存)
 xxx → (誤った復号鍵を入力) → b → エラー!
 xxx →(正しい復号鍵を再入力)→ a → OK!


これで問題はないように見える。
ただ、こうした自動判定の問題点として、自動である以上、
ブルートフォースアタックを受けやすいということが挙げられる。


サーバに保存したデータ(上の例で言えば「xxx」)が暴露した場合に、
復号鍵とフレーズの組み合わせをコンピュータで延々と試すことで
いつか、復号鍵とフレーズの組み合わせが暴露してしまう可能性がある。


同じことは上の「ユーザによる判定」でも言えるのだが
復号された値を「ユーザ自身の目」で正しいかどうかを判定する必要がある以上、
有限時間内で確認することは、より難しくなる。


、、、とは言え、この辺りは便利さとセキュリティのトレードオフ
やはり自動で判定できることで、ユーザの面倒さはグッと低下する。

正しい復号鍵の判別(復号鍵とフレーズ)

ちなみに、上では「復号鍵とは別のフレーズ」を使うと書いたが、
実はフレーズが復号鍵そのものであっても、大きな問題にはならない。

暗号文 == fuction(復号鍵, フレーズ)
暗号文 == fuction(復号鍵)


この二つを比べれば、フレーズが入っている分、前者の方が強固に見えるが
実はあくまでも「復号鍵の文字数 + フレーズの文字数」の組み合わせでしかない。


たとえば復号鍵が10文字、フレーズが10文字の合計20文字にすることと、
復号鍵を20文字にすることは、強固さから言えば全く同じである。


変に復号鍵とフレーズを分けて入力させるるよりも、
十分に強固な復号鍵だけを入力させた方が、ユーザ自身も覚えやすいだろう。

まとめ

延々と書いてきたので、最後にまとめ。


1. 暗号化と復号はクライアントで行う
2. サーバ側や通信経路には、暗号化したデータしか流さない
3. 復号鍵はクライアントのみで保持する or 入力する
4. 復号鍵を暗号化したものをサーバに保持しておけば自動チェックができる


これを踏まえて、セキュアなオンラインテキストエディタを作れば
安心して、IDとパスワードを保存することができるはず。


どうでしょうか。
「ここが危ないよ!」みたいな意見があれば、ぜひお願いします m(_ _)m