Каждый элемент HTML может иметь уникальный идентификатор или атрибут имени для удобства ссылки на определенные элементы в JavaScript. Например, следующая кнопка HTML имеет атрибут ID со значением "myButton"
:
Click Me!
Мы можем использовать этот идентификатор в нашем коде JavaScript, чтобы манипулировать кнопкой, например, изменяя ее текст при нажатии:
document.getElementById('myButton').onclick = function() {
this.textContent = 'Clicked!';
};
DOM облегчает это взаимодействие между HTML и JavaScript. Но что произойдет, если атрибут ID или имя элемента HTML конфликтует с глобальной переменной или функцией JavaScript?
Когда браузер загружает HTML-страницу, он автоматически создает глобальные переменные JavaScript для каждого идентификатора и атрибута имени в HTML DOM. Если у нас есть элемент HTML с именем "myButton"
, браузер создает глобальную переменную JavaScript myButton
, ссылаясь на этот элемент HTML.
Теперь давайте рассмотрим сценарий, в котором мы объявили функцию JavaScript с именем myButton
:
function myButton() {
// some code
}
Но у нас также есть элемент HTML с идентификатором или именем "myButton"
. Когда страница загружается, автоматический процесс браузера перезаписывает функцию JavaScript myButton
со ссылкой на элемент HTML:
Click Me!
console.log(myButton); // This will log the HTML button element, not the function
Этот процесс представляет собой затирание DOM, что может привести к непредсказуемому поведению и уязвимостям безопасности. Если злоумышленник может контролировать атрибуты, он может внедрить вредоносный код на веб-страницу, что приведет к проблемам безопасности, таким как межсайтовый скриптинг (XSS).
Для иллюстрации рассмотрим следующий сценарий:
Enter your name:
Greet
function greet() {
var username = document.getElementById('username').value;
alert(`Hello ${username}`);
}
Злоумышленник может ввести что-то вроде 
, создав новый HTML-компонент с 'id'
из 'alert'
. Этот компонент заменяет — или затирает — обычную функцию оповещения в JavaScript. В следующий раз, когда веб-сайт попытается использовать эту функцию, она не будет работать должным образом и может даже запустить вредоносный код.
Выявление уязвимостей затирания DOM
Рассмотрим базовое веб-приложение с формой обратной связи с пользователем. Пользователи вводят свое имя и сообщение обратной связи, а затем отправляют его. На странице отображаются отзывы:
HTML:
Feedback Form
-------------
Name:
Feedback:
JavaScript:
document.querySelector('form').onsubmit = function(event) {
event.preventDefault();
let name = document.getElementById('name').value;
let feedback = document.getElementById('feedback').value;
let feedbackElement = document.getElementById('feedbackDisplay');
feedbackElement.innerHTML = `**${name}**: ${feedback}
`;
};
Этот код берет имя пользователя и отзывы и отображает их в элементе абзаца внутри блока feedbackDisplay
.
Злоумышленник может использовать этот код, отправив HTML-код в форму обратной связи. Например, если они введут следующий код в поле имя и отправят форму, область отображения отзыва будет заменена тегом скрипта:
window.location.href='https://maliciouswebsite.com';
Когда форма пытается отобразить следующую часть отзыва, она вместо этого выполняет сценарий, перенаправляя пользователя на вредоносный веб-сайт.
Этот сценарий создает уязвимость, поскольку приложение использует предоставленные пользователем данные для создания HTML, создавая тег скрипта с тем же идентификатором, что и у существующего элемента. Это пример затирания DOM с серьезными последствиями — злоумышленник может получить контроль над браузером пользователя, что позволяет ему украсть конфиденциальные данные или установить вредоносное ПО.
Практики безопасного кодирования для предотвращения затирания DOM
С более глубоким пониманием этих уязвимостей мы можем перейти к некоторым передовым методам, которые снижают риск затирания DOM.
Правильно определяйте переменные области видимости и функции
Одной из наиболее распространенных причин затирания DOM является неправильное использование глобальной области видимости в JavaScript. Определяя переменные и функции в определенной области, мы можем ограничить перезапись этой областью или любыми вложенными областями и свести к минимуму потенциальные конфликты.
Давайте применим правила области видимости JavaScript и рефакторим предыдущий пример, чтобы показать, как это можно сделать:
(function() {
// All variables and functions are now in this function's scope
const form = document.querySelector('form');
const feedbackElement = document.getElementById('feedbackDisplay');
form.onsubmit = function(event) {
event.preventDefault();
const name = document.getElementById('name').value;
const feedback = document.getElementById('feedback').value;
// Sanitize user input
name = DOMPurify.sanitize(name);
feedback = DOMPurify.sanitize(feedback);
const newFeedback = document.createElement('p');
newFeedback.textContent = `${name}: ${feedback}`;
feedbackElement.appendChild(newFeedback);
};
})();
Примечание. Мы использовали DOMPurify для очистки HTML в приведенном выше блоке кода. Вы можете установить его в Node.js с помощью npm install dompurify
. Включите его в свой HTML с помощью .
В этой версии кода мы заключили все в Выражение немедленно вызываемой функции (IIFE), которое создает новую область. Переменные form
и feedbackElement
, а также функция, назначенная обработчику события onsubmit
, не входят в глобальную область видимости, поэтому их нельзя затереть.
Используйте уникальные идентификаторы
Обеспечение уникального идентификатора каждого элемента на нашей веб-странице снижает риск непреднамеренной перезаписи важной функции или переменной. Кроме того, избегайте использования общих имен или имен, которые могут конфликтовать с глобальными объектами или функциями JavaScript.
Избегайте загрязнения глобального пространства имен
Поддержание чистоты глобального пространства имен является жизненно важным аспектом написания безопасного JavaScript. Чем больше переменных и функций в глобальной области видимости, тем выше риск затирания DOM. Используйте область видимости функций JavaScript или область видимости блоков ES6, чтобы сохранить ваши переменные и функции. Вот пример использования последнего:
// All variables and functions are now in this block's scope
let form = document.querySelector('form');
let feedbackElement = document.getElementById('feedbackDisplay');
form.onsubmit = function(event) {
event.preventDefault();
let name = document.getElementById('name').value;
let feedback = document.getElementById('feedback').value;
// Sanitize user input
name = DOMPurify.sanitize(name);
feedback = DOMPurify.sanitize(feedback);
let newFeedback = document.createElement('p');
newFeedback.textContent = `${name}: ${feedback}`;
feedbackElement.appendChild(newFeedback);
};
В этом коде мы использовали блок (определяемый {}
) для создания новой области. Все переменные и функции теперь ограничены этим блоком и не находятся в глобальной области видимости.
Примечание. Примеры кода в этом руководстве упрощены для демонстрационных целей и не предназначены для использования на рабочем веб-сайте или в приложении.
Использование функций JavaScript для предотвращения затирания DOM
Современный JavaScript предоставляет несколько функций, которые помогают свести к минимуму риск затирания DOM. В частности, ключевые слова let
и const
, представленные в ES6, обеспечивают больший контроль над объявлением переменных.
Традиционно мы объявляли переменные JavaScript с помощью ключевого слова var
. Однако у var
есть несколько особенностей, одна из которых заключается в том, что у него нет блочной области видимости — только функциональная область и глобальные области видимости. Это означает, что переменная, объявленная с помощью var
, может быть доступна и перезаписана за пределами блока, в котором мы ее объявили.
С другой стороны, let
и const
имеют блочную область видимости, то есть они доступны только внутри блока, в котором они были объявлены. Эта характеристика часто делает их лучшим выбором для объявления переменных, поскольку ограничивает возможность перезаписи переменных.
Мы также можем использовать const
для объявления констант — значений, которые мы не можем изменить после их присвоения. Они представляют собой еще один полезный инструмент для предотвращения непреднамеренных изменений важных переменных.
Давайте посмотрим, как реализовать эти функции в коде нашей формы обратной связи, чтобы предотвратить затирание DOM:
const form = document.querySelector('form');
const feedbackElement = document.getElementById('feedbackDisplay');
form.onsubmit = function(event) {
event.preventDefault();
const name = document.getElementById('name').value;
const feedback = document.getElementById('feedback').value;
// Sanitize user input
name = DOMPurify.sanitize(name);
feedback = DOMPurify.sanitize(feedback);
const newFeedback = document.createElement('p');
newFeedback.textContent = `${name}: ${feedback}`;
feedbackElement.appendChild(newFeedback);
};
В этом коде мы заменили все варианты использования var
на const
. Мы ограничили все переменные блоком, в котором мы их объявили, и константы не могут быть перезаписаны.
Помните, что использование let
и const
не устраняет риск затирания DOM, но практика остается критическим аспектом безопасного кодирования. Понимание и реализация этих функций JavaScript может сделать ваши веб-приложения более безопасными.
Мониторинг и обнаружение уязвимостей затирания DOM
Методы безопасного кодирования могут помочь предотвратить затирание DOM, но обнаружение уязвимостей при их появлении не менее важно. Давайте рассмотрим некоторые инструменты и методы, которые могут помочь.
Статический анализ кода
Инструменты статического тестирования безопасности приложений (SAST), такие как Snyk Code, могут эффективно обнаруживать потенциальные уязвимости безопасности, включая затирание DOM. Эти инструменты анализируют ваш исходный код без его выполнения, выявляя шаблоны, которые могут привести к проблемам с безопасностью. Всеобъемлющий охват JavaScript в Snyk делает его отличным выбором для веб-разработчиков.
Инструменты разработчика браузера
Инструменты разработчика браузера, такие как доступные в Chrome или Firefox, могут быть мощными союзниками в обнаружении уязвимостей, затирающих DOM.
Давайте рассмотрим краткий пример того, как использовать эти инструменты для выявления потенциальной проблемы:
- На веб-странице щелкните правой кнопкой мыши в любом месте и выберите Проверить, чтобы открыть инструменты разработчика. Вы также можете использовать Ctrl+Shift+I в Windows или Cmd+Option. +I в macOS.
- Перейдите на вкладку Консоль, где отображаются ошибки или журналы JavaScript.
- Введите «window» в консоль и нажмите Enter, чтобы просмотреть глобальную область со всеми глобальными переменными и функциями. Проверьте любые переменные, которые кажутся неуместными, особенно те, которые имеют то же имя, что и ваши идентификаторы или имена элементов HTML.
- Используя вкладку Элементы, отредактируйте HTML-код страницы, чтобы манипулировать DOM и проверить наличие потенциальных уязвимостей. Например, добавьте элемент с идентификатором, совпадающим с глобальной переменной или функцией, чтобы проверить, не будет ли он перезаписан.
Заключение
В этой статье объясняется, как происходит затирание DOM, связанные с ним опасности и как от него защититься. Реализация правильной области видимости переменных, использование уникальных идентификаторов и правильный выбор функций JavaScript let
и const
имеют решающее значение для устранения этой уязвимости.
Поддержание безопасности кода требует баланса усилий по предотвращению и обнаружению. Использование рекомендуемых методов кодирования, инструментов разработчика браузера и решения SAST, такого как Snyk, может помочь точно определить существующие и потенциальные проблемы.
Включив эти методы в свои приложения JavaScript и следя за тенденциями в области безопасности веб-приложений, вы можете оставаться на опережение в защите своих методов кодирования.