# Безопасность приложения

DHTMLX Gantt — это клиентская JavaScript‑библиотека, разработанная для плавной интеграции функциональности Gantt в различные веб‑приложения.
Поэтому мы не ограничиваем функциональные возможности нашего Gantt, которые могли бы повысить безопасность приложения, но одновременно снизить доступные возможности.
Таким образом, вы можете настраивать большинство функций Gantt в соответствии с требованиями вашего проекта.

Однако следует помнить, что DHTMLX Gantt не предоставляет каких-либо средств защиты вашего приложения от различных угроз, таких как SQL‑инъекции или XSS и CSRF
атаки самостоятельно. Поэтому задача обеспечить безопасность вашего проекта лежит на вас, предоставляя необходимые настройки конфигурации.
В этой статье вы найдете некоторую актуальную информацию и рекомендации по HTML‑санитации.

## Базовые шаги по обеспечению безопасности

Хотя кибербезопасность — сложная дисциплина и её нельзя охватить одним набором инструкций,
мы рекомендуем следовать практическим шагам, которые охватят основы и помогут снизить вероятность наиболее частых угроз.

**1. Используйте Content Security Policy (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](https://www.npmjs.com/package/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‑разметку, которую можно выполнить во время рендеринга, убедитесь, что HTML‑символы, которые могли ввести пользователи, экранированы перед подачей данных в Gantt. Ниже приведён пример использования библиотеки [validator](https://www.npmjs.com/package/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 или конструкторы запросов вместо этого**

Этот пункт касается типов атак типа SQL‑инъекции. Как правило, вы никогда не должны использовать неэкранированный или невалидированный ввод пользователя в ваших SQL‑запросах. Если вы обнаружили, что делаете это, рассмотрите возможность переписать код с использованием параметризованных запросов или используйте функции экранирования, поддерживаемые вашим SQL‑поставщиком, который вы используете.

**5. Последнее, но не менее важно: проконсультируйтесь с экспертом по кибербезопасности и следуйте политикам безопасности, принятым вашей компанией**

Безопасность работы никогда не может считаться полностью завершённой, но применив эти шаги, следуя политикам вашей компании и имея вашу работу рассмотренной специалистом по безопасности, вы избежите большинства угроз, которые могут встретиться в сети.

Теперь, когда основы охвачены, перейдём к вещам, специфичным для Gantt.

## Уязвимые области Gantt на стороне клиента

Прежде всего выделим три момента при интеграции сложных функций, таких как Gantt, на стороне клиента:

- DHTMLX Gantt — это библиотека на стороне клиента, поэтому все данные, загруженные с сервера, попадают в Gantt в исходной форме.
Так как набор данных сохраняется на стороне сервера, основная угроза вашему приложению исходит оттуда. Но защита бэкенда выходит за рамки DHTMLX Gantt
- Злоумышленники могут заставить конечных пользователей выполнить вредоносный код с помощью DevTools (атаки self‑XSS), тем самым обходя любые механизмы защиты. Любой код, вставленный в текст задачи, будет работать так же, как если использовать DevTools
- Если злоумышленник получает доступ к объекту экземпляра Gantt, любые защитные меры будут неэффективны. В таком случае злоумышленники могут изменить конфигурацию Gantt по своему усмотрению и полностью контролировать её

Теперь перейдём к списку уязвимых областей DHTMLX Gantt, где стоит ожидать потенциальные проблемы с безопасностью:

- данные, вводимые и сохраняемые конечными пользователями
- отображаемые данные Gantt (текстовое содержимое, различные визуальные элементы)
- [пользовательские HTML‑элементы](guides/export.md#exporting-html-elements), которые как‑то взаимодействуют с данными Gantt
- доступ к объекту Gantt

Перейдём к практическим соображениям по этим потенциальным проблемам.

## Изоляция доступа к Gantt

Когда речь идёт о возможных мерах по защите Gantt, первая вещь, которую нужно сделать, — изолировать Gantt от несанкционированного доступа
через взломанные компоненты или со стороны ошибочно мыслящих пользователей (само‑XSS).

:::note
Если злоумышленник получает доступ к файлам конфигурации приложения (включая файл конфигурации Gantt), любые принятые меры защиты от XSS (если они были приняты) могут быть неэффективны, поэтому мы не будем рассматривать данный сценарий.
:::

Когда приложение полностью загружено и объект экземпляра Gantt становится доступен злоумышленникам, 
они могут literally изменить всё в Gantt и переопределить все функции. Поэтому вам следует знать, как изолировать Gantt в вашем проекте. 

Для этого необходимо создать отдельный экземпляр Gantt внутри функции. Цель здесь — сделать код, который выполняется внутри функции, недоступным за её пределами. 

Что ещё важно: по умолчанию Gantt создаёт новый экземпляр в объекте *gantt*. Важно добавить новую переменную внутри функции, используя одно из ключевых слов *const* или *let*, чтобы сделать её недоступной за пределами функции и безопасно поместить экземпляр Gantt внутрь этой переменной. 

~~~js
function addGantt(){
  const gantt = Gantt.getGanttInstance();
}
addGantt()
~~~

Вы также можете использовать другое имя для экземпляра Gantt, чтобы избежать путаницы с объектом gantt:

~~~js
function addGantt(){
  const protectedGantt = Gantt.getGanttInstance();
}
addGantt()
~~~

После обеспечения защиты Gantt от нежелательного доступа, обратите внимание на ввод и отображение данных в диаграмме Gantt.

## Ввод данных в Gantt

Это чувствительная зона, которую киберпреступники могут использовать для компрометации безопасности Gantt в вашем приложении.

Области ввода данных считаются основными целями XSS‑атак. В нашем компоненте Gantt возможно изменение данных через:

- lightbox
- inline editors
- modalbox с пользовательскими элементами
- сторонние библиотеки
- назначения ресурсов в временной шкале загрузки ресурсов
- дополнительные слои (если у них есть пользовательские элементы, в которые можно вводить данные)
- любые пользовательские решения, использующие API Gantt и требующие ввода данных (например, панель инструментов или пользовательская форма редактирования задач)

Объект задачи имеет [много различных параметров](guides/task-properties.md), которые используются в зависимости от активированных функций.
Чем больше параметров можно редактировать, тем больше параметров следует санитарировать при вводе данных.

### Рассмотрим пример

Мы подготовили пример, демонстрирующий различные шаги, которые можно предпринять для повышения защиты от XSS‑атак через HTML‑санитацию при использовании DHTMLX Gantt. 

**Связанный пример**: [Пример предотвращения XSS‑атак (безопасность, CSP)](https://snippet.dhtmlx.com/cdy9p0yl)

В нашем примере можно редактировать имя задачи, менять её дату и продолжительность, изменять назначения ресурсов и добавлять текстовые заметки. 
Изменять дату начала и продолжительность можно только через lightbox и inline editors. В inline editors явно указаны типы **date** и **number**.
В lightbox можно указать только продолжительность, в то время как дату необходимо выбрать из выпадающего списка.

В обоих случаях вставлять текст, содержащий вредоносный код в эти элементы интерфейса невозможно. 
Если попытаться изменить тип элементов через инспектор DOM, вы получите неверные значения даты или продолжительности.
Это вызовет ошибку, и Gantt не сможет продолжать работать до перезагрузки страницы. При этом данные не будут отправлены на сервер, так как диаграмма не будет перерассчитана.

Однако мы используем тип значения **string** для названий задач, и это может стать уязвимостью для XSS‑атак.
Поэтому вам нужно очистить введённое значение. В нашем примере приведён всего один вариант XSS‑атаки и один способ её предотвратить. 

![preventing_xss_attack](/img/preventing_xss_attack.png)

В реальном проекте вам потребуется добавить все возможные варианты очистки данных. 
В нашем случае мы просто заменяем символы "\<" и "\>" соответствующими HTML‑сущностями — **`&lt;`** и **`&gt;`**.
Таким образом, мы исключаем возможность отображения HTML‑элементов внутри текста задачи.

Указанная выше замена символов реализована в функции **sanitizeText()** как показано:

~~~js
function sanitizeText(text){
    // uncomment to test XSS
    // return text

    // prevent XSS by disabling HTML elements
    return text.split("<").join("&lt;").split(">").join("&gt;");
}
~~~

Эта функция вызывается в обработчиках событий: в **onLightboxSave** для lightbox и в **onBeforeSave** для inline editors.

В нашем примере вы также можете добавлять текстовые заметки к задаче с использованием либо настраиваемого inline editor, либо настраиваемого раздела lightbox.
В обоих случаях санитаризацию можно реализовать внутри функций этих настраиваемых объектов
(до того как значения будут отрисованы и изменения будут взяты из элементов DOM):

~~~js
// for an inline editor:
set_value: function(value, id, column, node){
    node.firstChild.value = sanitizeText(value || "");
},
get_value: function(id, column, node){
    return sanitizeText(node.firstChild.value);
},

// for the lightbox:
set_value: function(node, value, task){
    node.value = sanitizeText(value || "");
},
get_value: function(node, task){
    return sanitizeText(node.value);
},
~~~

Но управлять работой с текстовыми заметками проще с помощью обработчиков событий **onLightboxSave** и **onBeforeSave**: 

~~~js
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 ограничивает значения не только типом **number**, доступно использование строковых значений, что открывает возможность XSS‑атак.

Значения ресурсов записываются в свойство задачи, поэтому функция **sanitizeResourceValues()** перебирает все эти значения
и очищает значение назначения ресурса с использованием функции **sanitizeText()**:

~~~js
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);
            }
        })
       }
}
~~~

**sanitizeResourceValues()** вызывается в обработчике события **onLightboxSave**:

~~~js
protectedGantt.attachEvent("onLightboxSave", function(id, task, is_new) {
    sanitizeResourceValues(task)
    return true;
});
~~~

*Если вы используете любые другие строковые параметры в вашей конфигурации Gantt, их также следует очистить*.

В нашем примере, если вы попытаетесь вставить неблагообразное содержимое в назначения ресурсов в временной шкале ресурсов, будут приняты только числовые значения. В случае использования других типов значений изменения не будут сохранены.

### Ввод данных через сторонние инструменты

Наш компонент Gantt предоставляет множество возможностей для настройки, включая возможность редактирования задач с помощью форм, инструментов и библиотек третьих сторон.
В таком случае API Gantt используется для работы с задачами. В таких сценариях трудно дать универсальный совет по очистке данных, потому что всё зависит от реализованных способов настройки.

В нашем примере есть настраиваемая форма для редактирования имени задачи. Форма также включает функцию **sanitizeText()** для экранирования текста:

~~~js
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 и, определённо, не смогут добраться до сервера.

## Отображение данных в Gantt

Следующая уязвимая область, которую следует упомянуть, — отображение данных в диаграмме Gantt. 
Хотя это не так эффективно, как при вводе данных, очистка отображаемых данных всё равно поможет остановить или прервать цепочку XSS‑атак.
Например, если сервер с данными был взломан, но доступ к Gantt отсутствует, атака XSS будет прервана на уровне Gantt.

Наиболее безопасным подходом будет очистка всех областей Gantt, где отображаются данные.
Это предполагает [использование шаблонов в конфигурации каждой колонки грид](guides/specifying-columns.md#datamappingandtemplates). 
Использование [всех возможных шаблонов](api/overview/templates-overview.md) будет необходимо, чтобы предотвратить отображение контента с возможными XSS‑атаками.

Однако существует более простое решение для потенциальных проблем с отображением данных в диаграмме Gantt. 
Так как данные могут загружаться в диаграмму Gantt через ввод пользователя или с сервера, мы можем ограничить эти два потока данных.
Тогда не будет шансов повлиять на содержимое Gantt и внедрить вредоносный код в данные.

Можно защитить свойства задач при их загрузке с сервера. Это можно сделать в обработчике события **onTaskLoading**:

~~~js
protectedGantt.attachEvent("onTaskLoading", function (task) {
    task.text = sanitizeText(task.text);
    if (task.notes) {
        task.notes = sanitizeText(task.notes);
    }
    sanitizeResourceValues(task);
    return true;
});
~~~

Могут быть и другие способы загрузки данных в диаграмму Gantt. Например, объект задачи может поступать отдельно с сервера и обрабатываться какой‑то функцией. После этого новая задача добавляется в диаграмму Gantt или обновляется существующая задача.
В таком случае необходимо выполнить очистку задачи внутри этой функции перед загрузкой данных в Gantt.

Это может выглядеть так:

~~~js
let newTask = await loadFromServer(23);
sanitizeTaskProperties(newTask);
gantt.addTask(newTask);
~~~

Если киберпреступник заставит пользователя использовать инспектор элементов в заданном веб‑браузере и вставит вредоносный код в элементы DOM Gantt, вы не сможете этого избежать. Но при этом все применённые изменения будут потеряны при следующей переработке рендера Gantt и не будут сохранены на сервере.

## Проблемы на стороне сервера

Учтите, что валидацию на стороне клиента можно легко подорвать или обойти полностью, поэтому на неё нельзя полагаться как на средство обеспечения безопасности. Она предназначена для немедленного информирования пользователя об ошибке ввода, без ожидания ответа сервера, в то время как итоговая валидация должна выполняться на сервере.

Бэкенд должен правильно валидировать/экранировать/очищать входящие данные, правила доступа пользователей и т. п.

### SQL‑инъекции

dhtmlxGantt — это 100% компонент на стороне клиента, поэтому SQL‑инъекции должны предотвращаться на бэкенде разработчиком.

Есть две ситуации, которые следует учитывать:

- lightbox не имеет какой‑либо стандартной валидации по умолчанию, которая, если её не обработать, позволяет пользователю вводить любые значения в редактируемые поля
- ваш бэкенд API может быть вызван вручную через PUT/POST запрос, содержащий опасные значения, обходя клиентский UI

Следовательно, вам понадобится некое средство экранирования SQL‑инъекций на вашем бэкенде. Если вы используете [dhtmlxConnector](integrations/php/howtostart-connector.md) и укажете конфигурацию таблицы, как показано в соответствующей [документации](https://docs.dhtmlx.com/connector__php__basis.html#loading-from-database), все значения будут автоматически экранированы. В противном случае придётся использовать безопасную реализацию CRUD в соответствии с лучшими практиками вашей платформы. Реализации, приведённые в [руководства по началу работы](integrations/howtostart-guides.md), должны быть безопасны с точки зрения SQL‑инъекций.

### CSRF‑атаки

Пожалуйста, ознакомьтесь с [этой статьёй](guides/server-side.md#custom-request-headers-and-parameters) по добавлению пользовательских токенов авторизации заголовков к запросу, отправляемому Gantt в бэкенд.

## Content Security Policy

Библиотека предоставляет специальную конфигурацию, которая позволяет вам настроить код вашего приложения, созданного с использованием dhtmlxGantt, в соответствии со стандартом CSP (Content Security Policy). 
Она помогает предотвращать различные атаки инъекций кода и повышать безопасность приложения. 

[Узнайте больше о применении стандарта CSP к приложению на базе dhtmlxGantt](api/config/csp.md).