3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Cloudflare Turnstile をさわってみる

Last updated at Posted at 2023-09-14

目的

Cloudflare Turnstile を触って理解します。

成果物 @ GitHub => kyouheicf/turnstile-demo

Cloudflare Turnstile の特徴

  • Javascript タグを挿入するだけで challenges.cloudflare.com と通信してチャレンジをおこなってくれる
  • Cloudflare にゾーン登録して CDN を採用しなくても使える
  • Managed Challenge と同じ仕組みで、なるべくキャプチャを解く必要がないよう、機械学習も組み込み、高速なユーザ体験を提供する

ウィジェットタイプ

ページ内に配置するウィジェットのタイプを以下の中から選択できる。ほとんどの場合、Managed で問題ないでしょう。

  • Managed (recommended)
    • いくつかの計算要求等の結果によって、インタラクティブな操作をマネージドで要求することがある
  • Non-Interactive
    • インタラクティブな操作要求はない
  • ​​Invisible
    • ページ内のウィジェットは不可視の状態でチャレンジを実行

サイト作成

1ウィジェットを配置するにあたり、サイト作成時に取得できる sitekey (public) と secret key (private) が必要です。

ローカルでテストできるように localhost127.0.0.1 をドメインに追加しておくと良いです。

https://developers.cloudflare.com/turnstile/frequently-asked-questions/#can-i-use-turnstile-when-developing-locally
以下に提供するダミーのサイトキーは、localhostを含むどのドメインからでも使用できます。
Cloudflareは、本番で使用するサイトキーはローカルドメイン(localhost、127.0.0.1)を許可しないことを推奨していますが、ユーザは許可するドメインのリストにローカルドメインを追加することができます。

image.png

Implicit / Explicit レンダリング

以下のコードを参考に、Visual Studio Code でプレビューが確認できます。

disabled属性を使えば、ウィジェットの動きに連動してボタンを有効化・無効化することも可能です。

image.png

Implicit レンダリング

div class="cf-turnstile" が決めうちで、コード量が少なくシンプルに記述できます。

implicit.html
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Turnstile Demo</title>
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
</head>

<body>

    <form action="posttest.php" method="post">
        Username: <input type="text" name="username" /> <br>
        Blood Group: <input type="text" name="bloodgroup" /> <br>
        <!-- The Turnstile widget will be injected in the following div. -->
        <div class="cf-turnstile" data-sitekey="YOUR_SITE_KEY" data-callback="javascriptCallback" data-language="ja"></div>
        <!-- end. -->
        <input type="submit" id="submit" disabled />
    </form>

    <script>
        window.javascriptCallback = function () {
            document.getElementById('submit').disabled = false;
            console.log(`Challenge Success.`);
        }
    </script>
</body>

</html>

Explicit レンダリング

render() を使ってウィジェットのレンダリングすることができますが、ある時点までチャレンジの実行を延期したい場合には、 execute モードを使用して、チャレンジが実行され、トークンが生成されるタイミングを制御できたり、と柔軟性があります。

explicit.html
<!DOCTYPE html>
<html lang="en">

<head>
    <title>Turnstile Demo</title>
    <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
</head>

<body>

    <form action="posttest.php" method="post">
        Username: <input type="text" name="username" /> <br>
        Blood Group: <input type="text" name="bloodgroup" /> <br>
        <!-- The Turnstile widget will be injected in the following div. -->
        <div id="turnstileWidget"></div>
        <!-- end. -->
        <input type="submit" id="submit" disabled />
    </form>

    <script>
        // This function is called when the Turnstile script is loaded and ready to be used.
        // The function name matches the "onload=..." parameter.
        // if using synchronous loading, will be called once the DOM is ready
        window.onloadTurnstileCallback = function () {
            turnstile.render('#turnstileWidget', {
                sitekey: 'YOUR_SITE_KEY',
                theme: 'auto',
                language: 'ja',
                callback: function (token) {
                    document.getElementById('submit').disabled = false;
                    console.log(`Challenge Success. Token === ${token}`);
                },
            });
        }
    </script>
</body>

</html>

Server-side validation

ウィジェットのレスポンスを cf-turnstile-response として受け取れることができ、その内容を使って有効性を検証 siteverify することができます。

https://developers.cloudflare.com/turnstile/frequently-asked-questions/#why-does-a-turnstile-token-need-to-be-verified-using-siteverify
Turnstileは、暗号的に保護されたトークンを作成するフロントエンド・ウィジェットである。しかし、顧客は自分のエンドでトークンの有効性をチェックすることはできません。
トークンが攻撃者によって偽造されたものでないこと、またはまだ消費されていないことを確認するために、顧客はCloudflareのsiteverify APIを使用してトークンの有効性をチェックする必要があります。
https://developers.cloudflare.com/turnstile/frequently-asked-questions/#can-the-front-end-use-siteverify
siteverifyAPIは、認証に使われたsecretkeyを明らかにするかもしれないので、フロントエンドから呼び出してはならない。攻撃者は単純にフロントエンドを変更してsiteverifyチェックを全く行わないようにし、Turnstileを無効にしてしまうかもしれない。

以下のページを用意し、Submit します。

posttest.php
<html>  
   <body>  
   
      Welcome <?php echo $_POST["username"]; ?> </br>  
      Your blood group is: <?php echo $_POST["bloodgroup"]; ?> </br></br> 
      cf-turnstile-response (Token) is: <?php echo $_POST["cf-turnstile-response"]; ?>  </br></br>

      siteverify result is: <?php
      $data = [];
      $data['secret'] = 'YOUR_SECRET_KEY';
      $data['response'] = $_POST["cf-turnstile-response"];
      // 送信データをURLエンコード
      $data = http_build_query($data, "", "&");
      $curl = curl_init();
      curl_setopt_array($curl, [
      CURLOPT_URL => "https://challenges.cloudflare.com/turnstile/v0/siteverify",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_POST => true,
      CURLOPT_POSTFIELDS => $data]);
      $response = curl_exec($curl);
      $err = curl_error($curl);
      curl_close($curl);
      if ($err) {
         echo "cURL Error #:" . $err;
      } else {
         echo $response;
      }?>

   </body>  
</html> 

以下のような結果が得られ、サーバーサイドでシークレットキーを使った検証ができることが確認できました。

image.png

siteverify_result.json
{
  "success": true,
  "error-codes": [],
  "challenge_ts": "2023-09-13T16:03:09.099Z",
  "hostname": "grey.example.com",
  "action": "",
  "cdata": "",
  "metadata": {
    "interactive": false
  }
}

Solve Rate

Analytics 画面で Solve Rate を確認することができます。

Solve Rate は時間の経過とともに比較的安定すると予想されますが、解決率の変化は、ボットからの攻撃を受けているなど、ウェブサイト上で何かが起こっていることを示します。

https://developers.cloudflare.com/turnstile/frequently-asked-questions/#what-is-visitor-solve-rate
Visitor Solve Rate は、発行されたチャレンジと比較して、発行されたが必ずしもサイト検証されていないトークンの割合です。
https://developers.cloudflare.com/turnstile/frequently-asked-questions/#what-is-api-solve-rate
API Solve Rate(API解決率)は、発行されたトークンと比較してサイト検証されたトークンの割合である。

image.png

まとめ

導入も容易なため、非常に便利に使えそうなことがわかりました。

以下にもある通り、いろいろなツールに組み込んで使えそうです。

また、compat=recaptcha のオプションも用意されていたり、移行もシームレスにおこなえそうです。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?