フロントエンドエンジニア N による社内技術記事を本人の許可を得て公開します(一部社外向けに書き換え)。
TL;DR
不正ログイン試行を防ぐため、ソーシャルPLUS のメッセージマネージャーに使われているログインシステムへ、reCAPTCHA v3 を導入したら、意外と大変だった話。
reCAPTCHA 導入のきっかけ
機械的な不正ログイン試行を防ぎたい
ソーシャルPLUS で提供する「ソーシャルログイン」とは別に、管理画面などで利用する内部のログインシステムがある。
処理性能改善やリファクタリングを行い、次は辞書攻撃やリスト攻撃による連続的な試行への対策を行いたいと考えた。
フリクション 1 が少ない reCAPTCHA v3 を試したい
reCAPTCHA v3 をご紹介します。Bot の活動を阻止する新しい方法
もともと、ユーザーに「私はロボットではありません」とチェックを入れさせる reCAPTCHA v2 が Google から提供されていた。が、運悪くボットと判断される環境からアクセスすると、ボットの可能性を排除するため、引き続き、以下のような画像クイズに回答する必要がある。
フリクションは必ずしもUXに悪影響を与える訳ではない 2 とは言いますが、この画像クイズは例外だと思われます。ログインの度にこのような作業をやっていては、運用に支障が出る可能性もある。
これに対して、新しい reCAPTCHA v3 は、ユーザーに何か作業を行わせることはなく、アクセスに対する人間らしさのスコア値 (0.0 〜 1.0) を返すだけ。
returns a score for each request without user friction.
これをうまく利用すれば、ユーザーに苦行を負わせることなく、不正アクセスの防止が可能なのではないか。
以下、reCATCHA v3 を採用した理由をまとめると、
- 無料
- ユーザーに求める余計なアクションがない
- 簡単なアクセス解析やボットスコア分布が見れる
守られている感を醸し出せる
reCAPTCHA v3 を設置すると、右下に以下のようなバナーが自動的につく。
既存ユーザーにフリクションを感じさせないログインを目指したい
reCAPTCHA 導入の設計方針としては、疑わしいアクセスについては弾きたいが、既存ユーザーにフリクションを感じさせず、運用を支障がでないように今までどおりにログインさせたい。
すったもんだは省いて、試行錯誤の末、なんとかまとまった大まかな画面構成は以下。
安全と思われるユーザーに対しては
ボットの可能性が少ない、または最近ログインに成功したユーザーには、ユーザーID とパスワード入力だけのシンプルなログイン画面を表示。
違いがあるとすれば、右下に表示される reCAPTCHA のマークのみ。
ボットと思われるユーザーに対しては
reCAPTCHA v3 スコアに基づき、通常のログインフォームに「ロボットではありません」チェックを表示させる。
チェック時、ボットらしい要素が確認できれば、更に追加で画像クイズを出題。
それでも、人間性が確認できなければ
チェックを入れなかったり、クイズの回答がいい加減だったりした場合は、 ログイン押下後に、その旨を表示する。
以下、試行錯誤の過程
主に、開発者向けの詳細資料
まずはシンプルに reCAPTCHA v3 導入した、が
まず、reCAPTCHA v3 ドキュメントを参照しつつ、シンプルな導入を行った。
一般的な CSRF トークンの検証フローにスコア値が付いたようなもので、特に難しいことはない。が、
問題は、人間スコアが低かったときのフロー
reCAPTCHA v3 ドキュメント によると、スコアしきい値を下回ったアクセスの面倒見てくれるわけではなく、こちらで処遇を決める必要があるらしい。以下、抜粋
Use case Recommendation login With low scores, require 2-factor-authentication or email verification to prevent credential stuffing attacks
誤検知対策としての reCAPTCHA v2
二段階認証やメール認証だと、実装や運用が大掛かりになってしまうので、v3 が誤検知したときのフェイルセーフとして、上のほうでディスりました reCAPTCHA v2 を導入することに。(v3 と同じ API やロジックを使うことが多く、併用しやすかったため)
以下、人間スコアが低かったときのフローから再開。
↑の通り、ユーザーが最低限行わなければならないアクションが増えてしまうが、あくまでも reCAPTCHA v3 が誤検知したときにしか作動しないので大丈夫だろう。と、この時点では思っていた。
reCAPTCHA v3 が開発メンバーをボットと判断する事例が多発
同じ IP アドレスから何度もログイン動作確認を行っているのが良くなかったのか、ある時期を境にログイン後に誤検知が多発して、以下のようなエラー表示しかでなくなった。
reCAPTCHA v3 スコア指標がわからない
reCAPTCHA v3 はトラフィックを傾向をもとにスコアを算出する仕組みがあるようで、この条件ならスコアが良い/悪いということが、一概にいえないのが難しい。
一応、スコアが落ちやすいアクセスに、以下のような傾向はあった。
- Selenium / Puppeteer など
- ほぼ
0.1
のボット扱い - 導入 1 日目は様子見期間なのか、スコアは落ちず
- ほぼ
- 何度もログイン試行した場合
- 開発者メンバー誤検知がこれに当たる?
- VM 環境 (BrowserStack など)
- もしくは、特定の IP アドレス IP レンジからのアクセス
0.3 ~ 0.7
と微妙なスコアが多かった
- UA がボットっぽい
- Chrome devtools の
Network > Network conditions > User agent
から Googlebot を選んだりすると、スコアが落ちる - ただし、後日確かめると最高点を出したりして、一概には言えず
- Chrome devtools の
- シークレットブラウザ
- 外部記事に落ちたという事例あり
- こちらが試す分には、意外とスコアが落ちなかった
とはいえ、環境により低かったり低くなかったりすることがあり、正確な判別アルゴリズムは神のみぞ知る。
非常に嫌な予感がしたため、急遽以下のような仕組みを実装することに。
最近ログイン成功したユーザーに対しては reCAPTCHA 検証を行わないように
想定する最悪なパターンの1つとして、日々、管理画面を運用してくれているお客様に対して、毎朝画像クイズを出題して業務を滞らせること。
そもそも、一度ログインに成功したらユーザーに対しては、ボット検証をする意味はほぼないので、ログイン成功時に reCAPTCHA 検証を免除できる権利 (トークン) 3 をブラウザに付加することに。
具体的なフローチャート
以下の三段階の検証を段階的に設けることで、既存ユーザーに優しく、怪しいユーザーには厳しい検証を目指した。
- ① reCAPTCHA 免除検証
- ログインシステム管理、ログイン成功済みのユーザーのみがトークンを所有
- ② reCAPTCHA v3 検証
- Google 管理
- ③ reCAPTCHA v2 検証
- Google 管理
既存ユーザーへフリクションを意識させないことにこだわりすぎて、非常に複雑なロジックになってしまった苦肉のスパゲティ。もっとシンプルにすべく、現在改良中。
まとめ
- ログインシステムにて、ユーザーフリクションを減らした reCAPTCHA を導入した
- ログインで reCAPTCHA v3 を使う場合、誤検知ありきの設計が必要
- reCAPTCHA のインタフェースは簡単だが、うまく使いこなすのは難しい
今後の課題
- もう少しロジックを簡略化する
- 独自実装周りのセキュリティ考慮
- reCAPTCHA v3 のスコアを安定させる
付録
reCAPTCHA 他社導入事例や、ログイン以外の使いみち
ログインへの組み込みだと、最近 mixi が導入事例の記事を上げていた。
ログイン以外でも、以下のような使いみちをよく見かける。
- スパムコメントの防止
- 機械的な会員登録の防止
さらにユーザーのアクションが不要な reCAPTCHA v3 を使えば、以下のような自動化的な応用も可能
- アクセス解析からボットユーザーらしきものを仕分ける
reCAPTCHA 周りの技術論文
Hacking Google reCAPTCHA v3 using Reinforcement Learning
ウェブページの一部を 100x100 のグリッドと見立て、マウスの位置 (x, y) を状態として、始点からの終点 (v3 action 発火?) まで動き {上、左、右、下} をアクション4としたマルコフ決定過程による強化学習で、reCAPTCHA v3 突破を試みている論文。
突破の発想も面白いのですが、reCAPTCHA v3 は様々な状況を元にボット判別していることが伺える。
Most previous works (e.g [4]) used the browser automation software Selenium [9] to simulate interactions with the reCAPTCHA system. At the beginning, we adopted the same approach but we observed that the reCAPTCHA system always returned low scores suggesting that the browser was detected as fake. After investigating the headers of the HTTP queries, we found an automated header in the webdriver and some additional variables that are not defined in a normal browser, indicating that the browser is controlled by a script.
Selenium によるアクセスは、自動で付加される HTTP ヘッダーを見ているらしく、常に低いスコアを返した。とのこと。
Another attempt to use Tor [11] to change the IP address did not pass the reCAPTCHA test and resulted in low scores (i.e 0.3). It is possible that the reCAPTCHA system uses an API services such as ExoneraTor [12] to determine if the IP address is part of the Tor network or not on a specific date.
Tor による IP アドレスの変更は低いスコア (0.3) を返したとのこと。ExoneraTor のような API サービスを使い、そのようなアクセスを判別しているのかも。
We also discovered that simulations running on a browser with a connected Google account receive higher scores compared when no Google account is associated to the browser.
Googleアカウントがブラウザに関連付けられているシミュレーションでは、そうでない場合と比較してスコアが高くなることがわかった。とのこと。
リモート IP アドレスも考慮していたりと、ある程度強化学習されにくい (連続的な試行がされにくい) 仕組みも備わっているとは思うので、実運用サイトで容易に突破するのはまだ難しいのかも。今後の reCAPTCHA v3 に期待。