Статьи

Понимание подделки межсайтовых запросов в .NET

Вы можете создавать безопасные веб-приложения только с самого начала, принимая во внимание безопасность. Для этого необходимо подумать о возможных способах атаки на ваш сайт при создании каждой страницы, формы и действия. Это также требует понимания наиболее распространенных типов проблем безопасности и способов их решения.

Наиболее распространенный тип дыры в безопасности на веб-странице позволяет злоумышленнику выполнять команды от имени пользователя, но неизвестно пользователю. Атака межсайтовых запросов подделывает доверие, которое веб-сайт уже установил с веб-браузером пользователя.

В этом руководстве мы обсудим, что такое атака на подделку межсайтовых запросов и как она выполняется. Затем мы создадим простое приложение ASP.NET MVC , которое уязвимо для этой атаки, и исправим приложение, чтобы оно не повторилось.


Атака на подделку межсайтовых запросов сначала предполагает, что жертва уже прошла проверку подлинности на целевом веб-сайте, таком как банковский сайт, Paypal или другой сайт, который должен быть атакован. Эта аутентификация должна храниться таким образом, чтобы, если пользователь покидает сайт и возвращается, он по-прежнему считается зарегистрированным целевым веб-сайтом. Затем злоумышленник должен получить от жертвы доступ к странице или ссылке, которая будет выполнять запрос или публиковать на целевом веб-сайте. Если атака сработает, целевой веб-сайт увидит запрос от жертвы и выполнит запрос от имени этого пользователя. Это, по сути, позволяет злоумышленнику выполнить любое действие на целевом веб-сайте в качестве жертвы. Потенциальный результат может перевести деньги, сбросить пароль или изменить адрес электронной почты на целевом веб-сайте.

Чтобы заставить жертву использовать ссылку, им не нужно нажимать на ссылку. Простая ссылка на изображение может быть достаточно:

1
<img src=»http://www.examplebank.com/movemoney.aspx?from=myaccount&to=youraccount&amount=1000.00″ width=»1″ height=»1″ />

Включение такой ссылки в безобидную на первый взгляд пост на форуме, в комментарии в блоге или на сайт в социальных сетях может застать пользователя врасплох. Более сложные примеры используют JavaScript для создания полного HTTP-запроса и отправки его на целевой веб-сайт.


Давайте создадим простое приложение ASP.NET MVC и оставим его уязвимым для этой атаки. Я буду использовать Visual Studio 2012 для этих примеров, но это также будет работать в Visual Studio 2010 или Visual Web Developer 2010 будет работать, если вы установили поддержку MVC 4, которую можно загрузить и установить из Microsoft .

новая БД-столбец

Начните с создания нового проекта и выберите использование шаблона интернет-проекта . Либо View Engine будет работать, но здесь я буду использовать ASPX View Engine.

Мы добавим одно поле в таблицу UserProfile для хранения адреса электронной почты. Под Server Explorer разверните Data Connections . Вы должны увидеть Соединение по умолчанию, созданное с информацией для входа и членства. Щелкните правой кнопкой мыши таблицу UserProfile и выберите « Открыть определение таблицы» . В пустую строку под таблицей UserName мы добавим новый столбец для электронного письма. Назовите адрес электронной emailaddress столбца, emailaddress ему тип nvarchar(MAX) и установите флажок « Разрешить пустые значения» . Теперь нажмите Обновить, чтобы сохранить новую версию таблицы.

Это дает нам базовый шаблон веб-приложения с поддержкой входа в систему, очень похожий на то, что многие авторы начали бы с попытки создания приложения. Если вы запустите приложение сейчас, вы увидите, что оно отображается и работает. Нажмите F5 или используйте DEBUG -> Start Debugging из меню, чтобы открыть сайт.

по умолчанию, веб-страница

Давайте создадим тестовую учетную запись, которую мы можем использовать для этого примера. Нажмите на ссылку Зарегистрироваться и создайте учетную запись с любым именем пользователя и паролем, которые вы хотите. Здесь я собираюсь использовать учетную запись под названием testuser . После создания вы увидите, что я вошел в систему как testuser. После того, как вы это сделаете, выйдите, и давайте добавим страницу в это приложение, чтобы позволить пользователю изменить свою электронную почту.

по умолчанию-регистр

Прежде чем мы создадим эту страницу для изменения адреса электронной почты, нам сначала нужно внести одно изменение в приложение, чтобы код знал о новом столбце, который мы только что добавили. Откройте файл AccountModels.cs в папке Models и обновите класс UserProfile чтобы он соответствовал следующему. Это говорит классу о нашей новой колонке, где мы будем хранить адрес электронной почты для учетной записи.

1
2
3
4
5
6
7
8
9
[Table(«UserProfile»)]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get;
    public string UserName { get;
    public string EmailAddress { get;
}

Откройте файл AccountController.cs . После функции RemoveExternalLogins добавьте следующий код для создания нового действия. Это получит текущий адрес электронной почты для вошедшего в систему пользователя и передаст его представлению для действия.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public ActionResult ChangeEmail()
   {
       // Get the logged in user
       string username = WebSecurity.CurrentUserName;
       string currentEmail;
 
       using (UsersContext db = new UsersContext())
       {
           UserProfile user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == username);
           currentEmail = user.EmailAddress;
       }
 
       return View(currentEmail);
   }

Нам также нужно добавить соответствующее представление для этого действия. Это должен быть файл с именем ChangeEmail.aspx в папке Views\Account :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<%@ Page Title=»» Language=»C#» MasterPageFile=»~/Views/Shared/Site.Master» Inherits=»System.Web.Mvc.ViewPage<string>» %>
 
<asp:Content ID=»Content1″ ContentPlaceHolderID=»TitleContent» runat=»server»>
Change Email Address
</asp:Content>
 
<asp:Content ID=»Content2″ ContentPlaceHolderID=»MainContent» runat=»server»>
 
<hr>
<h2>Change Email Address</h2>
 
<p>Current Email Address: <%= Model ??
 
<% using(Html.BeginForm()) { %>
    <input type=»text» name=»newemail» />
    <input type=»submit» value=»Change Email» />
<% } %>
 
</asp:Content>
 
<asp:Content ID=»Content3″ ContentPlaceHolderID=»FeaturedContent» runat=»server»>
</asp:Content>
 
<asp:Content ID=»Content4″ ContentPlaceHolderID=»ScriptsSection» runat=»server»>
</asp:Content>

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

переключающая электронная почта страницы

Если мы запустим эту страницу и перейдем к действию /Account/ChangeEmail , мы увидим, что в настоящее время у нас нет электронной почты. Но у нас есть текстовое поле и кнопка, которую мы можем использовать, чтобы исправить это. Сначала нам нужно создать действие, которое будет выполняться при отправке формы на этой странице.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
[HttpPost]
public ActionResult ChangeEmail(ChangeEmailModel model)
{
    string username = WebSecurity.CurrentUserName;
 
    using (UsersContext db = new UsersContext())
    {
       UserProfile user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == username);
       user.EmailAddress = model.NewEmail;
       db.SaveChanges();
    }
 
    // And to verify change, get the email from the profile
    ChangeEmailModel newModel = new ChangeEmailModel();
    using (UsersContext db = new UsersContext())
    {
       UserProfile user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == username);
       newModel.CurrentEmail = user.EmailAddress;
    }
 
    return View(newModel);
}

После внесения этого изменения запустите веб-сайт и снова перейдите к действию /Account/ChangeEmail которое мы только что создали. Теперь вы можете ввести новый адрес электронной почты, нажать кнопку « Изменить адрес электронной почты» и увидеть, что адрес электронной почты будет обновлен.


Как написано, наше приложение уязвимо для атаки подделки межсайтовых запросов. Давайте добавим веб-страницу, чтобы увидеть эту атаку в действии. Мы собираемся добавить страницу на веб-сайте, которая изменит адрес электронной почты на другое значение. В файле HomeController.cs мы добавим новое действие с именем AttackForm .

1
2
3
4
public ActionResult AttackForm()
{
   return View();
}

Мы также добавим представление для этого AttackForm.aspx именем AttackForm.aspx в папку /Views/Home . Это должно выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ Page Title=»» Language=»C#» MasterPageFile=»~/Views/Shared/Site.Master» Inherits=»System.Web.Mvc.ViewPage<dynamic>» %>
 
<asp:Content ID=»Content1″ ContentPlaceHolderID=»TitleContent» runat=»server»>
Attack Form
</asp:Content>
 
<asp:Content ID=»Content2″ ContentPlaceHolderID=»MainContent» runat=»server»>
 
<hr>
<h2>Attack Form</h2>
 
<p>This page has a hidden form, to attack you, by changing your email:</p>
 
<iframe width=»1px» height=»1px» style=»display:none;»>
<form name=»attackform» method=»POST» action=»<%: Url.Action(«ChangeEmail», «Account») %>»>
    <input type=»hidden» name=»NewEmail» value=»newemail@evilsite.com»/>
</form>
</iframe>
<script type=»text/javascript»>
    document.attackform.submit();
</script>
 
</asp:Content>
 
<asp:Content ID=»Content3″ ContentPlaceHolderID=»FeaturedContent» runat=»server»>
</asp:Content>
 
<asp:Content ID=»Content4″ ContentPlaceHolderID=»ScriptsSection» runat=»server»>
</asp:Content>

Наша страница услужливо объявляет о своем злонамеренном намерении, которое, конечно же, настоящая атака не будет делать. Эта страница содержит скрытую форму, которая не будет видна пользователю. Затем он использует Javascript для автоматической отправки этой формы при загрузке страницы.

Атака-форма

Если вы снова запустите сайт и перейдете на страницу /Home/AttackForm , вы увидите, что он загружается просто отлично, но без каких-либо внешних признаков того, что что-то произошло. Если вы сейчас перейдете на страницу /Account/ChangeEmail , вы увидите, что ваш адрес электронной почты был изменен на newemail@evilsite.com . Здесь, конечно, мы намеренно делаем это очевидным, но в реальной атаке вы можете не заметить, что ваша электронная почта была изменена.


Существует два основных способа смягчения атак этого типа. Сначала мы можем проверить реферала, с которого поступил веб-запрос. Это должно сообщить приложению, когда отправка формы не приходит с нашего сервера. Это имеет две проблемы, хотя. Многие прокси-серверы удаляют эту реферальную информацию либо намеренно для защиты конфиденциальности, либо в качестве побочного эффекта, то есть законный запрос не может содержать эту информацию. Злоумышленник также может подделать реферала, хотя это увеличивает сложность атаки.

Наиболее эффективный метод — требовать, чтобы для каждой отправки формы существовал определенный токен пользователя. Значение этого токена должно генерироваться случайным образом при каждом создании формы, и форма принимается, только если токен включен. Если токен отсутствует или включено другое значение, мы не разрешаем отправку формы. Это значение может быть сохранено либо в состоянии сеанса пользователя, либо в файле cookie, чтобы мы могли проверить значение при отправке формы.

ASP.NET облегчает этот процесс, поскольку встроена поддержка CSRF. Чтобы его использовать, нам нужно всего лишь внести два изменения в наш веб-сайт.


Во-первых, мы должны добавить уникальный токен в форму, чтобы изменить адрес электронной почты пользователя при его отображении. Обновите форму в представлении ChangeEmail.aspx в /Account/ChangeForm :

1
2
3
4
5
<% using(Html.BeginForm()) { %>
    <%: Html.AntiForgeryToken() %>
    <%: Html.TextBoxFor(t=>t.NewEmail) %>
    <input type=»submit» value=»Change Email» />
<% } %>

Эта новая строка: <%: Html.AntiForgeryToken() %> сообщает ASP.NET о создании токена и размещении его в виде скрытого поля в форме. Кроме того, платформа обрабатывает размещение его в другом месте, где приложение может получить к нему доступ позже, чтобы проверить это.

Если мы сейчас загрузим страницу и посмотрим на источник, мы увидим эту новую строку в форме, отображаемой в браузере. Это наш токен:

1
2
3
4
<form action=»/Account/ChangeEmail» method=»post»><input name=»__RequestVerificationToken» type=»hidden» value=»g_ya1gqEbgEa4LDDVo_GWdGB8ko0Y91p98GTdKVKUocEBy-xAoH_Pok4iMXMxzZWX_IDDAXEkVwu3gc6UNzRKt8tjZ88I9t4NE8WT0UTT0o1″ />
    <input id=»NewEmail» name=»NewEmail» type=»text» value=»» />
    <input type=»submit» value=»Change Email» />
</form>

Нам также необходимо внести изменения в наше действие, чтобы сообщить ему, что мы добавили этот токен и что он должен проверить токен, прежде чем принимать опубликованную форму.

Опять же, это просто в ASP.NET MVC. В верхней части действия, которое мы создали для обработки опубликованной формы, с добавленным атрибутом [HttpPost] , мы добавим еще один атрибут с именем [ValidateAntiForgeryToken] . Это делает начало нашего действия теперь похожим на следующее:

1
2
3
4
5
6
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ChangeEmail(ChangeEmailModel model)
{
    string username = WebSecurity.CurrentUserName;
    *rest of function omitted*

Давайте проверим это. Сначала перейдите на страницу /Account/ChangeEmail и восстановите адрес электронной почты для вашей учетной записи с известным значением. Затем мы можем вернуться на страницу /Home/AttackForm и снова код атаки пытается изменить нашу электронную почту. Если вы вернетесь на страницу /Account/ChangeEmail раз, на этот раз вы увидите, что ранее введенный вами адрес электронной почты по-прежнему безопасен и не поврежден. Изменения, которые мы внесли в нашу форму и действие, защитили эту страницу от атаки.

Если вы посмотрите на форму атаки напрямую (это легко сделать, удалив теги <iframe> вокруг формы на странице атаки, вы увидите ошибку, которая действительно возникает, когда форма атаки пытается отправить сообщение).

неудавшееся нападение

Этих двух дополнительных строк, добавленных на сайт, было достаточно, чтобы защитить нас от этой ошибки.


Подделка межсайтовых запросов является одной из самых распространенных и опасных атак на веб-сайты. Они часто сочетаются с другими методами, которые выявляют слабые места сайта, чтобы облегчить атаку. Здесь я продемонстрировал способ защитить ваш сайт .NET от этого типа атак и сделать ваш сайт более безопасным для ваших пользователей.