В былые времена, когда веб разработка строилась на том, что серверные приложения направляли запросы в реляционные базы данных и выдавали на выходе HTML, часто встречался такой код:
или такой:
С тех пор мы научились использовать более безопасные подходы.
Широкое применение получили такие инструменты, как шаблонизаторы и привязка параметров. Сегодня редко можно встретить опасную конкатенацию строк.
В этой статье я хотел бы поделиться своими соображениями об атаках путем внедрения кода. По всей видимости, они все еще представляют собой угрозу в JavaScript.

Чтобы понять почему, разобьем один из неудачных примеров на более простые фрагменты. Вот так:
Очевидно, что первопричина атак путем внедрения кода связана с тем, что для компьютера нет разницы между командой и вводом информации пользователем! Поэтому злоумышленник может ввести данные, которые в дальнейшем будут обрабатываться как код.
Естественно, как я уже говорил, существуют хорошо известные средства защиты от таких атак. Вместо вот этого:
лучше написать что-нибудь такое:
Таким образом команда
Однако разработка веб-приложений стремительно развивается. Все чаще встречается не только такое:
но и такое:
Существенным отличием между двумя вариантами является то, что значение параметра представляет собой объект, включающий в себя команду!
Допустим значения читаются из
Беспокоиться о том, что пользователь предоставит вредоносную строку, не надо. Все будет обработано безопасным образом.
Проблема в том, что значениями параметра могут быть не только простые значения наподобие
Допустим
Вновь, получается, что для компьютера надежные команды неотличимы от ненадежного ввода данных пользователем. Только теперь речь идет не о надежных и ненадежных строках, а о надежных и ненадежных объектах.
Чтобы избежать эту проблему, нужно всегда писать команды так, чтобы их нельзя было получить от пользователя. Это можно реализовать в том числе с помощью подхода, применяемого в React и Snabbdom-Signature (это небольшая библиотека для защиты от инъекций в виртуальный DOM), а именно отмечать каждый объект команды
Признаюсь, и сам не раз думал, что раз нет базы данных SQL или если я использую виртуальный DOM, атаки путем внедрения кода мне не угрожают. Как же я ошибался!
// ВНИМАНИЕ: Плохой пример!
function popup(msg: string): string {
return "<p class=\"popup\">" + msg + "</p>";
}
или такой:
// ВНИМАНИЕ: Плохой пример!
function getName(login: string): string {
return "SELECT name FROM users WHERE login = \"" + login + "\"";
}
С тех пор мы научились использовать более безопасные подходы.
Широкое применение получили такие инструменты, как шаблонизаторы и привязка параметров. Сегодня редко можно встретить опасную конкатенацию строк.
В этой статье я хотел бы поделиться своими соображениями об атаках путем внедрения кода. По всей видимости, они все еще представляют собой угрозу в JavaScript.

Чтобы понять почему, разобьем один из неудачных примеров на более простые фрагменты. Вот так:
function f(userInput: A): A {
const firstCommand: A = ...;
const secondCommand: A = ...;
return firstCommand.concat(userInput.concat(secondCommand));
}
Очевидно, что первопричина атак путем внедрения кода связана с тем, что для компьютера нет разницы между командой и вводом информации пользователем! Поэтому злоумышленник может ввести данные, которые в дальнейшем будут обрабатываться как код.
Естественно, как я уже говорил, существуют хорошо известные средства защиты от таких атак. Вместо вот этого:
"SELECT name FROM users WHERE login = \"" + login + "\""
лучше написать что-нибудь такое:
query("SELECT name FROM users WHERE login = :login", {login})
Таким образом команда
SELECT name FROM users WHERE login =:login
отчетливо отделяется от данных {login}
. В то же время внутренние механизмы гарантируют, что данные будут подготовлены для использования в запросе SQL. Экранировать кавычки и внедрить вредоносный код не получится.Однако разработка веб-приложений стремительно развивается. Все чаще встречается не только такое:
{
paramA: "the value of the A parameter",
paramB: "the value of the A parameter",
}
но и такое:
{
paramA: "the value of the A parameter",
paramB: {$in: [
"the value of the B parameter",
"the value of the C parameter",
]},
}
Существенным отличием между двумя вариантами является то, что значение параметра представляет собой объект, включающий в себя команду!
Допустим значения читаются из
userInput
:{
paramA: userInput.paramA,
paramB: {$in: [
userInput.paramB[0],
userInput.paramB[1],
]},
}
Беспокоиться о том, что пользователь предоставит вредоносную строку, не надо. Все будет обработано безопасным образом.
Проблема в том, что значениями параметра могут быть не только простые значения наподобие
the value of the A parameter
, но и команды, например {$in: ["B", "C"]}
. Пользователь может разными способами направить запрос, после дешифровки которого получается объект (форма, JSON или XML), и поэтому код может подвергаться атакам путем инъекций.Допустим
userInput.paramA
равно {$empty: false}
. Тогда запрос выглядит следующим образом:{
paramA: {$empty: false},
paramB: {$in: [
userInput.paramB[0],
userInput.paramB[1],
]},
}
Вновь, получается, что для компьютера надежные команды неотличимы от ненадежного ввода данных пользователем. Только теперь речь идет не о надежных и ненадежных строках, а о надежных и ненадежных объектах.
Чтобы избежать эту проблему, нужно всегда писать команды так, чтобы их нельзя было получить от пользователя. Это можно реализовать в том числе с помощью подхода, применяемого в React и Snabbdom-Signature (это небольшая библиотека для защиты от инъекций в виртуальный DOM), а именно отмечать каждый объект команды
Symbol
, чтобы его нельзя было отправить по сети.Признаюсь, и сам не раз думал, что раз нет базы данных SQL или если я использую виртуальный DOM, атаки путем внедрения кода мне не угрожают. Как же я ошибался!
LOOKING.HOUSE — на проекте собрано более 150 точек looking glass в 40 странах. Можно быстро выполнить команды host, ping, traceroute и mtr.