一切的开始

众所周知,在互联网上暴露邮箱地址的后果就是会有一些人时不时往邮箱里发一些垃圾/广告之类的邮件。

因此我就想着用联系表单的形式来替代掉悬挂邮箱的形式,于是当天就开始了表单系统的前后端开发。

在开发过程中,突然想到表单这种东西怎么能少了CAPTCHA呢?最后选择了更加人性化一些的Cloudflare Turnstile作为表单的CAPTCHA。

2025-08-08T13:42:51.png

(毕竟谁也不想点个十几遍那个破红绿灯之类的)

接入Cloudflare Turnstile

那么首先在Cloudflare Dashboard创建一个Turnstile小组件

2025-08-08T13:47:52.png

创建完成后就可以获得两个密钥

2025-08-08T13:54:49.png

那么接下来就可以将前端的Turnstile代码直接复制粘贴到前端代码里了,但由于Hypermoe的主站前端使用的是React的Next.js框架,因此接入的方式与一般的接入方式会有所不同,但本质上就是使用script标签引用Turnstile的js文件。

相关文档:Client-side rendering · Cloudflare Turnstile docs

2025-08-08T13:58:59.png

然后在你想显示小组件的地方添加以下代码:

<div
  class="cf-turnstile"
  data-sitekey="yourSitekey"
  data-callback="javascriptCallback"
></div>

2025-08-08T14:13:48.png

并在点击提交表单时对表单进行状态的验证,如果没通过验证则拒绝提交。

2025-08-09T06:57:59.png

自此,我们就顺利地给我们的表单系统接入了CAPTCHA啦!

但...事情就这么简单就和文章标题不太符合了喵!

数据校验可不能只交给前端

在接入好Turnstile后,我一般都会对新更新的内容做一些测试,在测试时我突然想到好像后端没有数据校验的能力呢,这要是绕过前端直接POST后端,那后台岂不是炸了吗?

而事实也正是如此。

正常流程我们先要获取表单字段,可以在浏览器控制台轻松获取到。

2025-08-09T05:53:49.png

接下来我们就可以直接使用curl命令来根据这些字段直接发起POST。

curl -X POST "https://api.hypermoe.com/feedback/" -F "name=loli" -F "email=1011@hypermoe.net" -F "content=loli"

可以看到服务器处理了请求并返回了工单号,只要再写一个脚本就可以轻而易举地发出大量Abuse工单了。
2025-08-09T05:40:25.png

因此我们就需要给我们的后端也加上数据校验,而不是前端进行一个简单的判断,毕竟这只能阻止的利用你前端进行Abuse的操作。

2025-08-09T06:02:35.png

首先Cloudflare Turnstile在完成人机验证后会生成一串Token用来给服务器进行令牌校验,服务器再通过Siteverify API对令牌进行校验。

Server-side validation · Cloudflare Turnstile docs

在阅览文档后,我们就要开始为我们的后端接入数据校验的能力了。

首先,先从Turnstile的Callback中获取Token,并在原有传递三个表单字段的基础上在传递一个Token给服务器

2025-08-09T06:15:04.png
2025-08-09T06:15:33.png

目前我们的前端就会给后端传递Token了,接下来我们就要让后端检验这个Token,确定Token合法后再进行工单的创建。

这里我们先写好调用Siteverify API的调用代码,使用curl向Siteverify API发出验证表单请求内提供的Token的请求

2025-08-09T06:20:20.png

然后再写一个返回验证结果的判断,如果success不为true,就拒绝处理这个表单请求。

2025-08-09T06:32:21.png

测试效果

接下来我们再使用curl来直接向后端发出POST请求
2025-08-09T06:36:47.png

返回了Unicode,意思为:质询挑战失败,请重试

而浏览器正常发出的请求则会正常处理。
2025-08-09T06:42:30.png

自此,我们的表单系统才是真正地完成了CAPTCHA的接入喵。

标签: Cloudflare, CAPTCHA, 网络安全, Turnstile

添加新评论