简介
Turnstile 是 Cloudflare 的智能验证码替代方案。它可以嵌入到任何网站中,无需通过 Cloudflare 发送流量,并且无需向访问者显示验证码即可工作。
Turnstile 目前处于公开测试阶段,可供所有客户免费使用。
其以较小而简单的JavaScript 交互方式在其网站上的任何位置运行质询,包括工作量质询、空间质询、Web API 探测以及检测浏览器偏好和人类行为的各种其他验证。
因此,它根据特定请求微调验证难度,并避免向用户展示视觉验证的情况。避免验证码打开外部链接,这也减少了人类在互联网上解决验证码所花费的时间。
对于 Beta 版,每个站点每月对 siteverify 验证端点的调用仅限 100 万次。
形式
Turnstile小部件类型包括:
托管
Cloudflare 将使用来自访问者的信息决定是否应使用交互式质询。如果我们确实显示了交互,用户将被提示选中一个框(没有需要辨认的图像或文本)。
非交互式
一个完全非交互式的质询。浏览器质询运行时,用户会看到一个带有加载条的小组件。(转一圈,判断成功还是失败,失败了也不会让你选那个框而进行二次确认)
不可见
不需要交互的无可见质询(没有任何显示)
使用
要开始使用 Turnstile 小部件,您需要获取站点密钥和密钥。sitekey 和 Secret key 始终与一个小部件关联,不能重复用于其他小部件。
sitekey 是公开的,用于调用您站点上的 Turnstile 小部件。
sitekey 和密钥是在创建小部件时生成的,允许您的站点和 Cloudflare 之间进行通信,以验证来自 Turnstile 的已解决质询的响应。出于安全原因,请确保您妥善保管密钥。
总结:
sitekey 类似于公钥,可以理解为域名的编号,放在浏览器前端代码里,用于js发送api用
Secret key 相当于私钥,服务端验证用来验证前端质询是否成功。
注册
- 登录Cloudflare 仪表板,并选择您的帐户。
- 前往Turnstile。
- 在小部件概述中,选择“设置”。
- 复制您的站点密钥和秘密密钥。
应用-渲染客户端集成
1.隐式渲染
1.1引入js
将 Turnstile 脚本片段插入 HTML<head>
元素中:(其他地方也可)
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
1.2调用
<div class="cf-turnstile" data-sitekey="yourSitekey" data-callback="javascriptCallback"></div>
网页添加这行代码就行,js自动扫描 HTML 来查找具有cf-turnstile类名的元素,将此 div 标签 变为 验证的样式。
如果不要 调用js ,可以去掉 data-callback="javascriptCallback"
如果需要更改样式为白色的 可以添加 data-theme="light"
一旦解决了质询,就会将令牌传递给成功回调。该令牌必须根据我们的 siteverify 端点进行验证。一个token只能被验证一次。
令牌发出后,可以在接下来的 300 秒内进行验证。300 秒后,令牌不再有效,需要解决另一个质询。
示例-保护表格
Turnstile 通常用于保护网站上的表单,例如登录表单、联系表单等。
插入 JavaScript 脚本标签后,客户可以嵌入<div class="cf-turnstile"></div>到他们的网站中以保护他们的表单。
<form action="/login" method="POST">
<input type="text" placeholder="username"/>
<input type="password" placeholder="password"/>
<div class="cf-turnstile" data-sitekey="<YOUR_SITE_KEY>"></div>
<button type="submit" value="Submit">Log in</button>
</form>
//把代码里的<YOUR_SITE_KEY>改成网站的sitekey
此代码生成的样式是一个iframe,并且会添加一个带有不可见的元素,name为cf-turnstile-response,并将其与其他字段一起发送到服务器。
假如设置了验证通过前提交按钮不可用,设置了回调 data-callback="javascriptCallback",验证成功后使按钮可用。
<script>
function javascriptCallback() {
document.getElementById("submit").disabled = false;
}
</script>
2.显式渲染
2.1引入js
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
2.1调用
<script>
window.onloadTurnstileCallback = function () {
turnstile.render('#example-container', {
sitekey: '<YOUR_SITE_KEY>',
callback: function(token) {
console.log(`Challenge Success ${token}`);
},
});
};
<script>
//把代码里的<YOUR_SITE_KEY>改成网站的sitekey
//可以在callback里增加自己的代码,比如登陆按钮默认设置为disabled ,回调后 使 登陆 按钮可用
最好将属性配置ata-appearance="interaction-only",不需要质询时自动隐藏,需要质询时可自动出现。
配置
JavaScript 渲染参数 | 数据属性 | 描述 |
---|---|---|
sitekey | data-sitekey | 每个小部件都有一个站点密钥。该站点密钥与相应的小部件配置相关联,并在创建小部件时创建。 |
action | data-action | 客户值,可用于区分分析中同一站点密钥下的小部件,并在验证时返回。最多只能包含 32 个字母数字字符,包括_ 和- 。 |
cData | data-cdata | 客户有效负载,可用于在整个发布过程中将客户数据附加到挑战中,并在验证时返回。最多只能包含 255 个字母数字字符,包括_ 和- 。 |
callback | data-callback | 挑战成功后调用的 JavaScript 回调。回调会传递一个可以验证的令牌。 |
error-callback | data-error-callback | 出现错误(例如网络错误或质询失败)时调用的 JavaScript 回调。请参阅客户端错误。 |
execution | data-execution | 执行控制何时获取小部件的令牌,可以是 on render (默认)或 on execute 。有关详细信息,请参阅执行模式。 |
expired-callback | data-expired-callback | 当令牌过期并且不重置小部件时调用 JavaScript 回调。 |
before-interactive-callback | data-before-interactive-callback | 在挑战进入交互模式之前调用的 JavaScript 回调。 |
after-interactive-callback | data-after-interactive-callback | 当挑战离开交互模式时调用 JavaScript 回调。 |
unsupported-callback | data-unsupported-callback | 当 Turnstile 不支持给定客户端/浏览器时调用 JavaScript 回调。 |
theme | data-theme | 小部件主题。可以取以下值:light , dark , auto .默认值为 auto ,尊重用户偏好。可以通过相应地设置主题来强制其变亮或变暗。 |
language | data-language | 要显示的语言必须是:(auto 默认)使用访问者选择的语言,或者 ISO 639-1 两个字母的语言代码(例如en )或语言和国家/地区代码(例如en-US )。有关详细信息,请参阅支持的语言列表。 |
tabindex | data-tabindex | Turnstile 的 iframe 的 tabindex 用于辅助功能。默认值为0 。 |
timeout-callback | data-timeout-callback | 挑战到期时调用的 JavaScript 回调。 |
response-field | data-response-field | 控制是否创建带有响应令牌的输入元素的布尔值,默认为true 。 |
response-field-name | data-response-field-name | 输入元素的名称,默认为cf-turnstile-response . |
size | data-size | 小部件大小。可以取以下值:normal , compact . |
retry | data-retry | 控制小部件在未成功时是否应自动重试获取令牌。默认为auto ,会自动重试。可以将其设置为never 禁用失败时重试。 |
retry-interval | data-retry-interval | 当retry 设置为 时auto ,retry-interval 控制重试尝试之间的时间(以毫秒为单位)。值必须是小于 的正整数900000 ,默认为8000 。 |
refresh-expired | data-refresh-expired | 令牌过期时自动刷新。可以取auto ,manual 也可以never ,默认为auto 。 |
appearance | data-appearance | 外观控制小部件何时可见。它可以是always (默认)、execute 或interaction-only 。有关详细信息,请参阅外观模式。 |
Token验证
客户必须调用 siteverify 端点来验证其网站后端的 Turnstile 小部件响应。小部件响应只有在经过 siteverify 端点验证后才被视为有效。仅响应的存在不足以验证它,因为它不能防止重放或伪造攻击。在某些情况下,Turnstile 可能会故意创建被 siteverify API 拒绝的无效响应。
使用成功回调通过显式或隐式渲染向 Turnstile 发出的令牌必须使用 siteverify 端点进行验证。
需要向 siteverify 端点传递与 sitekey 关联的密钥。当您创建小部件时,密钥将与站点密钥一起配置。
此外,响应需要传递到 siteverify 端点。
响应只能验证一次。如果相同的响应出现两次,则第二个和每个后续请求将生成一个错误,指出该响应已被消耗。
curl 'https://challenges.cloudflare.com/turnstile/v0/siteverify' --data 'secret=verysecret&response='
{
"success": true,
"error-codes": [],
"challenge_ts": "2022-10-06T00:07:23.274Z",
"hostname": "example.com"
}
//verysecret就是私钥
//请求方式支持GET或者POST都可以,返回json
js小dome
//官网的示例,
// This is the demo secret key. In production, we recommend
// you store your secret key(s) safely.
const SECRET_KEY = '1x0000000000000000000000000000000AA';
async function handlePost(request) {
const body = await request.formData();
// Turnstile injects a token in "cf-turnstile-response".
const token = body.get('cf-turnstile-response');
const ip = request.headers.get('CF-Connecting-IP');
// Validate the token by calling the
// "/siteverify" API endpoint.
let formData = new FormData();
formData.append('secret', SECRET_KEY);
formData.append('response', token);
formData.append('remoteip', ip);
const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
const result = await fetch(url, {
body: formData,
method: 'POST',
});
const outcome = await result.json();
if (outcome.success) {
// ...
}
}
自用PHP验证token示例
if (isset($_POST['submit']) && isset($_POST['cf-turnstile-response'])) {
$ver_url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
$secret = '1x0000000000000000000000000000000AA';
$token = $_POST['cf-turnstile-response'];
$ver_data = 'secret='.$secret.'&response='.$token;
$ver_result = curldata($ver_url,$ver_data);
$result = json_decode($ver_result);
if(!$result->success) {
$error = "token错误";
//echo $error;
}
}
function curldata($url, $data) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}