JS: Веб-разработка

Теория: Безопасность

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

Безопасность веб-приложений — важная тема, которой мы уделим весь этот урок.

Главное правило безопасности

Оно звучит так:

Никогда не доверяйте пользователям

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

import fastify from 'fastify'

const app = fastify()
const port = 3000

app.get('/users/:id', (req, res) => {
  res.type('html')
  res.send(`<h1>${req.params.id}</h1>`)
})

app.listen({ port }, () => {
  console.log(`Example app listening on port ${port}`)
})

Когда код реализует эту функциональность, он рассчитывает, что в адресе используются только допустимые имена. А теперь попробуем открыть такой адрес:

http://localhost:3000/users/%3Cscript%3Ealert('attack!')%3B%3C%2Fscript%3E

Если мы доверимся данным от пользователей и откроем такой адрес, случится вот это:

XSS

В этом адресе закодирован JavaScript-код, который в оригинале выглядит так:

<script>
  alert('attack!');
</script>

Проблема в том, что код не отобразился, но попал в HTML страницы и выполнился. Так произошло, потому что для браузера такой JavaScript-код выглядит как часть страницы.

Если открыть получившийся HTML, то он будет выглядеть так:

<h1>
  <script>alert('attack!');</script>
</h1>

Именно так происходит XSS-атака (межсайтовый скриптинг). На страницу внедряется вредоносный код, который выполняется в браузере пользователя и отправляет информацию о пользователе на сервер злоумышленника. XSS — это одна из самых распространенных атак. Очень много подобных уязвимостей есть даже на сайтах больших компаний.

Специфика подобных атак в том, что вредоносный код может использовать авторизацию пользователя в веб-системе. Так злоумышленник может получить расширенный доступ к системе или логины и пароли пользователей. Если в исходном коде встречается конструкция <текст>, то браузер автоматически считает ее тегом.

Вернемся к коду из начала урока:

import fastify from 'fastify'

const app = fastify()
const port = 3000

app.get('/users/:id', (req, res) => {
  res.type('html')
  res.send(`<h1>${req.params.id}</h1>`)
})

app.listen({ port }, () => {
  console.log(`Example app listening on port ${port}`)
})

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

Чтобы закрыть ее, нужно использовать не сами теги, а HTML-эквиваленты символов. Тогда код выше начнет выглядеть так:

<h1>
  &lt;script&gt;alert('attack!');&lt;/script&gt;
</h1>

Здесь мы заменили:

  • < на &lt;
  • > на &gt

Это не экранирование, а именно замена спецсимволов на их HTML-эквиваленты. Если открыть браузер, то там мы увидим правильное отображение:

XSS

Замена символов на спецсимволы выполняется с помощью специальных библиотек, например, sanitize-html. Один из вариантов выглядит так:

# Установка пакета
npm i sanitize-html

Функция sanitize() принимает на вход HTML и заменяет в нем все спецсимволы на их HTML-эквиваленты. Остальные символы остаются без изменения.

Через такую обработку нужно пропускать любые данные, которые мы выводим. Исключение составляют лишь ситуации, в которых мы точно знаем, что в данных есть HTML и мы его хотим отобразить. К таким данным могут относиться статьи в блоге, потому что они содержат HTML-часть. Например, так работает блог Хекслета.

Завершено

0 / 23