Позвольте привести пример ...
Представьте, что у вас есть такой сайт, как упрощенный Twitter, размещенный на a.com. Зарегистрированные пользователи могут вводить некоторый текст (твит) в форму, которая отправляется на сервер в виде запроса POST и публикуется при нажатии кнопки отправки. На сервере пользователь идентифицируется cookie-файлом, содержащим его уникальный идентификатор сеанса, поэтому ваш сервер знает, кто опубликовал твит.
Форма может быть такой простой:
<form action="http://a.com/tweet" method="POST">
<input type="text" name="tweet">
<input type="submit">
</form>
А теперь представьте, что плохой парень копирует и вставляет эту форму на свой вредоносный веб-сайт, скажем, на b.com. Форма все еще будет работать. Пока пользователь входит в ваш Твиттер (т. Е. У него есть действующий файл cookie сеанса для a.com), запрос POST будет отправляться на http://a.com/tweet и обрабатываться как обычно, когда пользователь нажимает кнопку подтверждения. кнопка.
Пока что это не является большой проблемой, пока пользователь осведомлен о том, что именно делает форма, но что если наш плохой парень подправит форму следующим образом:
<form action="https://example.com/tweet" method="POST">
<input type="hidden" name="tweet" value="Buy great products at
http://b.com/#iambad">
<input type="submit" value="Click to win!">
</form>
Теперь, если один из ваших пользователей попадет на сайт злоумышленника и нажмет кнопку «Нажми, чтобы выиграть!», Форма будет отправлена на ваш сайт, пользователь будет правильно идентифицирован по идентификатору сеанса в файле cookie и скрытый твит публикуется.
Если бы нашему плохому парню было еще хуже, он заставил бы невинного пользователя отправить эту форму, как только они откроют его веб-страницу с помощью JavaScript, возможно, даже полностью скрытого в невидимом фрейме. В основном это подделка межсайтовых запросов.
Форма может быть легко отправлена отовсюду. Обычно это общая черта, но во многих случаях важно разрешить отправку формы только из домена, к которому она принадлежит.
Еще хуже, если ваше веб-приложение не различает запросы POST и GET (например, в PHP с использованием $ _REQUEST вместо $ _POST). Не делай этого! Запросы на изменение данных можно отправить так же просто, как http://a.com/tweet?tweet=This+is+really+bad,, встроенный в вредоносный веб-сайт или даже в электронное письмо.
Как сделать так, чтобы форму можно было отправлять только с моего собственного сайта?
Вот где появляется токен CSRF. Маркер CSRF - это случайная, трудно угадываемая строка. На странице с формой, которую вы хотите защитить, сервер сгенерирует случайную строку, токен CSRF, добавит ее в форму в качестве скрытого поля и также каким-то образом запомнит, либо сохранив ее в сеансе, либо установив cookie содержащий значение. Теперь форма будет выглядеть так:
<form action="https://example.com/tweet" method="POST">
<input type="hidden" name="csrf-token"
value="nc98P987bcpncYhoadjoiydc9ajDlcn">
<input type="text" name="tweet">
<input type="submit">
</form>
Когда пользователь отправляет форму, сервер просто должен сравнить значение отправленного поля csrf-token (имя не имеет значения) с токеном CSRF, запомненным сервером. Если обе строки равны, сервер может продолжить обработку формы. В противном случае сервер должен немедленно прекратить обработку формы и ответить ошибкой.
Почему это работает?
Есть несколько причин, по которым злоумышленник из нашего примера не может получить токен CSRF:
Копирование статического исходного кода с нашей страницы на другой веб-сайт было бы бесполезным, поскольку значение скрытого поля меняется с каждым пользователем. Если бы сайт злоумышленника не знал токен CSRF текущего пользователя, ваш сервер всегда отклонял бы запрос POST.
Поскольку вредоносная страница злоумышленника загружается браузером вашего пользователя из другого домена (b.com вместо a.com), у злоумышленника нет шансов написать код JavaScript, который загружает контент и, следовательно, текущий пользователь CSRF токен с вашего сайта. Это связано с тем, что веб-браузеры по умолчанию не разрешают междоменные запросы AJAX.
Плохой парень также не может получить доступ к куки, установленному вашим сервером, потому что домены не будут совпадать.
Когда мне следует защищать от подделки межсайтовых запросов?
Если вы можете быть уверены, что не смешиваете GET, POST и другие методы запроса, как описано выше, хорошим началом будет защита всех запросов POST по умолчанию.
Вам не нужно защищать PUT иУДАЛИТЬ запросы, потому что, как объяснялось выше, стандартная HTML-форма не может быть отправлена браузером с использованием этих методов.
С другой стороны, JavaScript действительно может делать другие типы запросов, например, с помощью функции jQuery $ .ajax (), но помните, чтобы запросы AJAX работали, домены должны совпадать (если только вы явно не настраиваете свой веб-сервер).
Это означает, что часто вам даже не нужно добавлять токен CSRF в запросы AJAX, даже если они являются запросами POST, но вам нужно будет убедиться, что вы обходите проверку CSRF только в своем веб-приложении, если запрос POST на самом деле запрос AJAX. Вы можете сделать это, посмотрев на наличие заголовка типа X-Requested-With, который обычно включают в себя запросы AJAX. Вы также можете установить другой пользовательский заголовок и проверить его наличие на стороне сервера. Это безопасно, поскольку браузер не будет добавлять пользовательские заголовки к обычной отправке формы HTML (см. Выше), поэтому у мистера Плохого парня нет шансов смоделировать это поведение с помощью формы.
Если у вас есть сомнения по поводу запросов AJAX, потому что по какой-то причине вы не можете проверить заголовок, такой как X-Requested-With, просто передайте сгенерированный токен CSRF в свой JavaScript и добавьте токен в запрос AJAX. Есть несколько способов сделать это; либо добавьте его в полезную нагрузку, как обычная форма HTML, либо добавьте пользовательский заголовок в запрос AJAX. Пока ваш сервер знает, где искать его во входящем запросе и может сравнить его с исходным значением, которое он запоминает из сеанса или файла cookie, вы отсортированы.