
本文旨在解决在EJS模板中显示由CKEditor等富文本编辑器生成的HTML内容时,内容被默认转义为纯文本的问题。通过详细阐述EJS的HTML转义机制,并提供正确的解决方案——使用``而非``,确保富文本内容能以预期的格式渲染。同时,文章强调了在处理用户生成HTML内容时的安全考量,特别是跨站脚本攻击(XSS)的防范措施。
在现代Web应用开发中,富文本编辑器(如CKEditor)是创建博客、新闻或内容管理系统不可或缺的工具。它们允许用户以所见即所得的方式编辑内容,生成包含各种HTML标签(如<strong>、<em>、<p>等)的字符串。然而,当我们将这些HTML字符串存储到数据库并在Node.js/Express应用中使用EJS作为视图引擎进行渲染时,常常会遇到一个问题:内容以原始HTML字符串的形式显示,而不是浏览器解析后的样式。本教程将深入探讨这一问题的原因,并提供一个简洁而安全的解决方案。
EJS(Embedded JavaScript)是一个流行的模板引擎,它默认会对输出的内容进行HTML转义。这意味着,当你在EJS模板中使用<%= variable %>语法来显示一个字符串时,所有潜在的HTML特殊字符(如<、>、&、"、')都会被转换成对应的HTML实体(如、&、"、')。
这种默认行为是出于安全考虑,主要是为了防止跨站脚本攻击(XSS)。如果用户在富文本编辑器中输入了恶意脚本(例如<script>alert('XSS!');</script>),并且这些脚本未经转义直接渲染到页面上,那么访问该页面的用户就会执行这些恶意代码。通过转义,这些脚本会被当作普通文本显示,从而避免了攻击。
立即学习“前端免费学习笔记(深入)”;
问题示例:
假设你有一个Node.js/Express应用,使用CKEditor收集用户输入的博客内容,并将其存储为HTML字符串。在EJS模板中,你可能这样尝试显示它:
<!-- views/post.ejs -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文章详情</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
<div class="container">
<h1><%= title %></h1>
<div>
<%= content %>
</div>
</div>
</body>
</html>如果content变量包含以下HTML字符串:
<p><strong>Lorem ipsum</strong> dolor sit amet, consectetur adipisicing elit.<i> Quae maxime</i> dolore necessitatibus iste aliquid dolorum in nostrum repellat rerum atque?</p>
那么在浏览器中,你看到的结果将是:
<p><strong>Lorem ipsum</strong> dolor sit amet, consectetur adipisicing elit.<i> Quae maxime</i> dolore necessitatibus iste aliquid dolorum in nostrum repellat rerum atque?</p>
这显然不是我们想要的效果,因为HTML标签被当作普通文本显示,而不是被浏览器解析并应用样式。
EJS提供了一种特殊的语法来指示模板引擎不要对内容进行HTML转义,直接将其作为原始HTML渲染。这个语法就是<%- variable %>。
正确的使用方式:
将你的EJS模板中的内容输出语句从<%= content %>修改为<%- content %>:
<!-- views/post.ejs (修改后) -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文章详情</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
<div class="container">
<h1><%= title %></h1>
<div>
<%- content %> <!-- 注意这里的区别 -->
</div>
</div>
</body>
</html>通过这个简单的修改,当content变量包含HTML字符串时,EJS将不再对其进行转义,而是将其直接插入到HTML文档中。浏览器会解析这些HTML标签,并按照预期应用样式和结构。
预期输出效果:
Lorem ipsum dolor sit amet, consectetur adipisicing elit.Quae maxime dolore necessitatibus iste aliquid dolorum in nostrum repellat rerum atque?
为了更好地理解,我们提供一个包含CKEditor表单提交和EJS渲染的简化Node.js/Express应用示例。
1. Express应用 (app.js):
const express = require('express');
const bodyParser = require('body-parser');
const ejs = require('ejs');
const app = express();
const port = 3000;
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('public')); // 假设你的CKEditor和CSS文件在public目录下
// 存储文章内容的简单模拟
let posts = [];
// 首页 - 显示所有文章
app.get('/', (req, res) => {
res.render('index', { posts: posts });
});
// 发布新文章的表单页面
app.get('/compose', (req, res) => {
res.render('compose');
});
// 处理新文章提交
app.post('/compose', (req, res) => {
const newPost = {
title: req.body.postTitle,
content: req.body.postBody // CKEditor生成的HTML内容
};
posts.push(newPost);
res.redirect('/');
});
// 查看单篇文章页面
app.get('/posts/:id', (req, res) => {
const postId = req.params.id;
if (postId < posts.length) {
res.render('post', {
title: posts[postId].title,
content: posts[postId].content
});
} else {
res.status(404).send('文章未找到');
}
});
app.listen(port, () => {
console.log(`Server started on port ${port}`);
});2. CKEditor表单页面 (views/compose.ejs):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>撰写新文章</title>
<link rel="stylesheet" href="/css/styles.css">
<!-- 引入CKEditor库 -->
<script src="https://cdn.ckeditor.com/ckeditor5/41.4.2/classic/ckeditor.js"></script>
</head>
<body>
<div class="container">
<h1>撰写新文章</h1>
<form action="/compose" method="post">
<div class="form-group">
<label for="postTitle">文章标题</label>
<input type="text" class="form-control" id="postTitle" name="postTitle" required>
</div>
<div class="form-group">
<label for="editor">文章内容</label>
<textarea name="postBody" id="editor">在这里开始写作...</textarea>
</div>
<button type="submit" class="btn btn-primary">发布</button>
</form>
</div>
<script>
ClassicEditor
.create(document.querySelector('#editor'))
.then(editor => {
console.log(editor);
})
.catch(error => {
console.error(error);
});
</script>
</body>
</html>3. 文章详情页面 (views/post.ejs):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title><%= title %></title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
<div class="container">
<h1><%= title %></h1>
<div class="post-content">
<%- content %> <!-- 使用 <%- %> 来渲染CKEditor生成的HTML -->
</div>
<a href="/" class="btn btn-secondary">返回首页</a>
</div>
</body>
</html>虽然使用<%- variable %>可以解决HTML渲染问题,但这是一个潜在的安全风险点,尤其是在处理用户生成的内容时。
1. 跨站脚本攻击 (XSS) 风险: 如果你的应用允许用户提交任意HTML内容,并且你直接使用<%- content %>进行渲染,那么恶意用户就可以注入XSS攻击代码。例如,用户可以输入:
<p>我的文章</p><script>alert('您被攻击了!');</script>如果这段内容未经处理直接渲染,那么访问该页面的其他用户就会看到一个弹窗,甚至更严重的攻击(如窃取Cookie、重定向到恶意网站等)。
2. 内容净化 (HTML Sanitization): 为了安全地显示用户生成的HTML内容,强烈建议在服务器端对内容进行净化(Sanitization)。净化是指移除HTML字符串中所有不安全或不必要的标签和属性,只保留安全的、允许的HTML结构。
常用的HTML净化库包括:
净化流程建议:
示例(使用DOMPurify):
首先安装DOMPurify:
npm install dompurify jsdom
然后在Express路由中:
const express = require('express');
const bodyParser = require('body-parser');
const ejs = require('ejs');
const { JSDOM } = require('jsdom'); // 用于DOMPurify的JSDOM环境
const createDOMPurify = require('dompurify');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
// ... 其他代码 ...
app.post('/compose', (req, res) => {
const rawContent = req.body.postBody;
// 净化HTML内容
const cleanContent = DOMPurify.sanitize(rawContent, {
USE_PROFILES: { html: true } // 允许标准的HTML标签
// 可以根据需求配置允许的标签和属性
});
const newPost = {
title: req.body.postTitle,
content: cleanContent // 存储净化后的HTML内容
};
posts.push(newPost);
res.redirect('/');
});
// ... 其他代码 ...在EJS模板中显示由CKEditor等富文本编辑器生成的HTML内容时,核心在于理解EJS的HTML转义机制。使用<%- variable %>语法可以确保HTML内容被正确解析和渲染。然而,为了构建一个安全可靠的Web应用,务必在服务器端对用户生成的所有HTML内容进行严格的净化处理,以防范潜在的XSS攻击。遵循“输入验证,输出净化”的原则,将有效提升应用的安全性。
以上就是在EJS中正确渲染CKEditor生成的HTML内容的详细内容,更多请关注php中文网其它相关文章!
HTML怎么学习?HTML怎么入门?HTML在哪学?HTML怎么学才快?不用担心,这里为大家提供了HTML速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号