# 网站安全扫描

URL: https://discuvr.org/security-scan/?hl=zh-hans
Last updated: 2026-11-07
MCP tool: `security_scan`

扫描域名以发现缺失的安全响应头、TLS 问题和暴露的文件。每项发现都显示原始证据和用于复现的确切命令。

很多扫描器给你一个字母等级和一串吓人的词。这个工具也给等级，但扣掉的每一分都对应一条发现，其中列出它实际收到的响应，以及能让你拿到同样响应的一行命令。所以一条发现是你能自己核实的，而不是只能相信的。

## 功能说明

- 检查 DNS 和 CAA、SPF 和 DMARC、TLS 证书和 TLS 1.0/1.1、HTTPS 跳转、安全响应头、Cookie 属性、CORS、版本标识、HTTP 方法、混合内容、23 个敏感路径、目录列表、robots.txt、security.txt 以及 12 个服务端口。
- 每项发现都附带原始观察结果和用于复现它的 shell 命令。
- 无法运行的检查会报告为"未检查" - 绝不会悄悄地算作通过。
- 报告可下载为 Markdown 或 JSON 格式。

## 工作原理

1. 输入一个你负责的域名。
2. 扫描执行被动探测 - 普通请求，不进行任何利用。
3. 阅读每项发现及其证据，并核实其中重要的部分。

## 没有任何发现是凭空捏造的

扫描器不含任何语言模型。每项检查的严重程度、影响和修复方法都是固定的，每条发现都来自一次真实观察。页面上的报告以及 Markdown 和 JSON 下载都由同一组发现生成，全程不涉及模型。

## 等级如何计算

分数从 100 开始，每条严重发现扣 34 分，高危扣 13 分，中危扣 5 分，低危扣 1.5 分，每个级别的扣分有上限（高危 45、中危 22、低危 9）。信息类和通过的结果不扣分。90 分及以上为 A，75 分起为 B，60 分起为 C，40 分起为 D，更低为 F。只要有一个暴露的 .env 文件或一个可访问的数据库端口，一个原本干净的网站就会从 100 分降到 66 分，即 C；两条这样的发现得 32 分，即 F。

## 如何避免误报

在探测 /.git/HEAD 或 /backup.sql 这类路径之前，扫描器会先请求一个不可能存在的随机地址，并记录网站的响应。只有当某个路径返回 HTTP 200、与这个未找到基准不同、并且符合该文件自身的特征时，才会被报告为暴露。因此，对任何网址都返回首页的网站不会被一整页严重发现淹没。

## 干净的报告不是一张证书

这些是针对运行中网站的被动检查。它们不涉及应用逻辑、身份验证、访问控制，或登录之后的任何内容 - 而那些正是真正的入侵通常开始的地方。无法完成的检查正是因为这个原因被列为未检查：由失败连接得出的通过是一种虚假的安心，而没有发现任何问题的扫描意味着没有找到问题，并不意味着那里什么都没有。

## 只扫描属于你自己的目标

只扫描你拥有或已获得书面测试许可的域名。该工具会拒绝解析到私有或内部地址的主机，并按 IP 进行速率限制，但授权仍然是你自己的责任。

## 问题

**这是主动攻击吗？**

不是。它只发送普通的 GET、HEAD 和 OPTIONS 请求，进行 TLS 握手（包括尝试 TLS 1.0 和 1.1），通过 Cloudflare 的 DNS over HTTPS 查询 DNS，并对 12 个端口做简单的 TCP 连接。不利用任何漏洞，不提交任何表单，也不修改任何内容。

**我可以扫描任何网站吗？**

只能扫描你拥有或已获授权测试的网站。未经许可扫描第三方基础设施在你所在地可能是违法的。

**为什么一项检查显示"未检查"？**

因为它无法完成 - 通常是因为该源没有任何响应。它会被诚实地报告，而不会被记为通过。

**我可以导出报告吗？**

可以，导出为 Markdown 或 JSON，有效期一小时：报告在这一小时内保存在服务器缓存中以便下载，之后过期。两种导出都不使用模型。使用记录只保存发现数量和等级，不保存域名。

**它会检测哪些文件和端口？**

文件：23 个固定路径，包括 /.git/HEAD, /.git/config, /.env, /.env.local, /.env.production, /.aws/credentials, /id_rsa, /backup.zip, /backup.sql, /dump.sql, /wp-config.php.bak, /phpinfo.php, /server-status, /actuator/env；只有当响应与该文件特有的签名相符时才会报告。若已发布 /.well-known/security.txt，则计为加分项。端口：对 21 FTP, 23 Telnet, 25 SMTP, 3306 MySQL, 5432 PostgreSQL, 6379 Redis, 27017 MongoDB, 9200 Elasticsearch, 3389 RDP, 5900 VNC, 11211 Memcached, 8080 发起普通 TCP 连接，绝不尝试登录。可从外网访问的数据库端口（MySQL、PostgreSQL、Redis、MongoDB、Elasticsearch）或 Telnet 会被评为严重。

**SPF 和 DMARC 检查会标记哪些问题？**

两条记录都从公共 DNS 读取，即使网站不发邮件也会检查，因为缺少它们，任何人都能发送冒充你域名的邮件。没有 SPF 记录为中危；含 +all 的 SPF 记录允许互联网上任何服务器发信，为高危。_dmarc 下没有 DMARC 记录为中危；p=none（或没有 p= 标签）只做监控，为低危；p=quarantine 或 p=reject 通过。完全不发邮件的域名可以发布 v=spf1 -all。

**会检查哪些安全响应头，什么值才算通过？**

检查首页响应中的六个响应头。缺失时，HSTS 和 Content-Security-Policy 为高危，X-Content-Type-Options 和 X-Frame-Options 为中危，Referrer-Policy 和 Permissions-Policy 为低危。Strict-Transport-Security 的 max-age 至少要 15552000（180 天）：更短为低危，max-age=0 为中危。如果 script-src 允许 'unsafe-inline'、策略允许 'unsafe-eval'，或 default-src、script-src 中有单独的 * 通配符，Content-Security-Policy 会被标为已削弱（中危）。X-Content-Type-Options 必须正好是 nosniff。X-Frame-Options 应为 DENY 或 SAMEORIGIN，如果 CSP 已设置 frame-ancestors 则不需要。Referrer-Policy 只有在 unsafe-url 或 no-referrer-when-downgrade 时才会被标记。Permissions-Policy 只要存在即可。每个缺失或偏弱的响应头都附有一条 curl -sI 命令，方便你自己核对。

**它如何检查 Cookie 属性（Secure、HttpOnly、SameSite）？**

它读取首页响应中的每个 Set-Cookie 响应头（最多 12 个 Cookie），逐个检查 Secure、HttpOnly 和 SameSite。缺少 Secure 或 HttpOnly 为中危；只缺 SameSite 为低危；SameSite=None 但没有 Secure 会被单独指出，因为浏览器会拒绝这种 Cookie。三项齐全即通过。常见修复是 Secure; HttpOnly; SameSite=Lax（Django 中为 SESSION_COOKIE_SECURE、SESSION_COOKIE_HTTPONLY 和 SESSION_COOKIE_SAMESITE）。扫描不会登录，所以看不到只在登录后才设置的 Cookie。可以用这条命令自己核对：curl -sI https://your-site/ | grep -i set-cookie

**CORS 检查如何进行，什么情况下是严重？**

它向首页发送一个带 Origin: https://scanner-probe.example 的 GET 请求，读取响应中的 Access-Control-Allow-Origin 和 Access-Control-Allow-Credentials。没有 Access-Control-Allow-Origin 响应头，或返回一个不回显测试来源的固定来源，即通过。回显测试来源并同时返回 Access-Control-Allow-Credentials: true 为严重，因为已登录用户访问的任何网站都能读取其响应。回显来源但不带凭据为中危。通配符 * 为低危，若同时允许凭据则为中危（浏览器会拒绝这种组合）。只检测首页，也不发送 OPTIONS 预检请求，所以请单独检测 API 路由：curl -sI -H "Origin: https://scanner-probe.example" https://your-site/api/ | grep -i access-control

**TLS 检查测试哪些内容（证书到期、TLS 1.0 和 1.1）？**

它连接 443 端口，验证证书链和主机名。无法通过验证的证书为高危，已过期的证书还会额外标为严重，剩余不足 15 天的为中危；否则会把主体、颁发者、到期日期和 SAN 名称作为通过项列出。协商出的协议必须是 TLS 1.2 或 TLS 1.3，更旧的版本为高危。随后它分别单独尝试一次 TLS 1.0 握手和一次 TLS 1.1 握手：服务器接受其中任何一个都会得到中危结果，因为两者都已被 RFC 8996 废弃。如果扫描器自身的 OpenSSL 无法提供旧版本，则不报告任何内容，而不是去猜测。nginx 的修复方法是 ssl_protocols TLSv1.2 TLSv1.3; 可以用以下命令自行查看日期：echo | openssl s_client -connect your-site:443 -servername your-site 2>/dev/null | openssl x509 -noout -dates

**它会检查 HTTP 是否重定向到 HTTPS 吗？**

会。它在不跟随重定向的情况下请求 http://your-site/。以 301 或 308 跳转到 https:// 地址即为通过。以 302、303 或 307 跳转到 HTTPS 为低危，因为浏览器不会记住临时重定向，每次新的访问都会先发出一个未加密的请求。通过未加密 HTTP 直接提供页面（2xx 响应）为高危。如果 80 端口完全没有响应，只作为提示信息，不算失败。nginx 的修复方法是 server { listen 80; return 301 https://$host$request_uri; }，然后添加 Strict-Transport-Security。可以用以下命令自行检查：curl -I http://your-site/

**它会标记哪些 HTTP 方法（TRACE、PUT、DELETE）？**

它向你的首页发送一次 OPTIONS 请求，读取 Allow 响应头；没有 Allow 时读取 Access-Control-Allow-Methods。声明了 TRACE、TRACK、PUT 或 DELETE 为中危；只有 PATCH 或 CONNECT 为低危；只有标准读取方法即为通过。如果服务器两个响应头都不发送，则不报告任何内容。声明了某个方法并不能证明它真的可用，所以修改之前先确认。TRACE 可能把请求头（包括 Cookie）原样回显给调用方（Cross-Site Tracing）。nginx 默认拒绝 TRACE；Apache 需设置 TraceEnable Off。把 PUT 和 DELETE 限制在确实需要它们的 API 路由上，并放在身份验证之后。可以用以下命令自行检查：curl -X OPTIONS -i https://your-site/

**它如何检测目录列表（"Index of /"）？**

它请求 7 个路径：/, /static/, /assets/, /uploads/, /images/, /files/ 和 /backup/。某个路径以 HTTP 200 返回、且页面标题或主标题以 "Index of /" 开头时，报告为中危（CWE-548），因为目录列表会暴露没有任何链接指向的文件，例如备份、导出文件和旧版本。如果都没有，检查即为通过；如果无法访问网站，则报告为未执行。nginx 的修复方法是 autoindex off;（这本就是默认值，所以要查找显式写出的 autoindex on），Apache 是 Options -Indexes，然后把所有敏感文件移出网站根目录。可以用以下命令自行检查：curl -s https://your-site/uploads/ | head -20

**它能发现混合内容和通过 HTTP 提交的表单吗？**

能，针对首页。它按服务器返回的原样读取 HTML（不执行 JavaScript），列出 src 或 href 以 http:// 开头的每个 script、link、iframe 或 img 标签。只要其中有 .js 或 .css 文件，结果就是中危，因为在传输途中被替换的脚本或样式表会在你的 HTTPS 页面里运行；只有图片和框架为低危；一个都没有则通过（CWE-311）。action 以 http:// 开头的表单报告为高危（CWE-319），因为在其中输入的一切，包括密码，都以明文发送。修复方法是把这些 URL 改为 https:// 或改成相对路径，并添加 Content-Security-Policy: upgrade-insecure-requests 响应头作为兜底。自己检查：curl -s https://your-site/ | grep -Eo "(src|href|action)=.http://[^ >]*"

**它在 robots.txt 和 security.txt 中检查什么？**

robots.txt：它获取 /robots.txt，如果返回 HTTP 200 且是文本文件，就读取前 8000 个字符中的 Disallow 行。包含 admin, internal, private, backup, config, staging, test, api, secret, dev 等词的 Disallow 路径报告为低危（CWE-200）：robots.txt 是公开的，只是请守规矩的爬虫不要访问，列出这些路径等于替它们做广告。请用登录保护它们，并用 noindex 让页面不进入搜索结果。security.txt：它请求 RFC 9116 规定的位置 /.well-known/security.txt，文件中有 Contact: 行时计为加分。没有该文件不扣分。RFC 9116 还要求 Expires: 字段，扫描不检查它，所以两者都要发布，例如 Contact: mailto:security@your-site, Expires: 2027-06-30T23:00:00Z。自己检查：curl -s https://your-site/.well-known/security.txt

**CAA 检查看什么？如何添加 CAA 记录？**

它通过 Cloudflare 的 DNS-over-HTTPS 查询你输入的主机名的 CAA 记录（RFC 8659）。找到记录则检查通过，证据中最多显示 5 条；一条都没有则报告 "No CAA record"，严重程度为低，因为这时任何公共证书颁发机构都可以为该名称签发证书。CAA 记录列出你实际使用的 CA，例如 your-site. IN CAA 0 issue "letsencrypt.org"，再加上 0 iodef "mailto:security@your-site" 用于接收报告。每个使用的 CA 加一行 issue；如果通配符证书要由另一家 CA 签发，再加一行 issuewild。自己核对：dig CAA your-site

**为什么扫描 www 或子域名时提示没有 CAA、SPF 或 DMARC？**

每项 DNS 检查都针对你输入的确切名称，所以 www.example.com 按 www.example.com 查询，而不是 example.com，而这三种记录的继承方式各不相同。CAA：证书颁发机构在 www.example.com 上找不到 CAA 记录时，会向上查到 example.com 并使用那里的记录（RFC 8659 第 3 节），因此子域名上的 "No CAA record" 可能是误报。DMARC：接收方会回退到组织域名的策略，该策略可以用 sp= 为子域名单独设置（RFC 9989，已取代 RFC 7489），因此 _dmarc.www.example.com 没有记录时，通常已由 _dmarc.example.com 覆盖。SPF：没有继承（RFC 7208），所以 "No SPF record" 对该名称是准确的；不发邮件的主机名可以发布 v=spf1 -all。DNS 查询失败也会显示为没有记录。请同时扫描不带 www 的主域名，或用 dig CAA example.com 和 dig TXT _dmarc.example.com 确认

## 更多隐私与安全

- [图片元数据清理](https://discuvr.org/image-scrub/?hl=zh-hans)

## 常一起使用

- [我的 IP 是什么](https://discuvr.org/what-is-my-ip/?hl=zh-hans)
- [密码生成器](https://discuvr.org/password-generator/?hl=zh-hans)

---

Part of Discuvr (https://discuvr.org/): Discuvr is a collection of free, focused tools for day-to-day professional work - office and data utilities, legal and compliance helpers, privacy and security checks, and developer formatters. Every tool has its own page, and most can also be called from ChatGPT or Claude over MCP.
