应用程序安全
DHTMLX Gantt 是一个客户端 JavaScript 库,可无缝集成 Gantt 功能到各种 Web 应用程序中。 我们不会通过限制 Gantt 的功能来提升安全性,但这也意味着不会限制其能力。 这样,您可以根据项目需求自定义大部分 Gantt 功能。
需要注意的是,DHTMLX Gantt 本身并不负责防护诸如 SQL 注入、XSS 和 CSRF 攻击等威胁。 确保项目安全,取决于您如何配置和保护您的应用程序。 本文将为您提供关于 HTML 清洗的有用见解和建议。
基本安全措施
网络安全是一个广泛且复杂的领域,不可能用一个简单的清单完全覆盖。 不过,以下实用步骤涵盖了基础内容,有助于降低常见风险。
1. 在应用程序中使用内容安全策略(CSP)
添加如下 CSP 头部可以阻止 XSS 脚本在您的应用中运行:
Content-Security-Policy: script-src 'self'
您的应用可能需要更详细的策略,但阻止内联脚本执行可以防止许多 XSS 和 CSRF 攻击。
2. 在后端对用户输入进行清洗后再保存到数据库
添加新记录时,避免直接原样保存用户输入:
db.query("INSERT INTO gantt_tasks(text, start_date, duration, progress, parent)"
+ " VALUES (?,?,?,?,?)",
[task.text, task.start_date, task.duration, task.progress, task.parent])
建议先验证输入格式并清除有害内容。 例如,在 Node.js 中可以使用 DOMPurify 这类库:
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
...
db.query("INSERT INTO gantt_tasks(text, start_date, duration, progress, parent)"
+ " VALUES (?,?,?,?,?)",
[task.text, task.start_date, task.duration, task.progress, task.parent]
.map((input) => DOMPurify.sanitize(input))
3. 在渲染数据前对 HTML 实体进行转义
为了防止显示数据时执行 HTML 标记,务必在将用户输入传递给 Gantt 前转义 HTML 字符。 可以使用 validator 库实现:
const validator = require('validator');
...
// GET /data
Promise.all([
db.query("SELECT * FROM gantt_tasks"),
db.query("SELECT * FROM gantt_links")
]).then(results => {
let tasks = results[0],
links = results[1];
tasks.forEach((task) => {
Object.entries(task).forEach(([key, value]) => {
if(typeof value === "string") {
task[key] = validator.escape(value); //#!
}
});
task.open = true;
task.start_date = task.start_date.format("YYYY-MM-DD hh:mm:ss");
});
links.forEach((link) => {
Object.entries(link).forEach(([key, value]) => {
if(typeof value === "string") {
link[key] = validator.escape(value); //#!
}
});
});
res.send({
tasks,
links
});
4. 如果使用 SQL 数据库,避免通过字符串拼接创建 SQL 查询。应使用参数化查询、ORM 或 Query Builder。
这样可以防止 SQL 注入攻击。 切勿在 SQL 查询中直接使用未经转义或未检查的用户输入。 如果目前代码存在这种情况,请考虑切换到参数化查询或使用 SQL 库提供的转义函数。
5. 最后但同样重要:请咨询网络安全专家并遵循公司认可的安全策略
安全是一个持续的过程。 通过遵循上述步骤、遵守组织政策,并让安全专家审核您的工作,可以最大限度减少常见 Web 威胁。
基础措施介绍完毕后,下面来看一些 Gantt 相关的具体安全注意事项。
客户端 Gantt 的易受攻击区域
首先,在客户端添加像 Gantt 这样复杂功能时需要注意以下要点:
- DHTMLX Gantt 运行在客户端,所有从服务器加载的数据都会被原样使用。
由于数据存储在服务器端,最大风险通常来自那里。但后端防护超出了 DHTMLX Gantt 的范围。 - 攻击者可能通过 DevTools(自我 XSS 攻击)诱骗用户运行恶意代码,从而绕过安全措施。
任何粘贴到任务文本中的代码,其行为与通过 DevTools 输入相同。 - 如果攻击者获取到 Gantt 实例对象的访问权限,任何保护措施都可能被绕过。
他们可以更改 Gantt 配置并获得完全控制权。
DHTMLX Gantt 中可能存在安全问题的易受攻击区域包括:
- 用户输入并保存的数据
- 展示的 Gantt 数据(文本和可视元素)
- 与 Gantt 数据交互的 自定义 HTML 元素
- 对 Gantt 对象本身的访问
下面我们将详细探讨这些问题。
隔离 Gantt 访问
保护 Gantt 的第一步是将其与被攻破的组件或被诱导的用户(自我 XSS)隔离开来。
如果攻击者获得了应用配置文件(包括 Gantt 配置文件)的访问权限, 任何已采取的 XSS 防护措施都可能失效,因此本场景不在本文讨论范围内。
应用完全加载后,如果攻击者能访问 Gantt 实例对象,就可以修改任何内容并重写函数。 因此,将 Gantt 隔离在项目内部非常重要。
实现方法是在函数内部创建独立的 Gantt 实例,这样函数内部的代码外部无法访问。
默认情况下,Gantt 会在 gantt 对象上创建新实例。 在您的函数内部,使用 const 或 let 声明新变量并存储 Gantt 实例,使其在外部作用域不可见。
function addGantt(){
const gantt = Gantt.getGanttInstance();
}
addGantt()
您也可以使用不同的变量名,以避免与全局 gantt 对象混淆:
function addGantt(){
const protectedGantt = Gantt.getGanttInstance();
}
addGantt()
在防止非授权访问 Gantt 后,接下来要关注数据在 Gantt 图中的输入和展示方式。
Gantt 中的数据输入
这是攻击者可能利用的关键领域,可能危及您的 Gantt 安全。
数据输入点是 XSS 攻击的常见目标。 在 Gantt 组件中,数据可通过以下方式修改:
- lightbox