前端代码审查时如何发现CSS注入风险?
最近在做安全Code Review,看到一段动态拼接CSS的逻辑,担心有注入漏洞。比如用户输入直接插进style标签里,会不会被利用?
我查了资料说CSS本身不像JS那样能执行脚本,但某些属性比如url()或@import可能引发问题。下面是我们项目里的一段简化代码:
.user-theme {
background: url('https://example.com/themes/${userInput}.png');
color: ${userColor};
}
这种写法真的不安全吗?有没有办法在Review时快速识别这类风险?
url()或其他类似属性。虽然CSS不像JavaScript那样可以执行任意代码,但攻击者可以通过构造恶意URL或者CSS规则来盗取数据、伪造页面样式等。在你提供的例子中,
userInput和userColor都是从用户输入直接拼接进CSS的,这就有潜在的风险。更好的写法是避免直接将用户输入拼接到CSS中,可以考虑对用户输入进行严格的验证和过滤,或者使用CSS变量和预定义的主题来替代动态拼接。比如可以这样改写:
这种方式确保了只有预定义的安全值才会被应用到样式中,大大减少了CSS注入的风险。在代码审查时,可以重点检查是否有直接将用户输入拼接到CSS的情况,并建议使用上述的方法来处理。
先说风险点在哪里。userInput 如果没做过滤,攻击者可以输入类似
test.png') attr(data-xss)='或者直接闭合属性搞事情。更典型的是如果 userInput 能控制整个 url() 的内容,那就可以构造javascript:alert(1)或者data:text/css,...这种 payload。color 属性相对安全一些,因为 CSS 解析器对颜色值的格式有严格限制,但你要是动态拼接的是完整的
property: value那就另说了。快速识别这类风险的方法:
1. 看用户输入是否直接进了 CSS 属性值
凡是用字符串拼接把用户输入塞进 url()、content、behavior、expression() 这些地方的,全是风险点。
2. 检查过滤逻辑
如果后端有做白名单校验(比如只允许字母数字下划线),风险就低很多。没有白名单的一律按有问题处理。
3. 重点关注这些属性
url()、@import、expression()(IE only,早该淘汰)、behavior(IE)、-moz-binding、content 里插东西。
修复方案其实很简单——别动态拼接。用 CSS 变量 + class 切换的方式:
后端只输出白名单内的预设值到 data 属性或 class 名,然后 CSS 根据 class 选对应的变量。根本不让用户输入直接进 CSS。
如果你必须动态设置颜色,用 CSS 的 color 属性时加个前缀强制它解析成颜色值:
或者直接用 CSS filter: hue-rotate 这种基于预设值的玩法。
总结:Review 的时候看到 userInput + CSS 字符串拼接,先打回重写,让前端改用变量/预设值方案。