Security面板实战:深入解析前端安全检测与防护技巧
又踩坑了,Security面板里一堆红标
前几天上线一个新功能,测试环境跑得好好的,结果一上生产,Chrome DevTools 的 Security 面板直接给我刷了一堆红色警告。点开一看,全是 Mixed Content(混合内容)的问题,什么“加载了不安全的脚本”“图片通过 HTTP 加载”之类的。我第一反应是:我们不是全站 HTTPS 吗?怎么还会出这种问题?
折腾了半天才发现,问题出在第三方资源引用上。我们项目里嵌了一个老系统的 iframe,那个 iframe 里还引用了几个外部 JS 和图片,而这些资源地址写的是硬编码的 http://。浏览器在 HTTPS 页面里加载 HTTP 资源,自然就被 Security 面板标记为不安全了。
试了三种方案,前两种都翻车了
一开始我想偷懒,直接在 HTML 里把所有资源链接改成 //example.com/xxx 这种协议相对路径。但很快发现不行——那个老系统根本没配 HTTPS,强行用 https:// 访问会直接 404。所以这条路走不通。
然后我想到用 CSP(Content Security Policy)来绕过?比如加个 upgrade-insecure-requests 指令。查了下文档,这玩意儿确实能自动把页面里的 HTTP 请求升级成 HTTPS。但问题来了:如果目标服务器根本不支持 HTTPS,那请求就直接失败,连 fallback 都没有。果然,一加上这条策略,iframe 里的内容全白了,JS 报错 404。行不通。
这里我踩了个坑:以为 CSP 是万能的,其实它只是“强制升级”,而不是“智能降级”。一旦对方不支持 HTTPS,你就彻底没戏了。
最后靠服务端代理搞定
既然前端改不了,那就只能让服务端当“中间人”了。我们的后端是 Node.js,于是我让后端同事加了个简单的代理接口,把那些不安全的资源通过我们自己的 HTTPS 域名转发一下。比如原来 iframe 里是:
<img src="http://old-system.com/image.jpg">
现在改成:
<img src="/api/proxy?target=http%3A%2F%2Fold-system.com%2Fimage.jpg">
后端代码很简单,就是个转发逻辑(注意做了基础的 URL 校验,避免 SSRF):
const express = require('express');
const axios = require('axios');
const app = express();
// 简单白名单,只允许代理特定域名
const ALLOWED_HOSTS = ['old-system.com', 'legacy-cdn.net'];
app.get('/api/proxy', async (req, res) => {
const { target } = req.query;
if (!target) {
return res.status(400).send('Missing target');
}
try {
const url = new URL(target);
if (!ALLOWED_HOSTS.includes(url.hostname)) {
return res.status(403).send('Host not allowed');
}
// 只允许 http,避免递归代理
if (url.protocol !== 'http:') {
return res.status(400).send('Only HTTP targets allowed');
}
const response = await axios({
method: 'GET',
url: target,
responseType: 'stream',
timeout: 5000
});
// 透传 Content-Type
res.set('Content-Type', response.headers['content-type'] || 'application/octet-stream');
response.data.pipe(res);
} catch (err) {
console.error('Proxy error:', err.message);
res.status(500).send('Proxy failed');
}
});
这样,所有资源都通过我们自己的 HTTPS 域名加载,Security 面板的红标立马消失了。而且因为是服务端代理,浏览器完全不知道背后是 HTTP,自然也不会报 Mixed Content。
但还有个小尾巴没解决
不过这个方案有个小问题:如果老系统的资源特别大(比如几十 MB 的视频),代理会占用我们服务器的带宽和内存。目前我们只代理了图片和小脚本,暂时没遇到性能问题。但如果以后要代理大文件,可能得加缓存或者限流。
另外,CORS 也得注意。如果被代理的资源需要携带 Cookie 或自定义 Header,还得在代理层处理 CORS 头。好在我们这个场景只是静态资源,不需要认证,所以没碰上这问题。
Security 面板到底在查什么?
顺便说一句,很多人其实不太清楚 Security 面板到底在监控啥。简单说,它主要干三件事:
- 检查页面是否通过 HTTPS 加载(如果不是,直接标红)
- 检查页面内所有子资源(JS、CSS、图片、iframe、XHR 等)是否也是 HTTPS(Mixed Content)
- 检查证书是否有效、是否被吊销、是否匹配域名
其中 Mixed Content 分两类:被动型(图片、音频、视频)和主动型(JS、CSS、iframe、XHR)。主动型的会被浏览器直接 block,被动型的可能只是警告(但 Chrome 现在也默认 block 了)。所以别指望“图片用 HTTP 应该没事”——现在基本全给你拦了。
另外,就算你用了 HTTPS,如果证书是自签名的或者过期了,Security 面板也会标红。不过我们这次的问题纯属 Mixed Content,证书没问题。
核心代码就这几行
总结下来,最靠谱的方案还是服务端代理。前端只需要改一下资源 URL,后端加个带白名单的代理接口。完整流程:
- 前端把所有不安全的资源 URL 替换为
/api/proxy?target=encodeURIComponent(原URL) - 后端验证 target 是否在白名单内,且协议为 http
- 后端发起请求,把响应流直接 pipe 给前端
关键点在于:**白名单必须做**,否则容易被用来做 SSRF 攻击。比如攻击者传个 target=http://169.254.169.254/latest/meta-data 就能探测内网。所以别偷懒,校验 hostname 是必须的。
对了,如果你用的是 Nginx,也可以用 proxy_pass 实现类似效果,不用写 Node 代码。比如:
location /proxy/ {
set $target $arg_target;
# 这里需要更严格的校验,Nginx 配置里不好做复杂逻辑,建议配合 Lua 或只代理固定后端
proxy_pass $target;
proxy_set_header Host $host;
}
但 Nginx 方案灵活性差一点,动态白名单不好搞,所以我还是推荐用应用层代理。
结尾碎碎念
这次问题说大不大,说小不小。如果上线前多看一眼 Security 面板,其实能提前发现。但谁让测试环境没配 HTTPS 呢……血的教训:**本地和测试环境尽量和生产一致,尤其是协议层面**。
以上是我踩坑后的总结,如果你有更好的方案(比如用 Service Worker 拦截?或者 CDN 层面处理?),欢迎评论区交流。这个代理方案虽然 work,但总觉得有点重,要是有更轻量的前端方案就好了。

暂无评论