I tested my security extension against 20 real sites and found three bugs - in my own tool
I tested my security extension against 20 real sites and found three bugs - in my own tool
我用 20 个真实网站测试了我的安全插件,结果发现了三个 Bug——全在我的工具里
I built ‘QuickAudit’, a browser extension that runs ten OWASP-style security checks on whatever web page you’re currently viewing (headers, cookie flags, mixed content, vulnerable JS libraries via OSV.dev, exposed files). Before publishing, I pointed it at a corpus of 20 real-world websites- ten major security vendor sites and ten older enterprise properties - expecting a quick validation exercise to confirm everything worked. Instead, it turned into a bug hunt. And the bugs were all mine. Here are the three biggest false-positive traps I uncovered in my own code, and how testing against a live corpus changed the architecture.
我开发了一款名为“QuickAudit”的浏览器插件,它能对你当前浏览的网页运行十项 OWASP 风格的安全检查(包括 HTTP 标头、Cookie 标志、混合内容、通过 OSV.dev 检测易受攻击的 JS 库以及暴露的文件等)。在发布之前,我用它测试了 20 个真实网站——包括 10 个大型安全厂商网站和 10 个老牌企业站点——本以为这只是个确认功能正常的快速验证过程,结果却变成了一场“找茬”之旅,而且所有的 Bug 都是我自己的。以下是我在代码中发现的三个最大的误报陷阱,以及针对真实语料库的测试如何改变了我的架构设计。
Bug 1: I was auditing Cloudflare’s challenge page and calling it your website
Bug 1:我审计的是 Cloudflare 的验证页面,却把它当成了你的网站
During the corpus test, QuickAudit reported ‘sourceforge.net’ as missing HTTP Strict Transport Security (HSTS). Surprised, I opened terminal and ran ‘curl -I https://sourceforge.net’. The header was right there: ‘strict-transport-security: max-age=31536000; includeSubDomains; preload’. Why was my extension flagging it? It turned out my automated scan had been served a Cloudflare bot-protection interstitial page in 44ms. The extension was faithfully auditing the challenge page’s headers, not Sourceforge’s actual production application.
在语料库测试期间,QuickAudit 报告称 ‘sourceforge.net’ 缺少 HTTP 严格传输安全(HSTS)。我很惊讶,于是打开终端运行了 curl -I https://sourceforge.net。标头明明就在那里:strict-transport-security: max-age=31536000; includeSubDomains; preload。为什么我的插件会报错?原来我的自动化扫描在 44 毫秒内被重定向到了 Cloudflare 的机器人防护拦截页面。插件忠实地审计了拦截页面的标头,而不是 Sourceforge 真正的生产环境应用。
The Lesson: Any security tool that programmatically fetches a URL rather than inspecting a real, fully completed browser navigation inherits this bug — and it fails toward confident wrongness, which is the worst direction for a security tool. The Fix: I added a ‘detectChallenge()’ check that inspects headers like ‘cf-mitigated’, ‘x-amzn-waf-action’, and interstitial page titles. When triggered, QuickAudit now explicitly skips header-dependent checks with an explanation rather than presenting false findings about a page that isn’t yours.
经验教训:任何通过程序抓取 URL 而不是检查真实、完整浏览器导航的安全工具,都会继承这个 Bug——而且它会以一种“自信地出错”的方式失败,这对安全工具来说是最糟糕的情况。修复方案:我添加了一个 detectChallenge() 检查,用于识别 cf-mitigated、x-amzn-waf-action 等标头以及拦截页面的标题。当触发此机制时,QuickAudit 现在会明确跳过依赖标头的检查,并给出解释,而不是针对一个根本不是你目标的页面提供虚假的发现结果。
Bug 2: I misread a web spec I’d have sworn I knew by heart
Bug 2:我误读了一个自以为烂熟于心的 Web 规范
My Referrer-Policy auditor initially flagged ‘origin-when-cross-origin’ as a high-risk failure, bucketing it with ‘unsafe-url’ for “leaking full path and query parameters cross-origin.” Except it doesn’t. According to the W3C spec, ‘origin-when-cross-origin’ sends the full URL (origin + path + query) for ‘same-origin’ requests, but strips the path and sends ‘only the origin’ for cross-origin requests.
我的 Referrer-Policy(引用来源策略)审计器最初将 origin-when-cross-origin 标记为高风险失败,并将其与 unsafe-url 归为一类,理由是“跨源泄露完整路径和查询参数”。但事实并非如此。根据 W3C 规范,origin-when-cross-origin 在“同源”请求时发送完整 URL(源 + 路径 + 查询),但在跨源请求时会剥离路径,仅发送“源”。
The Lesson: When you encode web security standards into lookup tables or regex rules, your own misconceptions get frozen into code and shipped to users. The Fix: I updated the policy matrix to reflect the exact spec table. ‘origin-when-cross-origin’ now passes cleanly.
经验教训:当你将 Web 安全标准编码为查找表或正则表达式时,你自己的误解也会被固化到代码中并交付给用户。修复方案:我更新了策略矩阵,使其完全符合规范表格。现在 origin-when-cross-origin 可以顺利通过检查了。
Bug 3: Pedantry disguised as a security finding
Bug 3:伪装成安全发现的教条主义
In my original code, any site using ‘X-Frame-Options: SAMEORIGIN’ without a modern CSP ‘frame-ancestors’ directive generated a warning. Technically, CSP ‘frame-ancestors’ is the modern standard. But in practice, ‘every major current browser honors X-Frame-Options’. Flagging this on sites like ‘stripe.com’, ‘python.org’, and ‘nasa.gov’ was pure pedantry.
在我最初的代码中,任何使用 X-Frame-Options: SAMEORIGIN 但没有配置现代 CSP frame-ancestors 指令的网站都会产生警告。从技术上讲,CSP frame-ancestors 是现代标准。但在实践中,所有主流浏览器都支持 X-Frame-Options。在 stripe.com、python.org 和 nasa.gov 等网站上标记此项纯属教条主义。
The Lesson: A tool that warns you “technically you could be more fashionable” trains developers to ignore warnings. Then, when a real severity-1 finding occurs, they ignore that too. The Fix: ‘X-Frame-Options’ now passes with a neutral informational note rather than a warning.
经验教训:一个只会警告你“从技术上讲你可以更时髦一点”的工具,会训练开发者去忽略警告。这样当真正的一级严重漏洞出现时,他们也会选择忽略。修复方案:现在 X-Frame-Options 会以中性的提示信息通过,不再触发警告。
The Part I’d Repeat on Any Project: The Catch-All SPA Trap
我会在任何项目中重复的操作:单页应用(SPA)的“全捕获”陷阱
One of the checks in QuickAudit checks for exposed sensitive files (‘/.env’, ‘/.git/HEAD’, ‘/.htpasswd’). If you write a naive check that just requests ‘/.env’ and checks for HTTP 200, ‘every Single Page Application (SPA) with a catch-all route will report 14 critical security vulnerabilities.’ Why? Because SPAs return ‘HTTP 200 OK’ with ‘index.html’ for ‘any’ requested path.
QuickAudit 的其中一项检查是检测暴露的敏感文件(如 /.env、/.git/HEAD、/.htpasswd)。如果你写了一个简单的检查,仅仅请求 /.env 并检查是否返回 HTTP 200,那么“每一个带有全捕获路由(catch-all route)的单页应用(SPA)都会报告 14 个严重安全漏洞。”为什么?因为 SPA 对于“任何”请求路径都会返回 HTTP 200 OK 以及 index.html。
To solve this, QuickAudit performs a two-step fingerprint: Requests a random nonexistent path (e.g. ‘/random-a8f92z’) to baseline how the server handles garbage requests. Requires the response body to match a strict content signature for that file type (‘/.git/HEAD’ must match ‘^ref:\s+refs/’, ‘/.env’ must match ‘KEY=value’ lines and ‘must not be HTML’). Evidence snippets are also redacted in local storage so a screenshot of a report never leaks the secret it found.
为了解决这个问题,QuickAudit 执行了两步指纹识别:首先请求一个随机的不存在路径(例如 /random-a8f92z),以建立服务器如何处理垃圾请求的基准。然后要求响应体必须匹配该文件类型的严格内容签名(/.git/HEAD 必须匹配 ^ref:\s+refs/,/.env 必须匹配 KEY=value 行且“不能是 HTML”)。证据片段也会在本地存储中进行脱敏处理,确保报告截图永远不会泄露发现的机密信息。
Summary & Try It Out
总结与试用
Running against 20 real sites took an afternoon and completely refactored three core checks. If you’re building any tool that renders a verdict on someone else’s system, real-world corpus testing isn’t optional and handling the edge cases gracefully is what builds developer trust.
针对 20 个真实网站的测试花了一个下午,并彻底重构了三个核心检查项。如果你正在构建任何会对他人系统做出评估的工具,真实语料库测试是必不可少的,而优雅地处理边缘情况正是建立开发者信任的关键。
QuickAudit is 100% free, privacy-first (runs locally, no accounts or telemetry), and available on all major stores: QuickAudit 是 100% 免费且隐私优先的(本地运行,无需账户,无遥测数据),现已在各大商店上架:
🌐 Chrome Web Store 🌐 Microsoft Edge Add-ons 🦊 Firefox AMO 💻 Source Code
I’d love to hear feedback or edge cases from other web & security engineers! 欢迎其他 Web 和安全工程师提供反馈或分享你们遇到的边缘情况!