BrowserへPasswordを保存するのは危険

セキュリティー
Gerd AltmannによるPixabayからの画像

Firefox NSSのBuildをした目的とは?

過去4回にわたりFirefox NSSをWindows上でBuildしてきたわけですが、その目的はデバッグでした。

先日、FirefoxのNSS(Network Security Services)を使ったちょっとしたコードを書いたのですが、一部の関数で時々エラーが発生して困っています。呼び出し元のコードの問題もありそうなのですが、NSSが返してくるエラーの内容もよくわからないため、デバッガーで調べようとしています。

Firefox NSSをWindows上でBuildする(1) | Day Dream (mankichiblog.com)

どういうコードを書いたのか、また結局デバッグをしてどうなったのかを書きたいと思います。

そもそものきっかけは下記の記事を読んだことにはじまります。

記事に書いてあることが本当なのか確かめるために実際にプログラムを作ってみました。

1番目に関してはChome(およびEdge)のログイン情報を読み出すことができました。

2番目に関しては記事のコードではエラーが発生してしまいログイン情報を読み出すことはできませんでした。ただし、EdgeはChromeベースですので1番目の方法でEdgeのログイン情報は読み出せました。

3番目に関してはログイン情報を読み出すことはできたのですが、エラーになってしまうケースとエラーにはならないがログイン情報が間違っている(文字化けする)が発生しました。そのためNSSが返してくるエラー含めてデバッガーで調べようと思ったわけです。

目的は果たせたのか?

結果デバッグをしても原因はわからず、入力文字列によって100%の再現性があることから入力に問題があるのだろうという結論になり、よくよく調査したところ下記のことがわかりました。

問題の原因

Firefoxのユーザー名およびパスワード情報は暗号化されたあとBase64でエンコードされてファイルに保存されています。そのため、まずBase64のデコードをしてからFirefox NSSの暗号化を解く関数であるPK11SDR_Decrypt()に文字列(実際にはByte列)を渡してあげる必要があります。

筆者はBase64のデコード結果をそのまま関数に渡すことをせず、いったん文字列に変換をして、再度それをByte 列に変換して関数に渡していました。これが間違いでした。

簡単に書くと下記のようなコードです。b64はもともとのBase64エンコードされた文字列です。

Byte[] b1 = Convert.FromBase64String(b64);

// re-create string from byte array
string s1 = Encoding.Unicode.GetString(b1);

// re-create byte array 
Byte[] b2 = Encoding.Unicode.GetBytes(s1);

b1とb2は同じになるはずだと思い込んでいたのですが、実際に同じにならないケースがあり、その条件によってエラーになる場合と文字化けになる場合にわかれていました。

PK11SDR_Decryptがエラーになるケース

b1とb2に書きのような差異が出ました。中間を省略していますが、最後の2バイトです。

b1=303A0410……..D183F8DB

b2=303A0410……..D183FDFF

PK11SDR_Decryptがエラーにならないが文字化けするケース

b1とb2に書きのような差異が出ました。途中省略していますが、中間の2バイトです。

b1=303A0410….F17347DD….07FB13EA

b2=303A0410….F173FDFF….07FB13EA

いろいろ調べていった結果これらはサロゲートペアにあたるコードであることがわかりました。前者がハイサロゲートコードですが、本来これに続いてくるべきローサロゲートコードがないために不正なコードと判断され、代替えコード xFFFDに置き換えられています。後者はハイサロゲートコードなしにいきなりローサロゲートコードがきているため同じく代替えコードに置き換えられています。ちょっとわかりづらいですが、リトルエンディアンなのでxFFFD は 表記上xFDFFになります。

サロゲートペア

サロゲートペア(代用対)は16ビットUnicodeの領域1024文字分を2つ使い(前半 U+D800 〜 U+DBFF、後半 U+DC00 〜 U+DFFF)、各々1個ずつからなるペアで1024 × 1024 = 1,048,576文字を表す。これはちょうど16面分であり、第1面〜第16面(U+010000 〜 U+10FFFF)の文字をこれで表すこととした。加えて第0面(基本多言語面)も使用可能なので、Unicodeには合計で 1,048,576 + 65,536 – 2,048 = 111万2,064文字分の空間が確保されたことになる。Unicodeの符号空間が10FFFF16まで(サロゲート領域を除いて111万2064文字)とされているのはUTF-16が表現可能な限界だからである。

サロゲートはUnicodeの符号位置の U+010000 〜 U+10FFFF の範囲を16ビットユニットのペア(2つ)で表現する集合で、最初の16ビットユニットを前半サロゲートもしくはハイサロゲート、二番目を後半サロゲートもしくはローサロゲートと称する。ハイサロゲートは U+D800 〜 U+DBFF の範囲、ローサロゲートは U+DC00 〜 U+DFFF の範囲である。

サロゲートペアはUTF-16でのみ使われ[11]、UTF-8、UTF-32ではすべての符号位置を符号化できるためこのような特別な処理は必要ない。

Unicode – Wikipedia

先のコードを書き換えてbase64をデコードしたバイト列を直接PK11SDR_Decryptに渡すようにしたらきちんと動くようになりました。

ブラウザーに保存されたログイン情報は簡単に読み出せてしまう

Firefox NSSをBuildする必要はまったくありませんでしが(汗)、記事にあったようにブラウザーに保存されたログイン情報はいとも簡単に読み出せてしまうことがわかり震撼しました。

なお、このプログラムの実行中にMcAfeeが反応して下記のようなポップアップを表示しました。

プログラムは実行を中断され、隔離されてしまいました。(ように表示されました。)

ところが、、、実際にはプログラムは残っており、実行も可能な状態でした。どういう条件かはわかりませんが、プログラム名がPasswordではなく別のもの(例:ConsoleApp)だと引っかからなかったり、ちょっとMcAfeeの挙動が怪しいです。

やっていることがやっていることなのでさすがにMcAfeeに苦情は入れにくいです。。

今回の結論

以上のことからブラウザーのログイン保存機能は使わないほうがよいと思い、パスワード管理ソフトを導入しようかと考え始めました。それもあり、本家のブログの方でパスワード管理ソフトに関する記事を書きました。

パスワード管理ってどうしてる?

どういう方法をとっても安全とは言い切れず、最後は紙が一番安全なのかなぁと。。

コメント

タイトルとURLをコピーしました