DHTMLX Gantt — это JavaScript-библиотека, разработанная для бесшовной интеграции функционала диаграмм Ганта в веб-приложения. Она предоставляет широкий спектр функций для настройки, позволяя разработчикам адаптировать её к специфическим нуждам проекта. Однако важно отметить, что DHTMLX Gantt не включает встроенную защиту от угроз, таких как SQL-инъекции, XSS или CSRF-атаки. Обеспечение безопасности вашего приложения требует внедрения необходимых конфигураций и защитных мер самостоятельно. Ниже приведены некоторые рекомендации по эффективной обработке HTML-санитизации.
Кибербезопасность — это обширная область, и ни одно руководство не может охватить всё. Однако следование этим практическим шагам может помочь устранить распространенные уязвимости и снизить риски.
Добавление заголовка CSP, как в примере ниже, может заблокировать выполнение встроенных скриптов, значительно снижая риск XSS и CSRF атак:
Content-Security-Policy: script-src 'self'
В зависимости от требований вашего приложения, вам может понадобиться более детализированная политика, но отключение встроенных скриптов — это хорошая отправная точка.
Перед сохранением пользовательского ввода в вашу базу данных убедитесь, что данные правильно очищены от любого вредоносного содержимого. Например, вместо прямого вставления необработанных значений:
db.query("INSERT INTO gantt_tasks(text, start_date, duration, progress, parent)"
+ " VALUES (?,?,?,?,?)",
[task.text, task.start_date, task.duration, task.progress, task.parent])
Используйте библиотеку, такую как DOMPurify с Node.js, для очистки ввода:
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))
Чтобы предотвратить выполнение HTML-разметки при отображении, экранируйте 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
});
Чтобы защититься от атак SQL-инъекций, избегайте создания SQL-запросов путём конкатенации строк. Вместо этого используйте параметризованные запросы или конструктор запросов/ORM. Если вы используете необработанные или непроверенные пользовательские вводы в своих запросах, стоит переписать код, чтобы следовать более безопасным практикам.
Безопасность — это непрерывный процесс. Реализуя эти шаги, соблюдая политики безопасности вашей организации и проверяя свою работу у специалистов, вы можете устранить большинство распространенных угроз.
С этими базовыми мерами давайте рассмотрим вопросы безопасности, специфичные для Gantt.
При использовании сложных инструментов, таких как Gantt, на клиентской стороне необходимо учитывать несколько ключевых моментов:
Некоторые области, где могут возникнуть уязвимости, включают:
Защита Gantt начинается с его изоляции от несанкционированного доступа, будь то скомпрометированные компоненты или атаки самонаправленного XSS.
Если злоумышленники получат доступ к конфигурационным файлам (включая конфигурации Gantt), защита от XSS может оказаться неэффективной. Этот сценарий выходит за рамки данного документа.
Чтобы предотвратить несанкционированный доступ к экземпляру Gantt, инкапсулируйте его в функции. Это гарантирует, что экземпляр недоступен за пределами области действия функции. Например:
function addGantt(){
const gantt = Gantt.getGanttInstance();
}
addGantt()
Вы также можете переименовать экземпляр для ясности:
function addGantt(){
const protectedGantt = Gantt.getGanttInstance();
}
addGantt()
После того как доступ к Gantt защищен, сосредоточьтесь на безопасной обработке ввода и отображения данных.
Точки ввода данных являются распространенными целями для XSS-атак. В Gantt данные могут вводиться через:
Объект задачи включает различные параметры в зависимости от включенных функций. Чем больше параметров доступно для редактирования, тем больше данных необходимо санитизировать при вводе.
Вот пример, иллюстрирующий, как можно усилить защиту от XSS-атак, санитизируя HTML при работе с DHTMLX Gantt.
Related sample: Example to prevent XSS attacks (security, csp)
В этом примере вы можете изменять имя задачи, даты, продолжительность, назначение ресурсов и добавлять заметки. Изменение даты начала и продолжительности ограничено lightbox и встроенными редакторами. Для встроенных редакторов типы date и number явно определены. В lightbox вы можете установить только продолжительность, а дата выбирается из выпадающего списка.
Эти элементы интерфейса разработаны таким образом, чтобы предотвратить вставку вредоносного кода. Если вы попытаетесь изменить типы элементов с помощью инспектора DOM, некорректные значения для даты или продолжительности вызовут ошибки, что остановит функциональность Gantt до перезагрузки страницы. В таких случаях данные не отправляются на сервер, так как данные не будут перерисованы.
Тем не менее, имена задач, которые используют тип значения string, могут быть слабым местом для XSS-атак. Чтобы решить эту проблему, становится важной санитизация ввода. Пример демонстрирует один из сценариев XSS-атаки и способ его предотвращения.
Для реальных проектов важно реализовать комплексную санитизацию данных. В этом примере символы "<" и ">" заменяются на их HTML-сущности, <
и >
, чтобы предотвратить отображение HTML-элементов в тексте задачи.
Логика замены реализована в функции sanitizeText():
function sanitizeText(text){
// раскомментируйте для тестирования XSS
// return text
// предотвращение XSS путём отключения HTML-элементов
return text.split("<").join("<").split(">").join(">");
}
Эта функция используется в обработчиках событий, таких как onLightboxSave для lightbox и onBeforeSave для встроенных редакторов.
Пример также включает пользовательские встроенные редакторы и секции lightbox для добавления текстовых заметок. Санитизация применяется в функциях этих пользовательских объектов перед отображением значений или получением изменений из элементов DOM:
// для встроенного редактора:
set_value: function(value, id, column, node){
node.firstChild.value = sanitizeText(value || "");
},
get_value: function(id, column, node){
return sanitizeText(node.firstChild.value);
},
// для lightbox:
set_value: function(node, value, task){
node.value = sanitizeText(value || "");
},
get_value: function(node, task){
return sanitizeText(node.value);
},
Альтернативно, можно обработать санитизацию текстовых заметок, используя обработчики событий onLightboxSave и onBeforeSave:
protectedGantt.attachEvent("onLightboxSave", function(id, task, is_new){
if (task.notes) {
task.notes = sanitizeText(task.notes);
}
return true;
});
protectedGantt.ext.inlineEditors.attachEvent("onBeforeSave", function(state){
if (state.columnName == "notes") {
state.newValue = sanitizeText(state.newValue);
}
return true;
});
Назначения ресурсов в lightbox также требуют внимания. Поскольку Gantt позволяет строковые значения, санитизация необходима для предотвращения XSS-атак. Функция sanitizeResourceValues() проходит по значениям ресурсов и применяет sanitizeText():
function sanitizeResourceValues(task){
const resources = task[protectedGantt.config.resource_property];
if (resources && resources.length) {
resources.forEach(function (resource) {
if (typeof resource.value == "string") {
resource.value = sanitizeText(resource.value);
}
})
}
}
Эта функция привязана к обработчику события onLightboxSave:
protectedGantt.attachEvent("onLightboxSave", function(id, task, is_new) {
sanitizeResourceValues(task)
return true;
});
Не забывайте санитизировать любые дополнительные строковые параметры в вашей конфигурации Gantt.
В примере принимаются только числовые значения для назначения ресурсов в временной шкале ресурсов. Любые другие типы не сохранят изменения.
Gantt позволяет настраивать, включая редактирование задач с помощью сторонних форм, инструментов или библиотек. Эти сценарии часто зависят от API Gantt. Поскольку настройки различаются, нет универсального решения для санитизации данных.
В примере используется пользовательская форма для редактирования имен задач. Функция sanitizeText() включена для обработки экранирования текста:
document.body.querySelector("[name='save']").onclick = function(){
const newTaskName = document.body.querySelector("[name='text']").value;
task.text = sanitizeText(newTaskName);
protectedGantt.updateTask(task.id);
}
Санитизируя данные при их вводе в Gantt, потенциальные XSS-атаки нейтрализуются, гарантируя, что они не достигнут сервера.
Еще одна область, которую следует учитывать, — это отображение данных в диаграмме Gantt. Хотя она не так критична, как ввод данных, санитизация отображаемых данных может прервать цепочки XSS-атак. Например, если сервер скомпрометирован, но Gantt остается недоступным, санитизация может заблокировать атаку.
Самый безопасный подход — санитизировать все области, где отображаются данные. Это включает использование шаблонов в конфигурациях столбцов грида и всех доступных шаблонов для предотвращения отображения вредоносного содержимого.
Более простая альтернатива — ограничение потока данных в Gantt. Это гарантирует, что никакой вредоносный код не сможет повлиять на содержимое. Например, свойства задач могут быть санитизированы при загрузке данных с сервера, используя обработчик события onTaskLoading:
protectedGantt.attachEvent("onTaskLoading", function (task) {
task.text = sanitizeText(task.text);
if (task.notes) {
task.notes = sanitizeText(task.notes);
}
sanitizeResourceValues(task);
return true;
});
Если задачи загружаются индивидуально или обновляются в Gantt, они должны быть санитизированы перед добавлением или обновлением:
let newTask = await loadFromServer(23);
sanitizeTaskProperties(newTask);
gantt.addTask(newTask);
Хотя пользователи могут манипулировать элементами DOM Gantt с помощью инспекторов браузера, такие изменения не сохранятся и будут утеряны при повторной отрисовке или обновлении сервера.
Помните, что клиентская валидация не является непогрешимой и может быть обойдена. Она предоставляет немедленную обратную связь пользователям о ошибочном вводе, но окончательная валидация всегда должна происходить на сервере.
Поскольку dhtmlxGantt полностью на стороне клиента, предотвращение SQL-инъекций является обязанностью серверной стороны.
Основные моменты:
Если вы используете dhtmlxConnector и конфигурацию таблицы, как описано в документации, значения автоматически экранируются. В противном случае следуйте лучшим практикам для безопасной реализации CRUD на вашей платформе. Руководства по началу работы предоставляют безопасные примеры для предотвращения SQL-инъекций.
Для добавления пользовательских авторизационных токенов или заголовков в запросы на сервер Gantt, обратитесь к этой статье.
Чтобы привести ваше приложение dhtmlxGantt в соответствие со стандартом CSP (Политика безопасности содержимого), библиотека предлагает специальную конфигурационную опцию. Это помогает предотвратить атаки с внедрением кода и укрепляет безопасность вашего приложения.
Узнайте больше о применении стандартов CSP в приложениях dhtmlxGantt.
К началу