防垃圾表单三层:Honeypot 隐藏字段(拦截简单机器人)+ 提交时间检测(少于3秒判定为机器人)+ Cloudflare Turnstile(免费且无需用户交互,替代 reCAPTCHA)。定期维护:每月检查依赖漏洞和证书状态,每季度检查认证有效期和第三方服务配额。
网站安全维护:防垃圾表单与定期巡检清单
静态站的攻击面
第 14 篇选了 Astro + CF Pages 的静态方案,这个选择本身就消除了大部分安全风险:
静态站没有的风险:
✅ 无数据库 → 无 SQL 注入
✅ 无服务端代码执行 → 无 RCE
✅ 无后台登录 → 无凭证爆破
✅ 无 PHP/WordPress → 无插件漏洞(外贸站被黑的最常见原因)
剩下的风险点只有四个:
| 风险点 | 影响 | 优先级 |
|---|---|---|
| 表单垃圾提交 | 淹没真实询盘,浪费 API 配额 | 高 |
| 依赖包漏洞 | 供应链风险(构建时注入) | 中 |
| API 密钥泄露 | 第三方服务被滥用 | 高 |
| 第三方服务配额耗尽 | 表单失效,询盘丢失 | 中 |
一、防垃圾表单:三层方案
层 1:Honeypot 隐藏字段(拦截 60–70%)
第 19 篇已经加了,这里补充正确的实现细节。
<!-- 正确的 honeypot 实现 -->
<div class="hp-field" aria-hidden="true">
<label for="website-url">Website</label>
<input
type="text"
id="website-url"
name="website_url"
tabindex="-1"
autocomplete="off"
>
</div>
<style>
/* 用绝对定位移出视口,而不是 display:none */
.hp-field {
position: absolute;
left: -9999px;
width: 1px;
height: 1px;
overflow: hidden;
}
</style>
为什么不用 display: none:较新的爬虫会检查 CSS,跳过 display:none 的字段。绝对定位移出视口的方式对机器人来说仍然“可见”。
三个关键属性:
tabindex="-1":键盘用户 Tab 不会聚焦到它aria-hidden="true":屏幕阅读器忽略它(可访问性)autocomplete="off":浏览器不会自动填充
字段名要选机器人爱填的(website、url、phone2),不要用 honeypot 这种明显的名字。
层 2:提交时间检测(拦截 20–30%)
人类填完一个 5 字段表单最少需要 8–15 秒。机器人通常在 1–2 秒内提交。
// 表单加载时记录时间戳
const formLoadTime = Date.now();
form.addEventListener('submit', async (e) => {
e.preventDefault();
const elapsed = (Date.now() - formLoadTime) / 1000;
const formData = new FormData(form);
const payload = Object.fromEntries(formData);
payload._elapsed = elapsed; // 传给后端判断
// 客户端也做一次拦截(减少无效请求)
if (elapsed < 3) {
// 静默"成功",不提示机器人被拦截了
showSuccessMessage();
return;
}
await fetch(N8N_WEBHOOK, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
showSuccessMessage();
});
在 n8n 的过滤节点(第 58 篇)里加上这个判断:
// 补充到第 58 篇的垃圾过滤 Function 节点
if (data._elapsed != null && data._elapsed < 3) {
isSpam = true;
spamReason = `submitted_too_fast: ${data._elapsed}s`;
}
注意:不要在前端提示“你提交太快了”。静默处理,让机器人以为成功了——否则它会调整策略重试。
层 3:Cloudflare Turnstile(拦截剩余的)
如果前两层还不够(垃圾提交仍然很多),加 Turnstile。它是 Cloudflare 的免费 CAPTCHA 替代方案。
为什么用 Turnstile 而不是 reCAPTCHA:
| Turnstile | reCAPTCHA v2 | |
|---|---|---|
| 用户交互 | 通常无需交互(自动通过) | 需要点击/选图 |
| 隐私 | 不追踪用户 | Google 追踪(GDPR 需额外声明) |
| 成本 | 免费无限 | 免费有限额 |
| 中国访问 | 正常 | 常被墙(关键差异) |
中国访问这一条对外贸站是决定性的——如果你的团队在国内测试表单,reCAPTCHA 加载失败会导致表单完全不可用。
配置:
<!-- 1. 加载 Turnstile 脚本 -->
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<!-- 2. 在表单里加 widget -->
<div
class="cf-turnstile"
data-sitekey="YOUR_SITE_KEY"
data-theme="light"
data-size="flexible"
></div>
在 n8n 里验证 token:
// n8n HTTP Request 节点:验证 Turnstile token
// POST https://challenges.cloudflare.com/turnstile/v0/siteverify
{
"secret": "{{ $env.TURNSTILE_SECRET }}",
"response": "{{ $json['cf-turnstile-response'] }}",
"remoteip": "{{ $json._clientIp }}"
}
// 后续 Function 节点判断
const verify = $input.item.json;
if (!verify.success) {
return { json: { ...data, isSpam: true, spamReason: 'turnstile_failed' } };
}
建议的启用顺序:先只用层 1 + 层 2,观察一个月。如果垃圾提交少于每周 2–3 条,不需要加 Turnstile——少一个第三方脚本对性能和可用性都更好。
二、依赖包安全
静态站的依赖漏洞不会直接影响线上(构建产物是纯 HTML/CSS/JS),但存在构建时的供应链风险。
每月检查
# 检查已知漏洞
npm audit
# 只看高危和严重
npm audit --audit-level=high
# 查看可更新的包
npm outdated
更新策略
补丁版本(1.2.3 → 1.2.4):直接更新
npm update
次版本(1.2.x → 1.3.0):更新后跑构建验证
npm install package@^1.3.0
npm run build
主版本(1.x → 2.0):读 CHANGELOG,评估破坏性变更后再决定
→ 静态站不急于升级主版本,能构建成功就不动
Astro 的特殊说明:Astro 主版本升级通常涉及配置变更。升级前先在分支上做,用 CF Pages 的 Preview Deployment(第 22 篇)验证,确认无问题再合并。
自动化:GitHub Dependabot
在仓库根目录加 .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
open-pull-requests-limit: 5
# 只自动提补丁和次版本的 PR,主版本手动处理
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
Dependabot 会自动提 PR,CF Pages 会为每个 PR 生成预览部署——你只需要看预览没问题就合并。
三、API 密钥管理
外贸站会用到几个 API 密钥(Resend、Turnstile Secret、Anthropic API Key 等)。
绝对不能做的
❌ 把密钥写在前端代码里(任何人都能在浏览器看到)
❌ 把密钥提交到 Git(即使后来删除,历史记录里仍然存在)
❌ 把 .env 文件提交到仓库
正确做法
# .gitignore 必须包含
.env
.env.local
.env.production
.dev.vars # Cloudflare Workers 本地变量文件
密钥存放位置:
| 用途 | 存放位置 |
|---|---|
| CF Pages 构建时变量 | Pages → Settings → Environment variables |
| CF Workers 运行时密钥 | wrangler secret put KEY_NAME |
| n8n 中的密钥 | n8n Credentials 管理(加密存储) |
| 本地开发 | .env 文件(已 gitignore) |
如果密钥泄露了
1. 立刻在服务商控制台吊销该密钥(不是先改代码)
2. 生成新密钥,更新到环境变量
3. 检查服务商的用量记录,确认是否被滥用
4. 如果密钥曾提交到 Git 历史:
- 公开仓库:必须吊销,历史无法真正清除
- 私有仓库:吊销后可选择重写历史(git filter-repo)
GitHub 的 Secret Scanning(公开仓库自动启用)会检测常见的密钥格式并通知你。私有仓库需要 GitHub Advanced Security 才有这个功能。
四、第三方服务配额监控
这是最容易被忽略但影响最直接的一项——配额耗尽时表单会静默失效,你收不到任何询盘也不会收到报错。
需要监控的配额
| 服务 | 免费额度 | 耗尽后果 | 检查频率 |
|---|---|---|---|
| Resend | 100 封/天,3000 封/月 | 询盘通知和自动回复都发不出 | 每月 |
| Formspree(如用) | 50 次提交/月 | 表单提交失败 | 每月 |
| n8n Cloud(如用) | 5000 次执行/月 | 流程停止 | 每月 |
| CF Workers | 100,000 请求/天 | 表单处理失败 | 一般不会超 |
| Brevo(第 12 篇) | 300 封/天 | 邮件发不出 | 每月 |
| Anthropic API | 按量付费 | 批量生成中断 | 用量大时每周 |
配额告警配置
Resend 和 Brevo 都支持用量告警,在控制台设置:
建议阈值:达到免费额度的 70% 时发邮件提醒
理由:70% 时还有足够时间处理(升级套餐或排查异常用量)
异常用量排查:如果某天用量突然翻倍,通常是垃圾表单被自动回复触发了大量邮件。这时应该:
- 检查 Notion 里 Grade=SPAM 的记录数(第 58 篇)
- 在 n8n 流程里确认自动回复节点在垃圾过滤之后(不要给垃圾提交发自动回复)
五、月度巡检清单
每月花 20 分钟,照着做:
□ 依赖安全
npm audit --audit-level=high → 无高危漏洞
合并 Dependabot 的补丁级 PR
□ 站点可用性
UptimeRobot 上月可用率 ≥ 99.9%(第 26 篇)
如有宕机记录,确认原因已排除
□ 表单功能(最重要,必须实测)
提交一条测试询盘 → 确认:
□ 收到通知邮件
□ 买家收到自动回复
□ Notion 数据库有新记录(如用第 58 篇的流程)
□ 第三方服务配额
Resend / Brevo 用量 < 70%
n8n 执行次数 < 70%(如用 Cloud 版)
□ 垃圾提交量
查看 Notion 中 Grade=SPAM 的上月数量
> 每周 5 条 → 考虑启用 Turnstile
□ 证书与 DNS
访问站点确认 HTTPS 正常(🔒 图标)
Cloudflare SSL 状态为 Active
表单实测这一项不能省。第三方服务变更、API 密钥过期、n8n 流程失效——这些都不会主动报错,只有实测能发现。见过太多站点表单坏了两个月才发现。
六、季度巡检清单
每季度额外检查这些(配合第 50 篇的健康度报告一起做):
□ 认证证书有效期
ISO 9001 / CE / ATEX 等是否临近到期
→ 到期前 3 个月启动复审流程
→ 网站上的证书信息同步更新(第 57 篇)
□ 内容准确性抽查
随机抽 5 个页面,核对:
□ 参数表数据与最新规格书一致
□ 认证信息与实际持有的证书一致
□ 引用的行业标准编号无误(第 42 篇强调过 AI 会编造标准号)
□ 停产型号页面已标记 deprecated(第 03 篇)
□ 安全响应头
用 securityheaders.com 复查评分 ≥ B(第 23 篇)
(CF 或框架升级后配置可能变化)
□ AI 爬虫可访问性
curl -A "GPTBot" https://yourdomain.com/products/xxx/ | head -50
→ 确认返回正常 HTML 内容(第 43 篇)
→ robots.txt 未意外屏蔽 AI 爬虫
□ 备份确认
Git 仓库有远程备份(GitHub 本身即备份)
registry.json / entities.yaml 已提交到仓库
Notion 数据库导出一份 CSV 存档(询盘数据)
registry.json 和 entities.yaml 必须在 Git 里——它们是整个 SEO 体系的事实来源(第 01、55 篇),丢了要重建的成本极高。
七、事故响应预案
写一份简单的应急流程,存在容易找到的地方。
## 网站无法访问
1. 检查 CF Pages → Deployments,最近一次部署是否失败
→ 失败:查看构建日志,回滚到上一个成功版本
(CF Pages 支持一键 Rollback)
2. 检查 Cloudflare → DNS,记录是否正常
3. 检查域名是否过期(Cloudflare Registrar → Domains)
4. 用 downforeveryoneorjustme.com 确认是全站问题还是本地网络
## 询盘表单失效
1. 提交测试询盘,看浏览器 Console 有无报错
2. 检查 n8n 流程执行历史,是否有失败记录
3. 检查 Resend / Brevo 配额是否耗尽
4. 检查 API 密钥是否过期或被吊销
5. 临时方案:在页面顶部加公告,展示直接邮箱和 WhatsApp
## 收到大量垃圾提交
1. 确认 Honeypot 和时间检测是否生效(看 n8n 的 spamReason 分布)
2. 启用 Cloudflare Turnstile
3. 如仍严重:Cloudflare → Security → WAF 加速率限制规则
(同一 IP 每 10 分钟最多 3 次表单提交)
→ 外链建设基础:适合小团队的白帽增长策略
RELATED / 相关推荐
接着读这些
按同一分类、系列与标签为你挑选。
网站监控与告警:UptimeRobot + 免费方案全配置
网站宕机每分钟都在损失潜在询盘。这篇讲用 UptimeRobot 免费计划监控网站可用性(每5分钟检测一次,宕机立即发邮件/微信提醒)、Cloudflare Analytics 监控流量异常,以及设置 GSC 覆盖率告警——让你在买家发现问题前就知道出了什么。
企业邮箱:CF Email Routing + Gmail 零成本方案
用 info@yourdomain.com 收发询盘是外贸站最基础的信任信号。这篇讲用 Cloudflare Email Routing 把域名邮件转发到 Gmail,再配置 Gmail 用域名邮箱发件——全套零成本,10 分钟完成,比买 Google Workspace 省 $72/年。
域名注册与 Cloudflare DNS 完整配置
域名是外贸站最长期的资产,选错难以更换。这篇讲外贸站域名的选择原则(TLD、品牌词、长度)、在哪里注册、如何把 DNS 迁移到 Cloudflare 并完成全套基础配置——包括 A 记录、CNAME、www 重定向、DNSSEC。