Feedforce Developer Blog

フィードフォース開発者ブログ

ユーザーアクションが不要な reCAPTCHA の導入 試行錯誤編

フロントエンドエンジニア N による社内技術記事を本人の許可を得て公開します(一部社外向けに書き換え)。

TL;DR

不正ログイン試行を防ぐため、ソーシャルPLUS のメッセージマネージャーに使われているログインシステムへ、reCAPTCHA v3 を導入したら、意外と大変だった話。

reCAPTCHA 導入のきっかけ

機械的な不正ログイン試行を防ぎたい

ソーシャルPLUS で提供する「ソーシャルログイン」とは別に、管理画面などで利用する内部のログインシステムがある。

処理性能改善やリファクタリングを行い、次は辞書攻撃やリスト攻撃による連続的な試行への対策を行いたいと考えた。

フリクション 1 が少ない reCAPTCHA v3 を試したい

reCAPTCHA v3 をご紹介します。Bot の活動を阻止する新しい方法

もともと、ユーザーに「私はロボットではありません」とチェックを入れさせる reCAPTCHA v2 が Google から提供されていた。が、運悪くボットと判断される環境からアクセスすると、ボットの可能性を排除するため、引き続き、以下のような画像クイズに回答する必要がある。

f:id:tech-feedforce:20190419143950g:plain
学びも達成感もない内職を永遠に繰り返す図

フリクションは必ずしもUXに悪影響を与える訳ではない 2 とは言いますが、この画像クイズは例外だと思われます。ログインの度にこのような作業をやっていては、運用に支障が出る可能性もある。

これに対して、新しい reCAPTCHA v3 は、ユーザーに何か作業を行わせることはなく、アクセスに対する人間らしさのスコア値 (0.0 〜 1.0) を返すだけ。

returns a score for each request without user friction.

これをうまく利用すれば、ユーザーに苦行を負わせることなく、不正アクセスの防止が可能なのではないか。

以下、reCATCHA v3 を採用した理由をまとめると、

  • 無料
  • ユーザーに求める余計なアクションがない
  • 簡単なアクセス解析やボットスコア分布が見れる

f:id:tech-feedforce:20190419162138p:plain
ログインシステムのボットアクセス解析の例

守られている感を醸し出せる

reCAPTCHA v3 を設置すると、右下に以下のようなバナーが自動的につく。

f:id:tech-feedforce:20190419144448p:plain
reCAPTCHA v3 のバナー表示

f:id:tech-feedforce:20190419144511p:plain
reCAPTCHA v3 バナーにマウスホバーしたとき

既存ユーザーにフリクションを感じさせないログインを目指したい

reCAPTCHA 導入の設計方針としては、疑わしいアクセスについては弾きたいが、既存ユーザーにフリクションを感じさせず、運用を支障がでないように今までどおりにログインさせたい。

すったもんだは省いて、試行錯誤の末、なんとかまとまった大まかな画面構成は以下。

安全と思われるユーザーに対しては

ボットの可能性が少ない、または最近ログインに成功したユーザーには、ユーザーID とパスワード入力だけのシンプルなログイン画面を表示。

f:id:tech-feedforce:20190419144656p:plain
ユーザーID と パスワード入力だけのシンプルなログイン画面

違いがあるとすれば、右下に表示される reCAPTCHA のマークのみ。

ボットと思われるユーザーに対しては

reCAPTCHA v3 スコアに基づき、通常のログインフォームに「ロボットではありません」チェックを表示させる。

f:id:tech-feedforce:20190419144757p:plain

チェック時、ボットらしい要素が確認できれば、更に追加で画像クイズを出題。

f:id:tech-feedforce:20190419144837g:plain

それでも、人間性が確認できなければ

チェックを入れなかったり、クイズの回答がいい加減だったりした場合は、 ログイン押下後に、その旨を表示する。

f:id:tech-feedforce:20190419145014p:plain

以下、試行錯誤の過程

主に、開発者向けの詳細資料

まずはシンプルに reCAPTCHA v3 導入した、が

まず、reCAPTCHA v3 ドキュメントを参照しつつ、シンプルな導入を行った。

f:id:tech-feedforce:20190419160143p:plain

一般的な 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 v2 Checkbox ドキュメント

以下、人間スコアが低かったときのフローから再開。

f:id:tech-feedforce:20190419160158p:plain

↑の通り、ユーザーが最低限行わなければならないアクションが増えてしまうが、あくまでも reCAPTCHA v3 が誤検知したときにしか作動しないので大丈夫だろう。と、この時点では思っていた。

reCAPTCHA v3 が開発メンバーをボットと判断する事例が多発

同じ IP アドレスから何度もログイン動作確認を行っているのが良くなかったのか、ある時期を境にログイン後に誤検知が多発して、以下のようなエラー表示しかでなくなった。

f:id:tech-feedforce:20190419162239p:plain
機械なんかにボット認定される図

reCAPTCHA v3 スコア指標がわからない

reCAPTCHA v3 はトラフィックを傾向をもとにスコアを算出する仕組みがあるようで、この条件ならスコアが良い/悪いということが、一概にいえないのが難しい。

f:id:tech-feedforce:20190419145945p:plain
reCAPTCHA v3 のスコア算出について

一応、スコアが落ちやすいアクセスに、以下のような傾向はあった。

  • Selenium / Puppeteer など
    • ほぼ 0.1 のボット扱い
    • 導入 1 日目は様子見期間なのか、スコアは落ちず
  • 何度もログイン試行した場合
    • 開発者メンバー誤検知がこれに当たる?
  • VM 環境 (BrowserStack など)
    • もしくは、特定の IP アドレス IP レンジからのアクセス
    • 0.3 ~ 0.7 と微妙なスコアが多かった
  • UA がボットっぽい
    • Chrome devtools の Network > Network conditions > User agent から Googlebot を選んだりすると、スコアが落ちる
    • ただし、後日確かめると最高点を出したりして、一概には言えず
  • シークレットブラウザ
    • 外部記事に落ちたという事例あり
    • こちらが試す分には、意外とスコアが落ちなかった

とはいえ、環境により低かったり低くなかったりすることがあり、正確な判別アルゴリズムは神のみぞ知る。

非常に嫌な予感がしたため、急遽以下のような仕組みを実装することに。

最近ログイン成功したユーザーに対しては reCAPTCHA 検証を行わないように

想定する最悪なパターンの1つとして、日々、管理画面を運用してくれているお客様に対して、毎朝画像クイズを出題して業務を滞らせること。

そもそも、一度ログインに成功したらユーザーに対しては、ボット検証をする意味はほぼないので、ログイン成功時に reCAPTCHA 検証を免除できる権利 (トークン) 3 をブラウザに付加することに。

具体的なフローチャート

以下の三段階の検証を段階的に設けることで、既存ユーザーに優しく、怪しいユーザーには厳しい検証を目指した。

  • ① reCAPTCHA 免除検証
    • ログインシステム管理、ログイン成功済みのユーザーのみがトークンを所有
  • ② reCAPTCHA v3 検証
    • Google 管理
  • ③ reCAPTCHA v2 検証
    • Google 管理

既存ユーザーへフリクションを意識させないことにこだわりすぎて、非常に複雑なロジックになってしまった苦肉のスパゲティ。もっとシンプルにすべく、現在改良中。

具体的なフローチャート

まとめ

  • ログインシステムにて、ユーザーフリクションを減らした reCAPTCHA を導入した
  • ログインで reCAPTCHA v3 を使う場合、誤検知ありきの設計が必要
  • reCAPTCHA のインタフェースは簡単だが、うまく使いこなすのは難しい

今後の課題

  • もう少しロジックを簡略化する
  • 独自実装周りのセキュリティ考慮
  • reCAPTCHA v3 のスコアを安定させる
    • 以下、付録に添付した論文のとおり、ユーザーのマウスの動きなどを参考にしているのであれば、reCAPTCHA が人間の動きを学習しやすいように、action を適切に振り分けてみる。など

付録

reCAPTCHA 他社導入事例や、ログイン以外の使いみち

ログインへの組み込みだと、最近 mixi が導入事例の記事を上げていた。

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 に期待。


  1. ユーザーの目的を阻む「フリクション」を取り除いてUXを改善しよう

  2. フリクションは必ずしもUXに悪影響を与える訳ではない

  3. { username } がペイロードの期限付き JWT とした。署名形式は reCAPTCHA Secret Key を salt (HMAC SHA256) で包んだ HS256 (like HKDF)

  4. マウスの動きだけでなく、キーイベントなども考慮されると難易度が上がりそう