6 марта 2013 г.

Защита от XSS в ASP.NET MVC, Часть 2

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

Сегодня мы познакомимся со сторонним средством для обеспечения безопасности введенных, потенциально-опасных данных. Поможет нам в этом библиотека AntiXSS, разработанная Microsoft.
Для тестового проекта продолжим использовать пример из предыдущей статьи.

Мы закончили на том, что позволили пользователю добавлять произвольные данные через поле Notes. В пробном варианте это были лишь дополнительные html-тэги. В реальности же, чаще всего, посредством XSS, злоумышленник обычно пытается внедрить либо чистый скрипт, либо какой-то тэг, с привязанным к нему скрипту.

Давайте попробуем внедрить простой скрипт в наш проект через существующую форму создания новой персоны:
В представлении Details мы использовали хэлпер Raw, поэтому весь текст будет отображен "как есть", без кодирования. Таким образом, после добавления персоны, при открытии страницы Details, мы увидим следующее:

Красиво, не правда ли? Пользователь добился того, что в рамках нашего сайта выполнился его скрипт. Это типичная, простая XSS-атака. Теперь этот скрипт будет выполняться каждый раз, при просмотре деталей этой персоны.

Необходимо как-то противостоять этому.
Первое, что приходит в голову, - это попытаться запретить использование скриптов, делая проверку в контроллере. Например так:

   1:  [HttpPost]
   2:  public ActionResult Index(Person inputModel)
   3:  {
   4:      if (ModelState.IsValid)
   5:      {
   6:          if (inputModel.Notes.ToLower().Contains("<script"))
   7:              inputModel.Notes = string.Empty;
   8:   
   9:          TempData.Add("AddedPerson", inputModel);
  10:      }
  11:   
  12:      return RedirectToAction("Details");
  13:  }

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

Для понимания всей серьезности ситуации, приведу несколько примеров современных XSS.

XSS Locator:

';alert(String.fromCharCode(88,83,83))//';alert(String.fromCharCode(88,83,83))//";
alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//--
></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>

XSS Locator 2:

'';!--"<XSS>=&{()}

Image XSS:

<IMG SRC="javascript:alert('XSS');">

Image XSS 2:

<IMG SRC=JaVaScRiPt:alert('XSS')>

Default SRC tag:

<IMG SRC= onmouseover="alert('xxs')">

UTF-8 Unicode XSS:

<IMG SRC=&amp;#106;&amp;#97;&amp;#118;&amp;#97;&amp;#115;&amp;#99;&amp;
#114;&amp;#105;&amp;#112;&amp;#116;&amp;#58;&amp;#97;&amp;#108;&amp;#101;&amp;
#114;&amp;#116;&amp;#40;&amp;#39;&amp;#88;&amp;#83;&amp;#83;&amp;#39;&amp;#41;>

Body tag:

<BODY ONLOAD=alert('XSS')>

Все взято с OWASP. И это лишь мизерная часть из имеющихся там, реальных примеров.

Итак, что же мы можем сделать, чтобы хоть немного себя обезопасить?
Давайте воспользуемся AntiXSS-библиотекой. Добавим ее через NuGet в наш проект:

После установки данного пакета, в References появится две библиотеки: AntiXSSLibrary и HtmlSenitizationLibrary:

Теперь осталось лишь применить AntiXSS в нашем примере. Для этого, мы добавим в контроллере специальный метод для проверки значения полученного свойства:

   1:  [HttpPost]
   2:  public ActionResult Index(Person inputModel)
   3:  {
   4:      if (ModelState.IsValid)
   5:      {
   6:         inputModel.Notes = Sanitizer.GetSafeHtmlFragment(inputModel.Notes);
   7:   
   8:         TempData.Add("AddedPerson", inputModel);
   9:      }
  10:   
  11:      return RedirectToAction("Details");
  12:  }

И не забудьте подключить в контроллере пространство имен Microsoft.Security.Application.
Метод GetSafeHtmlFragmet просто отсекает опасные конструкции из заданного контента. И если мы еще раз попробуем совершить предыдущую атаку скриптом, то результат будет таким:
Скриптинг не прошел. Небезопасная часть контента была просто пропущена, и ее даже нет в коде страницы:

Таким образом, мы позволили пользователю вводить произвольные данные в форму, но в то же время имеем какую-никакую защиту от XSS-атак.

В итоге, хочу еще раз напомнить, что хотя и можно пытаться защититься от XSS-своими силами, стоит все же положиться на стандартные механизмы защиты asp.net mvc, и ни в коем случае не позволять ввода произвольных данных пользователем. В крайнем случае, это может быть лишь админ, или например, пользователь будет вводить текст только на той странице, где кроме него никто это не увидит. В любом случае, беречься нужно. Ибо xss любой школьник освоить может, и просто ради интереса изводить ваш сайт.

_____
Исходники