基于Node/Express搭建简易CMS:HTML存储与防SQL注入方案咨询
嘿,这个需求我刚好折腾过类似的,给你梳理下安全实现HTML存储的关键步骤——既要防SQL注入,还要考虑HTML内容带来的XSS风险,毕竟你用的是WYSIWYG编辑器,用户输入的内容自由度很高:
不管你用MySQL还是SQLite,这一步是底线,绝对不能省:
用参数化查询代替字符串拼接:别图省事直接把用户输入拼进SQL语句里,哪怕是CKEditor输出的HTML也不行。用数据库驱动的参数化查询功能,它会自动把用户输入当作纯数据处理,不会解析成SQL指令。
举个mysql2的例子:// 错误示范:直接拼接,分分钟被注入 db.query(`INSERT INTO posts (title, content) VALUES ('${title}', '${htmlContent}')`); // 正确姿势:参数化查询,安全得很 db.query('INSERT INTO posts (title, content) VALUES (?, ?)', [title, htmlContent], (err, results) => { // 处理结果逻辑 });SQLite的用法类似,比如用sqlite3库的话,也是用
?作为占位符,把参数放在数组里传进去。ORM尽量用内置方法,少写原始查询:如果你用Sequelize这类ORM,优先用
Model.create()、Model.update()这些封装好的方法,它们内部已经做了参数化处理。除非万不得已要写原始SQL,那也一定要用ORM提供的参数化方式,别自己拼串。
存HTML本身没问题,但用户可能在CKEditor里插入恶意脚本(比如<script>偷cookie的代码</script>),直接存进去再展示就会出大事。所以必须在存储前做净化:
用DOMPurify做后端净化:这个库专门用来清洗HTML,只保留安全的标签和属性,过滤掉所有恶意代码。Node环境下需要配合JSDOM模拟DOM环境,代码示例:
const DOMPurify = require('dompurify'); const { JSDOM } = require('jsdom'); // 初始化DOMPurify const window = new JSDOM('').window; const purify = DOMPurify(window); // 净化用户提交的HTML内容 const safeHtml = purify.sanitize(htmlContent); // 把净化后的safeHtml存入数据库就行你还可以自定义配置,比如只允许
<h1>、<p>、<img>这些常用标签,或者限制<img>的src只能是合法域名,具体看你的需求。数据库字段选对类型:MySQL用
TEXT或者LONGTEXT(如果内容很长的话),SQLite直接用TEXT类型就够了。不用刻意对HTML做转义,因为参数化查询已经处理了SQL注入的问题,净化后的HTML直接存就行。
后端是核心,但前端也可以提前拦一波,减少恶意内容提交:
- 限制CKEditor的允许内容:在初始化CKEditor的时候,配置
allowedContent来指定允许的标签和属性,或者直接移除不需要的插件。比如:
注意:前端防护只是辅助,绝对不能依赖它,因为用户可以绕过前端直接发HTTP请求,后端必须做最终的验证。ClassicEditor .create(document.querySelector('#editor'), { allowedContent: 'h1 h2 p strong em ul ol li img[src,alt];', // 只允许这些标签和属性 removePlugins: 'MediaEmbed,Table' // 去掉用不到的插件,减少风险 }) .catch(error => { console.error(error); });
如果你的CMS是多用户场景(比如多个作者),展示HTML的时候可以加个内容安全策略(CSP),在Express里设置响应头:
app.use((req, res, next) => { res.setHeader( 'Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" ); next(); });
CSP可以限制页面只能加载自己域名的资源,就算有漏网的恶意脚本,浏览器也会阻止它执行,多一层保障。
总结一下核心流程:前端CKEditor限制内容 → 后端接收后用DOMPurify净化HTML → 用参数化查询存入数据库 → 展示时配合CSP,这样就能安全实现HTML内容的存储和展示啦。
内容的提问来源于stack exchange,提问作者Ollie Williams

