文档
监控与变化提醒
AppSafe 会将已验证网站的每次扫描与上一次扫描进行比较,并告诉你发生了哪些变化。配合定时扫描或部署后重新扫描,这就成了持续监控,并可通过邮件、Slack、Discord 或签名 webhook 发送提醒。
变化提醒包含在 Pro、Agency 和 Team 套餐中。Pro 和 Agency 的定时扫描每天或每周运行一次,Team 可每小时运行一次。
AppSafe 会比较什么
- 新出现和已解决的问题。
- 被添加、移除或削弱的安全响应头(例如移除了 Content-Security-Policy,或缩短了 HSTS max-age)。
- TLS 证书更换,以及在证书过期前 14 天和 3 天发出的到期提醒。
- 新的第三方脚本域名,以及缺少 Subresource Integrity(SRI)的外部脚本。
- Certificate Transparency(证书透明度)日志(crt.sh)中出现的你域名的新子域名,以及 CNAME 指向托管服务上已删除资源的子域名。这些检查只使用被动 DNS 查询。
- SPF、DMARC、CAA 和 MX 记录的变化。
定时扫描和部署后扫描会发送提醒,每个问题只提醒一次:保持不变的问题在下一次扫描时不会再次提醒。如果问题消失后又重新出现,你会再次收到提醒。
设置提醒
在你的仪表盘中打开一个已验证的网站,并在“监控与提醒”下添加渠道。每个渠道都有最低严重程度设置和“发送测试提醒”按钮。
- 邮件: 任意邮箱地址。提醒会使用你账户设置的语言撰写。
- Slack: 来自 Slack 应用的 Incoming Webhook URL(https://hooks.slack.com/services/…)。
- Discord: 频道的 Webhook URL(服务器设置 → 整合 → Webhook)。
- Webhook: 任意 https 端点。AppSafe 会 POST 下方的 JSON 负载,并使用创建渠道时仅显示一次的密钥进行签名。
Webhook 负载
通用 webhook 会收到这段 JSON(Content-Type: application/json)。测试提醒使用事件 monitoring.test,且 scan 为 null。
{
"event": "monitoring.changes",
"deliveryId": "5f2c9a0e41b7d3a8c6e1f0b2",
"createdAt": "2026-10-26T12:00:00.000Z",
"site": {
"domain": "myapp.com"
},
"scan": {
"id": "3f1c2b9e-1111-4222-8333-944455556666",
"grade": "C",
"score": 67,
"reportUrl": "https://appsafe.dev/report/…"
},
"summary": "2 changes on myapp.com",
"changes": [
{
"type": "header_removed",
"severity": "high",
"subject": "content-security-policy",
"message": "content-security-policy header removed"
},
{
"type": "finding_new",
"severity": "critical",
"subject": "baas-supabase-table-readable",
"title": "Supabase tables readable without logging in",
"message": "New issue: Supabase tables readable without logging in"
}
]
}变化类型:finding_new、finding_resolved、header_added、header_removed、header_weakened、header_changed、tls_changed、tls_expiring(附带 days)、script_domain_new、sri_missing、subdomain_new、subdomain_dangling、dns_changed。严重程度:low、medium、high、critical。提醒只包含退化性变化,因此 finding_resolved 和 header_added 会显示在仪表盘和报告中,但本身不会触发提醒。
验证签名
每个 webhook 请求都带有 X-AppSafe-Signature 请求头。使用你的渠道密钥,对时间戳、一个点号和原始请求体计算 HMAC-SHA256,并以恒定时间与 v1 进行比较。拒绝时间戳超过 5 分钟的请求。
X-AppSafe-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
X-AppSafe-Event: monitoring.changes | monitoring.testimport { createHmac, timingSafeEqual } from "node:crypto";
// header: the X-AppSafe-Signature value, e.g. "t=1761480000,v1=5d41…"
// body: the raw request body (string), before JSON.parse
export function verifyAppSafeSignature(secret, header, body) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const timestamp = Number(parts.t);
if (!parts.v1 || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = createHmac("sha256", secret).update(`${timestamp}.${body}`).digest();
const given = Buffer.from(parts.v1, "hex");
return given.length === expected.length && timingSafeEqual(given, expected);
}AppSafe 每条提醒只发送一次,不会重试。请在 8 秒内返回 2xx 状态码。仪表盘会显示该渠道最近一次的发送错误。
提醒中绝不会包含的内容
提醒负载包含变化类型、严重程度、问题标题、响应头名称、脚本和子域名的主机名,以及报告链接。绝不会包含问题详情、密钥(即使已脱敏也不会)或个人数据。