前端代码审查时如何发现CSS注入风险?

志红 Dev 阅读 61

最近在做安全Code Review,看到一段动态拼接CSS的逻辑,担心有注入漏洞。比如用户输入直接插进style标签里,会不会被利用?

我查了资料说CSS本身不像JS那样能执行脚本,但某些属性比如url()@import可能引发问题。下面是我们项目里的一段简化代码:

.user-theme {
  background: url('https://example.com/themes/${userInput}.png');
  color: ${userColor};
}

这种写法真的不安全吗?有没有办法在Review时快速识别这类风险?

我来解答 赞 6 收藏
二维码
手机扫码查看
2 条解答
夏侯绍桐
你说得对,这种动态拼接CSS的逻辑确实存在注入风险,特别是通过 url() 或其他类似属性。虽然CSS不像JavaScript那样可以执行任意代码,但攻击者可以通过构造恶意URL或者CSS规则来盗取数据、伪造页面样式等。

在你提供的例子中,userInputuserColor 都是从用户输入直接拼接进CSS的,这就有潜在的风险。更好的写法是避免直接将用户输入拼接到CSS中,可以考虑对用户输入进行严格的验证和过滤,或者使用CSS变量和预定义的主题来替代动态拼接。

比如可以这样改写:
// 假设我们有一个预定义的主题列表
const validThemes = ['light', 'dark', 'blue'];
const validColors = ['#000000', '#ffffff', '#0000ff'];

function getSafeTheme(userInput) {
return validThemes.includes(userInput) ? userInput : 'default';
}

function getSafeColor(userColor) {
return validColors.includes(userColor) ? userColor : '#000000'; // 默认颜色
}

const safeTheme = getSafeTheme(userInput);
const safeColor = getSafeColor(userColor);

document.body.style.backgroundImage = url('https://example.com/themes/${safeTheme}.png');
document.body.style.color = safeColor;


这种方式确保了只有预定义的安全值才会被应用到样式中,大大减少了CSS注入的风险。在代码审查时,可以重点检查是否有直接将用户输入拼接到CSS的情况,并建议使用上述的方法来处理。
点赞
2026-03-21 18:20
伟伟(打工版)
是的,你担心得对,这段代码确实有风险。

先说风险点在哪里。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 切换的方式:

:root {
--user-bg: url('https://example.com/themes/default.png');
--user-color: #333;
}

.user-theme {
background: var(--user-bg);
color: var(--user-color);
}


后端只输出白名单内的预设值到 data 属性或 class 名,然后 CSS 根据 class 选对应的变量。根本不让用户输入直接进 CSS。

如果你必须动态设置颜色,用 CSS 的 color 属性时加个前缀强制它解析成颜色值:

// 简单粗暴的做法
element.style.color = '#' + userColor.replace(/[^0-9a-f]/gi, '').slice(0, 6);


或者直接用 CSS filter: hue-rotate 这种基于预设值的玩法。

总结:Review 的时候看到 userInput + CSS 字符串拼接,先打回重写,让前端改用变量/预设值方案。
点赞
2026-03-16 14:37