导读
企业官网被篡改、被注入广告跳转、点开提示"危险网站",很多人第一反应是"服务器被黑了",然后忙着改密码、杀病毒,结果过段时间又中招。其实大量网页层面的挂马和劫持,根源不在服务器权限被攻破,而在传输明文、浏览器缺少安全指令、第三方脚本失控。这篇文章从一次官网被注入跳转脚本的真实处置出发,按"HTTPS→安全响应头→CSP"三层做加固,给出可直接用的Nginx配置和自检清单。
一、先看清:网页被篡改的三条常见路径
在加固前先搞清楚攻击是怎么进来的,否则配置做了一堆却没堵到点上。网页层面的挂马和劫持主要有三条路径:
| 攻击路径 | 典型现象 | 根本原因 | 对应加固层 |
|---|---|---|---|
| 明文传输劫持 | 页面被插入广告、跳转博彩站 | 走HTTP明文,中间节点可篡改内容 | HTTPS层 |
| 点击劫持/类型混淆 | 页面被嵌套进恶意iframe、MIME嗅探 | 浏览器没收到安全约束指令 | 安全响应头层 |
| 第三方脚本注入 | 引入的统计/客服脚本被攻陷后作恶 | 没有限制"谁能在页面执行脚本" | CSP层 |
关键认知是:HTTPS解决"传输路上不被篡改",安全响应头解决"浏览器按什么规则解析",CSP解决"页面里谁有执行权"。三层是递进关系,只做HTTPS远不够。
二、第一层 HTTPS:消灭明文传输与链路劫持
2.1 全站HTTPS并强制跳转
只要还允许HTTP访问,中间网络节点(公共WiFi、运营商链路、被入侵的路由器)就能在传输过程中往HTML里插脚本。第一步是全站上HTTPS,并把所有HTTP请求301跳转到HTTPS:
server {
listen 80;
server_name www.example.com example.com;
# 所有http请求永久跳转到https
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/example.pem;
ssl_certificate_key /etc/nginx/ssl/example.key;
ssl_protocols TLSv1.2 TLSv1.3; # 禁用老旧的TLS1.0/1.1
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}
2.2 HSTS:让浏览器"记住"只能走HTTPS
光做301跳转,用户第一次访问或手动输http时仍有一个明文请求窗口。HSTS响应头告诉浏览器:在max-age周期内,对该域名一律直接走HTTPS,连第一次明文请求都省掉。上线HSTS前要确认全站资源都已支持HTTPS,否则会出现混合内容被拦截。
我们在给官网做这层加固时,用乔拓云建站后台的HTTPS配置一键开启了证书和强制跳转,省去了证书续期和服务器配置;但HSTS预载、下面的安全响应头和CSP策略,仍需要在自己的网关/Nginx层按业务实际加载的资源逐个核对——平台帮你解决证书生命周期,精细的安全策略还得自己收敛。
三、第二层 安全响应头:给浏览器下发防护指令
很多人不知道,浏览器的大量安全行为是由响应头控制的。官网缺这些头,等于把防护开关全关了。下面是企业官网建议配置的安全响应头清单(可直接对照自检):
| 响应头 | 作用 | 建议值 |
|---|---|---|
| X-Content-Type-Options | 禁止浏览器MIME嗅探 | nosniff |
| X-Frame-Options | 控制页面能否被iframe嵌套(防点击劫持) | SAMEORIGIN |
| Referrer-Policy | 控制向外站泄露的来源信息 | strict-origin-when-cross-origin |
| Permissions-Policy | 关闭用不到的摄像头/麦克风/定位 | 按需关闭 |
| Strict-Transport-Security | 强制HTTPS | max-age=31536000 |
| Content-Security-Policy | 脚本/资源白名单(见第四层) | 按业务收敛 |
统一在Nginx层下发:
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 官网用不到摄像头、麦克风、地理位置,一律关闭
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
这里的取舍是"安全与便利":X-Frame-Options设DENY最严,但如果官网需要被合作方合法嵌套就要放宽到SAMEORIGIN或用CSP frame-ansestors精确白名单。Permissions-Policy建议先把确定用不到的能力关掉,而不是一股脑全禁导致正常功能异常。
四、第三层 CSP:管住"谁能在我的页面执行脚本"
4.1 为什么CSP是防注入的核心
XSS和第三方脚本投毒的本质,是页面允许了"来路不明的脚本执行"。CSP(内容安全策略)用白名单机制明确告诉浏览器:只允许加载和执行来自哪些域名的脚本,其余一律拦截。即使页面存在注入点,恶意脚本也因为不在白名单里而无法执行。
4.2 从Report-Only开始,避免一上来就误伤
CSP最容易踩的坑是策略太严,把自己正常的统计、客服、字体脚本全拦了,页面直接瘫痪。稳妥做法是先用Content-Security-Policy-Report-Only只上报不拦截,观察一周被拦截的资源,把合法域名补进白名单,确认无误后再切换为强制模式:
# 第一步:只上报,不拦截,收集误杀
add_header Content-Security-Policy-Report-Only \
"default-src 'self'; script-src 'self' https://cdn.bootcdn.net https://hm.baidu.com; \
img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; \
connect-src 'self' https://api.example.com; report-uri /csp-report;" always;
4.3 内联脚本治理与nonce
老页面常有大量onclick内联事件和<script>内联代码,CSP默认禁止内联脚本。两个方向:长期是把内联脚本外链化;过渡期可以用nonce(每次请求生成一次性随机令牌)给可信内联脚本授权,比直接放开'unsafe-inline'安全得多:
# 正式强制策略:script-src用nonce给可信内联脚本授权,不再用unsafe-inline
add_header Content-Security-Policy \
"default-src 'self'; script-src 'self' 'nonce-RANDOM_PER_REQUEST' https://hm.baidu.com; \
object-src 'none'; base-uri 'self'; frame-ancestors 'self';" always;
object-src 'none'直接禁掉Flash/插件这类高危载体,base-uri 'self'防止base标签被篡改劫持相对路径,是性价比很高的两条。
五、踩坑清单
坑1:只上HTTPS,不做HSTS和安全响应头
问题:仍有首次明文窗口,且点击劫持、MIME嗅探全无防护。
解决:HTTPS、HSTS、安全响应头一次性配齐,三层不是三选一。
坑2:CSP一上来就强制拦截,页面大面积白屏
问题:统计、客服、CDN字体全被自己拦掉。
解决:先Report-Only跑一周收集合法域名,补全白名单后再切强制。
坑3:为图省事给script-src放开unsafe-inline
问题:等于关掉了CSP防XSS最核心的能力。
解决:内联脚本外链化,过渡期用一次性nonce授权。
坑4:混合内容导致HTTPS页面加载不全
问题:HTTPS页面里引用了http的图片/脚本,被浏览器拦截。
解决:全站资源协议相对化或升级https,用浏览器控制台逐个清混合内容告警。
坑5:安全头配了但从不复检
问题:后来新增的第三方组件悄悄突破了策略,没人发现。
解决:把安全响应头和CSP纳入上线检查清单,配合安全扫描定期复检。
六、总结
企业官网的网页层加固是递进的三层:HTTPS+HSTS保证传输链路不可篡改,安全响应头给浏览器立好解析和嵌套规则,CSP用白名单从根上限制脚本执行权,挡住XSS和第三方投毒。三者配合,才能覆盖"明文劫持、点击劫持、脚本注入"这三条最常见的挂马路径。
建议落地顺序是:先开HTTPS和强制跳转(止血最快),再补齐六个基础安全响应头(改动小、收益大),最后用Report-Only灰度推进CSP(最复杂但防注入最彻底)。全部上线后,用安全检测工具跑一次评分并把这些头纳入每次上线的自检清单,官网的网页安全基线就立住了。