Web 安全系列把设置当作带作用域与失败模式的契约。本教程检查你有权查询的主机。HTTP、TLS 与 DNS 请求是只读观察,但仍会产生日志和流量。SSH 检查读取你自己的信任记录;部署主机可能不同于网站主机。

用一次性本地工作目录保存输出。这里没有任何输出被当作刚测得的 www.xbcatm.com 生产状态;命令通过,只能证明它实际检查的性质。

独立安全证据栏区分 SNI 与验证、到期与信任、可变的呈现链、HSTS 主机范围、适用 CAA 签发策略、本地 SSH 记录、Cookie 风险控制和正文 ETag 边界。这些观察不能认证整个服务安全。
每项观察只支持范围明确的论断。头部存在、证书日期与本地记录不能合成整站安全认证。

1. 从边缘声明的策略开始

抓取 GET 响应,不默默跟随重定向,再检查状态与头部:

host=www.xbcatm.com
url="https://$host/"
curl -sS --max-time 20 -D headers.txt -o body.html "$url"
cat headers.txt

仓库预期策略包含以下示意字段:

Content-Security-Policy: default-src 'self'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'; object-src 'none'; script-src 'none'; style-src 'self'; img-src 'self' data:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

script-src 'none' 是适合本站无脚本要求的严格选择,不是所有静态站的通用配方。CSP 由浏览器执行;报告模式不会阻止执行。应读完整策略,检查更具体的脚本指令,并比较普通页与错误路由。nosniff 限制 MIME 处理,尤其是脚本和样式表目标,但不证明正文的媒体类型正确。

HSTS 从有效 HTTPS 响应学习,作用于发送它的主机。www.example.com 的 includeSubDomains 覆盖该名称的后代,不覆盖兄弟 api.example.com 或父域 example.com。采用较长时长前先清点实际范围。代理增加或移除头部会改变浏览器契约,因此产物检查与边缘检查需要分开。

2. 读取证书日期前先验证 TLS 身份

SNI 选择目标虚拟主机,不验证证书名称。应让验证失败成为致命错误,并显式启用主机名验证。使用与受测客户端相适应、保持更新的信任库。

openssl s_client -connect "$host:443" -servername "$host" \
  -verify_hostname "$host" -verify_return_error -showcerts \
  </dev/null > tls.txt 2> tls.log
# Continue only if the command succeeded; preserve tls.log on failure.
openssl x509 -in tls.txt -noout -subject -issuer -dates -ext subjectAltName

分别检查每步结果,或在失败即停止的 shell 设置下运行。隐藏 stderr 或把结果管道给成功的 x509,可能掩盖握手失败。应检查 subjectAltName 的 DNS 名称;显示的 subject CN 不足以完成现代服务身份验证。某些库版本仍保留旧式 CN 回退,因此也要明确检查预期 SAN 存在。合法匹配的通配符可以有效,不相关的证书则不行。

notAfter 给出到期时刻,不证明信任链有效、主机名匹配,也不说明检查过吊销。记录日期与测量出的安全窗口,不要依赖记忆。OpenSSL 的结果描述其已配置的信任库与检查,不代表所有浏览器。

3. 读取呈现链,不虚构固定数量

-showcerts 输出的是服务器发送的证书,不是它们已经组成已验证链的声明。以下命令仅显示数量:

grep -c 'BEGIN CERTIFICATE' tls.txt

不存在“必须四张”的要求。服务器通常提供叶证书及必要的中间证书;根一般属于客户端信任库,不必发送。数量随 CA 与所选路径变化。只有一张时值得调查是否缺少中间证书,但数量本身既不代表成功,也不代表失败。

应使用第 2 步真实的验证结果,再调查签发者或 SAN 变化、中间证书缺失、到期及信任库差异。中间证书轮换可能只是正常 CA 维护;数量没变也可能出错。这个计数命令不会自动与任何预期值作比较。

4. 重新验证已抓取的表示

缓存是另一份契约,不是 TLS 身份证据。按照 HTTP 演练 抓取当前 ETag,并对相同表示选择发出条件请求。匹配的条件 GET 可以返回 304;两次请求之间发生发布,则可能合法返回 200。旧的硬编码校验器不是变化站点的可复用测试夹具。

无脚本策略不等于 CSP 永远不变。安全头可以独立于 HTML 改变,正文 ETag 也不是所有响应头的指纹。发布后应再次记录真实策略。

5. 找到适用的 CAA 策略

CAA 约束合规证书机构的签发,不是证书固定,也不会追溯地使已签发证书失效。先检查查询名称及其祖先,保留响应状态,避免把 DNS 失败误当成空答案。

dig www.xbcatm.com CAA +noall +comments +answer
dig xbcatm.com CAA +noall +comments +answer

www 的空答案不代表签发无限制。RFC 8659 会向上寻找第一个适用的非空 CAA 集合,每次查找按 DNS 规则跟随别名;需要时继续检查祖先。检查 issue,涉及通配符签发时还要看 issuewild。只有 iodef 报告地址并不限制签发。若没有适用限制,CAA 不增加签发者限制,但常规域名验证与 CA 规则仍适用。

应将 CA 文档规定的 CAA 标识与适用策略比较,不是只看叶证书中供人阅读的 issuer CN。今天的 CAA 结果证明的是今天的 DNS 策略,不证明证书签发时存在什么策略。

6. 检查部署主机已记录的 SSH 身份

使用真实 SSH 目标;非默认端口需要方括号主机加端口形式。ssh-keygen -F 能找到直接 grep 主机名会遗漏的哈希 known-host 条目。

deploy_host=deploy.example.test
ssh-keygen -F "$deploy_host" -f "$HOME/.ssh/known_hosts" > host-record.txt
ssh-keygen -lf host-record.txt

把指纹与独立可信记录比较,例如提供商控制台或已验证的管理员渠道。这只读取本地记录,不证明远端现在呈现什么密钥。网络扫描到的密钥本身也不能完成身份认证。更新信任前先调查变化;重建只是可能原因,不是自动接受的许可。

检查已抓取响应中的所有 Set-Cookie,以及相关重定向或应用路由。一个页面没设置 Cookie,不证明整站从不设置。主机专用 Cookie 省略 Domain;添加父域 Domain 会扩大接收它的主机范围。Path 控制发送范围,但不是防御同源代码的安全边界。

Secure 限制传输,HttpOnly 阻止脚本访问 Cookie,SameSite 控制规定的跨站发送行为。它们针对不同风险,不能互相代替;现代浏览器中的 SameSite=None 需要 Secure。对本站无账户的内容模型,扩大作用域前应先问为什么需要 Cookie。共享审计记录时应隐藏 Cookie 值。

保留范围明确的审计记录

记录 UTC 时间、客户端版本、请求及最终 URL、状态、强制策略头、TLS 验证结果与日期、适用 CAA 查询及 SSH 记录来源。区分观察成功、意外结果与未执行检查。这几项观察不止“两条命令”,也不能证明整个服务安全。

证书、DNS、代理或部署变化后,重做相关检查。保留旧结果以发现漂移;论断依赖浏览器执行时,应另行测试真实浏览器行为。

来源