リスト型攻撃に負けないログイン認証を考える

2016年ニジボックスAdventCalendar 14日目の記事です

## はじめに
みなさんのサービスは安全ですか？
みなさんのサービスはユーザにとって簡単ですか？
**他のサービスでIDパスワードが流出しても大丈夫ですか？**

この記事では、単体ではどんなにセキュアなサービスでも対策をしてないとコロっとヤられてしまう
リスト型攻撃に負けないログイン認証を考えていきたいと思います。

## リスト型攻撃とは

悪意を持つ第三者が、何らかの形で入手した「ID」と「パスワード」の組み合わせのリストで不正なログインを試みる攻撃です。
ユーザは様々なWebサイトで同じIDとパスワードを使いまわしていることが多く、リスト型攻撃の成功率は極めて高いです。

リスト型攻撃の何が怖いのかというと、
正しいIDと正しいパスワードの組み合わせでログインされるため、
**サイト側からは不正ログインであることが非常にわかりにくい点**でしょう。

ところで、我々のどこか与り知らぬところでIDとパスワードが流出し、
正しいIDとパスワードでログインされたんだったら、
我々のサービスには責任なんてないんじゃん？そこまで面倒みなくていいんじゃん？

なんて考えているエンジニアの方は、今すぐエンジニアを卒業してください。どうぞ。


## リスト型攻撃の対策を考える前に

リスト型攻撃対策のためにパスワードを使いまわさないように注意喚起する。
という対策をよく目にします。
これは実に効果的で、パスワードの使い回しがなければ、
実質リスト型攻撃の被害に遭うことはありません。対策完了です！やったね！

・・・とは言えず、どんなに注意喚起しようとも多数のユーザがパスワードを使いまわしています。
だって面倒だもん。しょうがないね。


## ログインIDについて考える

ちょっとログインIDにフォーカスしてみましょう。
よく見るログインIDとして以下の３つを例に挙げてみます。

- ユーザのメールアドレス
- ユーザが任意に決めた文字（数字）の列
- サービス側で任意に決めた文字（数字）の列

### ユーザのメールアドレス
まずユーザのメールアドレスですが、これは非常に危険なログインIDだと言えます。
メールアドレスは、個人情報でありながらも比較的収集が容易な情報で、
スパムメールなどの悪い利用用途も多いため、世の中に数多く出回っています。

リスト型攻撃からは少しずれますが、例えばメールアドレスのリストに対して
「asdfghjk」というパスワードでのログインを試みるような、逆総当たり攻撃の被害にあう恐れもあります。

またメールアカウント自体のパスワードと同じパスワードが使われていることも多く、
考えたくないことですが、自サービスからの流出があった場合に、
メールアカウント自体が乗っ取られてしまうリスクまでついてきます。

他にも様々なリスクがありますが、まとめているとキリがないので、
当記事では一旦、ログインIDをメールアドレスと同一にすることはやめましょう。
という結論を置いておきましょう。

### ユーザが任意に決めた文字(数字)の列
次にユーザが任意に決めた文字列ですが、一般的によく使われているものだと思います。
何も対策をしていない場合、リスト型攻撃に対する防御力はゼロです。
そのため対策を考えてみると

- **IDもしくはパスワードを超長くする** 
例えばパスワードのルールを256文字以上必須にしてみましょう。
リスト型攻撃及び、総当たり攻撃では突破困難になります。
代わりにサービスの利用者が誰もいなくなるという諸刃の剣です。私はお断りです。  

- **同一ホストから一定時間内の認証失敗回数が一定数を超えた場合、アクセスできないようにする（もしくはアラートを飛ばす）** 
この対策は一定の効果は期待できます。
ただし、複数の踏み台を使いまわしているケースや、ホストが偽装されているケースが想定されるため、
これだけでは大きな効果は見込めないかもしれません。  

- **ホスト問わず、一定時間内のサービス全体での認証失敗回数が一定数を超えた場合、ログイン機能を止める（もしくはアラートを飛ばす）**
上記の対策を広げたもので、閾値次第では大きな効果が見込める可能性があります。
ログイン機能を止めてしまう場合はサービス自体が利用できなくなるため、多くのユーザに影響が出ますが、
不正アクセスからユーザを守るためにはそこまで踏み切ってしまうことも手だと思います。
また、アラートを飛ばすだけでも不正アクセスの早期発見につながりますのでそちらは無条件でオススメです。  

- **普段使っていない端末やIPからのログイン時にユーザにアラートメールを飛ばす**
こちらも早期発見には効果が期待できます。Twitter等で実装されてますね。

上記の対策はそれぞれ一定の効果はあるものの、根本的な対策にはならなかったり、
対策を強固にすればするほどユーザビリティが悪くなってしまいます。

### サービス側で任意に決めた文字(数字)の列
最後に、サービスが任意に決めた文字列の場合はどうでしょうか？
これはリスト型攻撃の対策だけにフォーカスすると、しっかりと対策できているように思えます。
また自サービスで万が一流出があった場合でも、他サービスへの影響をかなり減らすことができるでしょう。
ただし、ユーザビリティという点にやや課題があります。
私も利用しているサービスの中に、ランダムな10桁のIDでログインするサービスがあるのですが、
正直ログインIDを忘れます。だって10桁ですよ？覚えられないじゃん。
なので毎回登録時のメールを確認してログインしています。
（パスワード管理ツール使えよって思ったあなた、おっしゃる通りでございます）


## リスト型攻撃に負けないように
悔い、改めまして・・・
前項の内容をヒントに、できるだけユーザビリティを損なわずに対策してみましょう。

まずログインIDですが、全てをランダムな文字列にしてしまうと、ユーザ側としてはちょっと使いづらいです。
かと言って任意な文字列のログインIDですとリスト型攻撃に負けちゃいそうになります。
（メールアドレス＝ログインIDは論外です）

ここは間をとって、「ユーザが任意に決めた文字列」＋「サービスが決めた短い文字列」
なんてのはどうでしょう？

例えば「gatayama」と「nbx」から「gatayama@nbx」のようなログインIDにしてみます。
もちろん「nbx」の部分はランダムにするかある程度のバリエーションをもたせてください。

文字数が少ないので、なんだか覚えられる気がしてきました。
だってnbxを覚えるだけでいいんです。

また、サービス側で決める部分に関しては、自サービスでしか使われていないため
リスト型攻撃の対策としても優秀です。

少なくとも
「パスワードの使い回しはやめて！」と注意喚起するだけだったり、
「うちではパスワードは32文字以上が必須だから。覚えられない？パスワード管理ツール使ってね。」
とするよりは効果的だと思います。


## まとめ
単体でどんなにセキュアなサービスだったとしても、正常なIDとパスワードでログインされるリスト型攻撃には、別観点での対策が必要となる。

ユーザが決めた文字列＋サービスが決めた文字列をログインIDにすることで、最低限のユーザビリティを担保しつつリスト型攻撃の対策を行うことができる。

複数の対策や検知の仕組みを作ることで、より安全なWebサービスにすることができ、また、今更ログインIDを変更することが困難なサービスでも一定の対策を行うことができる。


## おわりに

セキュリティとユーザビリティは、一部トレードオフな側面があります。
それでも、サービスを使ってくれるユーザが安全で便利にサービスを使えるように、
また、何か良くないことが起こってしまった時でも、
自分たちやユーザ、そして第三者が困ることのないようなサービスづくりを心掛けていきましょう。