新規投稿のお知らせを受信されたい方は、サブスクリプションをご登録ください:

フェッチリクエストへのチャレンジで、TurnstileとCloudflare WAFを統合

2023-12-18

4分で読了
この投稿はEnglish繁體中文FrançaisDeutsch한국어PortuguêsEspañolPolski简体中文でも表示されます。

2ヶ月前より、Cloudflare Turnstileが一般利用できるようになりました。これは、あらゆるWebサイトのオーナーに、CAPTCHAを発行することなくボットを撃退する簡単な方法を提供します。Turnstileを使えば、Webサイトの所有者であれば誰でも、簡単なコードスニペットで、自分のWebサイトにフラストレーションのないCloudflareチャレンジを埋め込むことができ、確実に人間のトラフィックだけを通過させるようにできます。Webサイトのフロントエンドを保護するだけでなく、TurnstileはWeb管理者がブラウザ起動型(AJAX)API呼び出しを硬化させることもできます。これらのAPIは、React、Angular、Vue.jsで作成されたような動的なシングルページのWebアプリで一般的に使用されています。

Integrating Turnstile with the Cloudflare WAF to challenge fetch requests

嬉しいことに、当社はTurnstileをCloudflare Webアプリファイアウォール(WAF)に統合したことを、本日、発表いたします。これは、Web管理者がTurnstileのコードスニペットをWebサイトに追加し、そのリクエストを管理するためにCloudflare WAFを設定できることを意味します。WAFルールを使用したカスタマイズは自由に行えます。例えば、Turnstileによって認証されたユーザが、それ以上チャレンジに直面することなく、アプリケーションのすべてのAPIエンドポイントとやりとりできるようにすることもできますし、Loginのような特定の機密エンドポイントが常にチャレンジを発行するように設定することもできます。

Cloudflare WAFでフェッチリクエストにチャレンジを実施

CloudflareのWAFによって保護されている何百万ものWebサイトは、JSチャレンジ、Managed Challenge、インタラクティブチャレンジを活用してボットを阻止し、人間を通過させています。これらのチャレンジのそれぞれについて、Cloudflareはマッチするリクエストを傍受し、ユーザーが人間であることを証明するための基本的なタスクを完了すると、ブラウザによってレンダリングされたHTMLページで応答します。ユーザーがチャレンジに成功すると、ユーザーはcf_clearance cookieを受け取ります。このクリアランスCookieは、ユーザーがチャレンジに成功したことやチャレンジの種類、いつ完了したかをCloudflareに伝えます。クリアランスCookieはユーザー間で共有することはできず、Cloudflareのお客様がセキュリティ設定ダッシュボードで設定した時間のみ有効です。

このプロセスは、ブラウザがフェッチリクエストでチャレンジを受信し、ブラウザが以前にチャレンジを通過していない場合を除き、うまく機能します。フェッチリクエスト、またはXML HTTPリクエスト(XHR)では、ブラウザは単純なテキスト(JSON または XML形式)を返すことを期待し、チャレンジを実行するために必要なHTMLをレンダリングすることはできません。

例として、ピザ屋のオーナーを想像してみましょう。このオーナーはReactでオンライン注文フォームを構築し、支払いを処理するAPIエンドポイントにデータを送信する決済ページを持っています。ユーザーがクレジットカードの詳細を追加するためにWebフォームを表示すると、Managed Challengeをパスすることができますが、ユーザーがフェッチリクエストでクレジットカードの詳細を送信すると、ブラウザはチャレンジの実行に必要なコードを実行しません。ピザ屋のオーナーにとって、疑わしい(しかし正当な可能性のある)リクエストを処理する唯一の選択肢は、それをブロックすることですが、誤検出のリスクもあり、レストランが販売機会を失う原因となりかねません。

そこでTurnstileが役に立ちます。Turnstileを使えば、インターネット上の誰もが自分のWebサイトのどこにでもCloudflareチャレンジを埋め込むことができます。今日まで、Turnstileの出力は1回限りのトークンのみでした。Turnstileは、お客様がこれらのフェッチリクエストに対してチャレンジを発行できるように、埋め込んだドメインのクリアランスCookieを発行できるようになりました。お客様は、フェッチリクエストの前にHTMLページ内でチャレンジを発行し、Web訪問者が決済APIと対話することを_事前に許可_することができます。

Turnstile Pre-Clearanceモード

ピザ屋の例に戻ると、TurnstileをCloudflare WAFと統合するためにPre-Clearanceを使用する3つの大きな利点があります。

  1. **ユーザーエクスペリエンスの向上。**Turnstileの埋め込みチャレンジは、Web訪問者が支払いの詳細を入力している間、バックグラウンドで実行することができます。

  2. **エッジでより多くのリクエストをブロック。**Turnstileは、それが埋め込まれているドメインのクリアランスCookieを発行するようになったため、ピザ屋のオーナーはカスタムルールを使用して、決済APIへのすべてのリクエストに対してManaged Challengeを発行することができます。これにより、決済APIを直接狙おうとする自動化された攻撃は、APIに到達する前にCloudflareによって阻止されます。

  3. **(オプション)アクションとユーザを保護します。**Pre-Clearanceのメリットを得るためにバックエンドのコードを変更する必要はありません。しかし、Turnstileをさらに統合することで、統合されたAPIのセキュリティが向上します。ピザ屋のオーナーは、受信したTurnstileトークンを検証するために決済フォームを調整することができ、すべての支払いの試みがセッションハイジャックから決済エンドポイントを保護するためにTurnstileによって個別に検証されるようにできます。

Pre-Clearanceを有効にしたTurnstileウィジェットでも、Turnstileトークンは発行されます。このため、エンドポイントへのリクエストごとにセキュリティチェックが必要なほど重要なエンドポイントか、またはチェックはセッションに1回だけ必要かを柔軟に判断することができます。Turnstileウィジェットによって発行されたクリアランスCookiesは、設定の必要なく、Turnstileウィジェットが埋め込まれているCloudflareゾーンに自動的に適用されます。トークンが有効なクリアランスタイムは、ゾーン固有の「チャレンジ通過」タイムによって管理されます。

Pre-ClearanceによるTurnstileの導入

基本的な実装を通して、これを具体的に行っていきましょう。始める前に、/your-apiエンドポイント上でフロントエンドとバックエンドのやり取りをエミュレートする簡単なデモアプリをセットアップしました。

そのために、次のようなコードを用意しました。

ボタンを作成しました。クリックすると、Cloudflareは/your-apiエンドポイントにfetch()リクエストを行い、結果をレスポンスコンテナに表示します。

<!DOCTYPE html>
<html lang="en">
<head>
   <title>Turnstile Pre-Clearance Demo </title>
</head>
<body>
  <main class="pre-clearance-demo">
    <h2>Pre-clearance Demo</h2>
    <button id="fetchBtn">Fetch Data</button>
    <div id="response"></div>
</main>

<script>
  const button = document.getElementById('fetchBtn');
  const responseDiv = document.getElementById('response');
  button.addEventListener('click', async () => {
  try {
    let result = await fetch('/your-api');
    if (result.ok) {
      let data = await result.json();
      responseDiv.textContent = JSON.stringify(data);
    } else {
      responseDiv.textContent = 'Error fetching data';
    }
  } catch (error) {
    responseDiv.textContent = 'Network error';
  }
});
</script>

ここで、/your-apiエンドポイントをManaged Challengeで保護するCloudflare WAFルールが設定されていると考えてみましょう。

このルールのため、先ほど書いたアプリは前述の理由で失敗することになります(ブラウザはJSONレスポンスを期待していますが、代わりにHTMLとしてチャレンジページを受け取ります)。

Networkタブを見ると、/your-apiへのリクエストに403応答が返されていることがわかります。

検査すると、Cf-Mitigatedヘッダーは、Web訪問者が以前にチャレンジを解決していないため、Cloudflareのファイアウォールによって応答がチャレンジされたことを示しています。

私たちのアプリでこの問題に対処するために、使用したいTurnstile sitekeyのPre-ClearanceモードでTurnstileウィジェットを設定します。

アプリでは、Cf-Mitigated応答が受信されると、Turnstileを呼び出すためにfetch()関数をオーバーライドします。

上のスニペットでは、たくさんのことが行われています。まず、非表示のオーバーレイ要素を作成し、ブラウザのfetch()関数をオーバーライドします。fetch()関数は、Cf-Mitigatedヘッダーの「チャレンジ」を見直すように変更されます。チャレンジが発行された場合、最初の結果は失敗します。代わりに、Turnstileオーバーレイ(Pre-Clearanceが有効)がWebアプリケーションに表示されます。Turnstileチャレンジが完了すると、Turnstileがcf_clearance Cookieを取得した後に、前のリクエストを再試行し、Cloudflare WAFを通過させます。

<script>
turnstileLoad = function () {
  // Save a reference to the original fetch function
  const originalFetch = window.fetch;

  // A simple modal to contain Cloudflare Turnstile
  const overlay = document.createElement('div');
  overlay.style.position = 'fixed';
  overlay.style.top = '0';
  overlay.style.left = '0';
  overlay.style.right = '0';
  overlay.style.bottom = '0';
  overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  overlay.style.border = '1px solid grey';
  overlay.style.zIndex = '10000';
  overlay.style.display = 'none';
  overlay.innerHTML =       '<p style="color: white; text-align: center; margin-top: 50vh;">One more step before you proceed...</p><div style=”display: flex; flex-wrap: nowrap; align-items: center; justify-content: center;” id="turnstile_widget"></div>';
  document.body.appendChild(overlay);

  // Override the native fetch function
  window.fetch = async function (...args) {
      let response = await originalFetch(...args);

      // If the original request was challenged...
      if (response.headers.has('cf-mitigated') && response.headers.get('cf-mitigated') === 'challenge') {
          // The request has been challenged...
          overlay.style.display = 'block';

          await new Promise((resolve, reject) => {
              turnstile.render('#turnstile_widget', {
                  'sitekey': ‘YOUR_TURNSTILE_SITEKEY',
                  'error-callback': function (e) {
                      overlay.style.display = 'none';
                      reject(e);
                  },
                  'callback': function (token, preClearanceObtained) {
                      if (preClearanceObtained) {
                          // The visitor successfully solved the challenge on the page. 
                          overlay.style.display = 'none';
                          resolve();
                      } else {
                          reject(new Error('Unable to obtain pre-clearance'));
                      }
                  },
              });
          });

          // Replay the original fetch request, this time it will have the cf_clearance Cookie
          response = await originalFetch(...args);
      }
      return response;
  };
};
</script>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=turnstileLoad" async defer></script>

Turnstileウィジェットを解決すると、オーバーレイが消え、要求されたAPIの結果が正常に表示されます。

Pre-Clearanceは、Cloudflareのすべてのお客様にご利用いただけます

無料プラン以上のCloudflareユーザーは、リクエスト数無制限でマネージドモードのTurnstileを無料で使用できます。重要なAPIエンドポイントのセキュリティとユーザーエクスペリエンスの向上をお考えのCloudflareユーザーの方は、今すぐダッシュボードにアクセスして、Pre-Clearance付きのTurnstileウィジェットを作成してください。

Cloudflareは企業ネットワーク全体を保護し、お客様がインターネット規模のアプリケーションを効率的に構築し、あらゆるWebサイトやインターネットアプリケーションを高速化し、DDoS攻撃を退けハッカーの侵入を防ぎゼロトラスト導入を推進できるようお手伝いしています。

ご使用のデバイスから1.1.1.1 にアクセスし、インターネットを高速化し安全性を高めるCloudflareの無料アプリをご利用ください。

より良いインターネットの構築支援という当社の使命について、詳しくはこちらをご覧ください。新たなキャリアの方向性を模索中の方は、当社の求人情報をご覧ください。
セキュリティCAPTCHABotsTurnstile製品ニュース開発者Micro-frontends

Xでフォロー

Adam Martinetti|@adamemcf
Benedikt Wolters|@worengawins
Miguel de Moura|@miguel_demoura
Cloudflare|@cloudflare

関連ブログ投稿