一、Web安全基础:从一次渗透测试说起
我记得刚入行那会儿,接了个企业网站的安全评估。代码写得挺规范,业务逻辑也没大毛病。但当我随手在搜索框输入 <script>alert('xss')</script> 时,弹窗出来了。嗯,就是这么简单的一个测试,整个后台会话就暴露了。那次之后我养成了一个习惯——写任何Web代码前,先想想「如果用户不按套路出牌怎么办」。
Web安全说白了就是一场攻防博弈。攻击者永远在找「预期之外」的输入点。而我们作为开发者,要做的就是把所有入口都堵死。今天咱们就聊聊几个最基础也最重要的安全机制。
1.1 HTTP安全头:你的第一道防线
HTTP响应头是服务器告诉浏览器「该怎么处理这个页面」的指令。很多安全问题其实可以通过几个简单的头信息解决。我个人习惯在项目初始化时就把这些头加上,省得后面忘了。
Content-Security-Policy (CSP)
CSP 是个好东西。它告诉浏览器「哪些来源的内容是可信的」。举个例子:
Content-Security-Policy: default-src 'self'; script-src 'self' https://apis.example.com; style-src 'self' 'unsafe-inline'
这条策略的意思是:
- 所有资源默认只从本站加载
- 脚本只能从本站和指定API域名加载
- 样式可以从本站加载,也允许内联样式
我在项目中遇到过这样的情况:第三方广告脚本被注入了恶意代码,但因为CSP限制了脚本来源,攻击根本没生效。说实话,那次真是庆幸提前配了CSP。
Content-Security-Policy-Report-Only 模式观察一段时间。
Strict-Transport-Security (HSTS)
HSTS 强制浏览器只能通过 HTTPS 访问你的站点。配置很简单:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
这里 max-age 单位是秒,31536000 就是一年。includeSubDomains 表示子域名也强制 HTTPS。preload 是申请加入浏览器预加载列表。
X-Frame-Options
这个头防止你的页面被嵌入到 iframe 里。攻击者经常用这个做点击劫持。设置方式:
X-Frame-Options: DENY // 完全禁止
X-Frame-Options: SAMEORIGIN // 只允许同源页面嵌入
我个人建议大部分场景用 SAMEORIGIN,除非你确定不需要任何页面嵌套。
1.2 跨站脚本(XSS)防御
XSS 分三种:反射型、存储型、DOM 型。说白了都是「用户输入被当成了代码执行」。防御思路其实就两条:
- 输入过滤——把危险字符转义或剔除
- 输出编码——在展示数据时做上下文相关的编码
看个实际例子。假设有个评论功能:
// 错误做法:直接拼接
document.getElementById('comment').innerHTML = userInput;
// 正确做法:使用 textContent
document.getElementById('comment').textContent = userInput;
// 或者用安全的模板引擎
const safeHtml = escapeHtml(userInput);
你想想看,如果用户输入 <img src=x onerror=alert(1)>,第一种做法直接执行了脚本,第二种只是显示一段文本。区别就这么大。
dangerouslySetInnerHTML 和 v-html 这些「逃生舱」——用了它们就等于绕过了框架的保护。
1.3 跨站请求伪造(CSRF)防御
CSRF 的攻击逻辑是:用户登录了银行网站,然后不小心访问了攻击者的页面,那个页面偷偷发起了一个转账请求。因为浏览器会自动带上 cookie,银行服务器以为是用户本人操作。
防御 CSRF 的常见方法:
| 方法 | 原理 | 推荐度 |
|---|---|---|
| CSRF Token | 每个表单带一个随机 token,服务端验证 | ⭐⭐⭐⭐⭐ |
| SameSite Cookie | 设置 Cookie 的 SameSite 属性 | ⭐⭐⭐⭐ |
| Referer/Origin 验证 | 检查请求来源 | ⭐⭐⭐ |
| 二次验证 | 敏感操作要求输入密码或验证码 | ⭐⭐⭐⭐⭐ |
我最推荐的做法是「CSRF Token + SameSite Cookie」组合。Token 放在表单隐藏域里,服务端对比 session 中存的 token。SameSite 设为 Lax 或 Strict,能挡住大部分跨站请求。
// 生成 Token 的伪代码
session['csrf_token'] = randomString(32);
// 表单中嵌入
<input type="hidden" name="csrf_token" value="<?= session['csrf_token'] ?>">
// 服务端验证
if ($_POST['csrf_token'] !== session['csrf_token']) {
die('CSRF 攻击检测');
}
1.4 会话管理安全
会话管理是 Web 安全的重灾区。说白了就是「怎么证明你是你」。常见问题包括:
- Session ID 太简单,容易被猜解
- Session 固定攻击
- 会话超时设置不合理
- Cookie 未设置安全属性
我建议的会话管理最佳实践:
- 使用安全的随机数生成器——别用
rand(),用random_bytes()或openssl_random_pseudo_bytes() - 登录后重新生成 Session ID——防止 Session 固定攻击
- 设置合理的超时时间——15-30分钟无操作自动过期
- Cookie 设置 HttpOnly 和 Secure 标志——防止 XSS 窃取和中间人攻击
// 安全的 Cookie 设置示例
Set-Cookie: sessionid=abc123; HttpOnly; Secure; SameSite=Lax; Max-Age=1800
这里 HttpOnly 让 JavaScript 无法读取 Cookie,Secure 保证只在 HTTPS 下传输,SameSite=Lax 防御 CSRF,Max-Age=1800 设置半小时过期。
知识体系总览
下面这张图把本章的核心知识点串起来了。你可以看到,Web 安全不是孤立的几个点,而是一整套防御体系。
你看,这四层防御是层层递进的。HTTP 安全头在最外层挡住大部分简单攻击,XSS 防御处理输入输出,CSRF 防御防止伪造请求,会话管理确保身份安全。每一层都不可或缺。
公众号:蓝海资料掘金营,微信 deep3321