Эта статья — своего рода ‘cheat sheet’ для веб-разработчика. Она даёт представление о «программе-минимум» для создания веб-приложения, защищённого от самых распространённых угроз.

Экранирование пользовательского ввода

Что это?

В случае если данные пользовательского ввода, отображаемые в браузере, не предполагают наличия активного контента (HTML-разметки, CSS-стилей, JavaScript), данные пользовательского ввода должны быть закодированы (экранированы) перед использованием (отображением). Экранирование предполагает замену специальных символов (набор специальных символов зависит от контекста — HTML, JavaScript и т.д) для того, чтобы обработанные данные трактовались как тест, а не как активный контент. С точки зрения безопасности это делается главным образом для защиты от XSS.

Что делать?

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

Возможные контексты экранирования:

  • HTML-контекст — необходимо экранирование для HTML.

  • HTML-атрибут — необходимо экранирование для HTML-атрибута.

  • JavaScript — необходимо экранирование для JavaScript.

  • URL — необходимо экранирование для URL.

Кроме того, хранение закодированных данных может привести к двойному экранированию. Многие фреймворки экранируют текст по умолчанию (например ASP.NET Core).

Многие фреймворки предоставляют встроенный набор методов для данных целей.

Примеры

  • Экранирование HTML

    System.Net.WebUtility.HtmlEncode("<b>Markup</b>");
    // &lt;b&gt;Markup&lt;/b&gt
  • Экранирование HTML-атрибута

    System.Web.HttpUtility.HtmlAttributeEncode("Quotation marks \" are encoded but not safe in case of single quoted attributes '"); '")
    // Quotation marks &quot; are encoded but not safe in case of single quoted attributes &#39
  • Экранирование JavaScript

    System.Text.Encodings.Web.JavaScriptEncoder.Default.Encode("Quotes ', \" are encoded. HTML-sensitive characters such as <, >, & are also encoded.'");.'")
    // Quotes \u0027, \u0022 are encoded. HTML-sensitive characters such as \u003C, \u003E, \u0026 are also encoded.\u0027
  • Экранирование JSON

    System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping.Encode("Quotation \" are escaped and HTML-sensitive characters such as <, >, & are not encoded");ed")
    // Quotation \" are escaped and HTML-sensitive characters such as <, >, & are not encoded
  • Экранирование URL

    System.Net.WebUtility.UrlEncode("Spaces and question mark?")
    // Spaces+and+question+mark%3F

Санитайзинг пользовательского ввода

Что это?

В случае если данные пользовательского ввода, отображаемые в браузере, предполагают наличие активного контента (HTML-разметки, CSS-стилей, JavaScript), необходимо выполнять санитайзинг пользовательского ввода — удаление/экранирование всего неразрешённого (например, в контексте HTML это касается тегов и атрибутов). С точки зрения безопасности это делается главным образом для защиты от XSS.

Что делать?

Предпочтительным подходом в таком случае является whitelisting — явное перечисление того, что разрешено. Конкретная логика и список разрешённых тегов и атрибутов сильно зависят от конкретного приложения. Реализовать санитайзинг безопасным образом в целом довольно сложно, предпочтительно использовать готовые протестированные решения и библиотеки. В контексте ASP.NET таким решением может быть использование Html Agility Pack — HTML-парсера, на основе которого можно создать необходимую логику санитайзинга HTML, удовлетворяющую необходимым требованиям приложения.

Пример

Пользователь может оставлять комментарии, содержащие теги <strong>. Пользовательский ввод:

<strong>Важный</strong> комментарий <script>alert(1)</script>

Пользовательский ввод после санитайзинга:

<strong>Важный</strong> комментарий alert(1)

Контроль над произвольным пользовательским вводом

Что это?

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

Что делать?

  • Валидация пользовательского ввода

    Пример

    Система принимает сообщение об ошибке как часть URL:

    https://some.example?error=<script>alert(1)<sript>

    Данное сообщение затем отображается в HTML-разметке:

    <p>Произошла ошибка: {error}<p>

    Примечание: В данном случае система также уязвима к XSS-атаке (защита достигается путём экранирования данных — см. следующий пункт).

    Поскольку в данном случае система принимает абсолютно любое сообщение, система открыта для фишинга. Например:

    https://some.example?error=Перейдите по ссылке https://ssome.example/pwd_change для срочной смены пароля

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

    https://some.example?errorType=1

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

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

    Пример

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

Контроль за легитимностью запросов на изменение данных

Что это?

Позволяет удостовериться, что запрос к системе на изменение данных действительно инициирован пользователем. Основной целью данного механизма контроля является защита от таких атак как CSRF и фишинг.

Что делать?

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

    Пример

    Потенциально уязвимая форма:

    <form action="/change_email">
      <label for="email">Email:</label><br>
      <input type="text" id="email" name="email" value="John">
      <input type="submit" value="Submit">
    </form>

    Форма с токеном, включённым в виде скрытого поля:

    <form action="/change_email">
      <label for="email">Email:</label><br>
      <input type="text" id="email" name="email" value="John">
      <input type="hidden" name="antiforgerytoken" value="GklQxnJiLk6LM4MMYZL5BQ==">
      <input type="submit" value="Submit">
    </form>

    На каждом запросе веб-приложение должно валидировать токен.

  • Использование атрибута SameSite для куки пользовательской сессии
    Если приложение использует куки для хранения пользовательской сессии, крайне желательно использование атрибута SameSite со значением Strict или Lax. Это предотвратит отправку авторизованных запросов, источником которых не является само веб-приложение.

    Пример

    <configuration>
      <system.web>
        <httpCookies sameSite="Strict" requireSSL="true" />
        <sessionState CookieSameSite="Lax" />
      <system.web>
    <configuration>
  • Подтверждение действия от пользователя
    Механизм подтверждения действия от пользователя с помощью дополнительного диалога либо 2FA крайне затруднит атаку. Так как данный подход требует дополнительных действий со стороны пользователя, его имеет смысл использовать для наиболее важных действий.

  • GET-запросы не должны изменять данные на сервере
    HTTP-запросы GET должны быть идемпотентными; в данном контексте — не вносить изменения в данные на сервере. Это связано главным образом с тем, что GET-запрос сделать намного проще, чем, например, POST, и намного сложнее сделать безопасным, если запрос меняет данные. Даже использование antiforgery токенов не гарантирует безопасности, т. к. GET-запросы не имеют тела, все их параметры являются частью URL, и относительно велика вероятность утечки токена через историю браузера, логи и т.д.

  • Валидация источника запроса
    Проверка HTTP-заголовков Origin и/или Referer. Данный механизм защиты может рассматриваться в качестве дополнительного к вышеперечисленным методам, но не в качестве единственного, т. к. значение заголовков зачастую может быть пустым (по целому ряду причин — из-за VPN, firewall и т. д.) и также может быть потенциально недостоверным.

Контроль над фреймингом

Что это?

Контроль над фреймингом предполагает реализацию явной политики относительно того, может ли веб-приложение (сайт) отображаться во фрейме (HTML-тег <iframe>), какие части приложения могут отображаться во фрейме и при каких условиях. Основной целью такой политики является защита от clickjacking.

Что делать?

  • Использование HTTP-заголовка Content-Security-Policy
    Данный заголовок позволяет с помощью директивы frame-src указать, в каких случаях ресурс может быть отображён в iframe.

    Примеры

    Фрейминг запрещён:

    Content-Security-Policy: frame-src: ‘none’

    Фрейминг разрешён только для страниц самого веб-приложения:

    Content-Security-Policy: frame-src: ‘self’
  • Использование HTTP-заголовка X-Frame-Options
    Данный заголовок не является стандартным. Тем не менее, он полезен для браузеров, не поддерживающих CSP (например, Internet Explorer) . Данный заголовок позволяет указать, в каких случаях ресурс может быть отображён в iframe.

    Пример

    Фрейминг запрещён:

    X-Frame-Options: DENY

    Фрейминг разрешён только для страниц самого веб-приложения:

    X-Frame-Options: SAMEORIGIN 

Использование специализированных HTTP-заголовков безопасности

Что это?

Различные HTTP-заголовки, позволяющие настраивать относящиеся к безопасности аспекты коммуникации через протокол HTTP.

Что делать?

  • Content-Security-Policy
    Позволяет контролировать, какой контент и откуда может использовать веб-приложение (скрипты, стили, шрифты, изображения), в каких случаях приложение может отображаться в iframe, политику использования HTTPS и другие аспекты, связанные с безопасностью веб-приложения. Чем строже политика, заданная в CSP-директивах, тем лучше с точки зрения безопасности. Различные аспекты политики предоставляют защиту от различных видов атак. Например, директива script-src, предоставляющая контроль над разрешёнными источниками для JavaScript, является отличным средством защиты от XSS. Директива frame-src является хорошим подспорьем в борьбе с clickjaсking.

    Пример

    • Разрешены скрипты, стили, шрифты и встроенный контент из файлов, предоставляемых самим сайтом; встроенные скрипты и стили запрещены.

    • Фрейминг запрещён.

    • Отправка форм — только с самого сайта.

    • Все запросы из страниц выполняются только по HTTPS.

Content-Security-Policy:  style-src 'self'; script-src 'self'; object-src 'self'; img-src 'self'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests;
  • Permissions-Policy (ex-Feature-Policy)
    Данный HTTP-заголовок позволяет явным образом указать, какие клиентские (JavaScript) API может использовать приложение. Это может быть особенно полезно для ограничения использования камеры, микрофона и т. д.

    Пример

    Политика ниже запрещает использование микрофона, камеры, payment API. Геолокация разрешена только для самого приложения:

    Feature-Policy: camera: 'none'; microphone 'none'; payment:'none'; geolocation 'self’
    Permissions-Policy: camera=(), microphone=(), payment=(), geolocation=(self)
  • Strict-Transport-Security
    Данный заголовок помогает реализовать политику использования защищённого HTTPS-соединения и в целом может быть полезен в контексте борьбы с утечкой данных, а также с атаками типа Man in the middle (подробнее см. в разделе «HTTPS»).

  • Referrer-policy
    Данный заголовок помогает предотвратить утечку данных из URL в случае перехода из приложения по внешним ссылкам (подробнее см. в разделе «Меры по предотвращению утечки данных»).

  • X-Frame-Options
    Данный заголовок не является стандартным. Тем не менее, он может быть полезен для браузеров, не поддерживающих CSP. Данный заголовок позволяет указать, в каких случаях ресурс может быть отображен в iframe (подробнее — в разделе «Контроль над фреймингом»).

  • X-XSS-Protection
    Этот подход менее гибок и используется реже, чем Content-Security-Policy. Тем не менее, он полезен для браузеров, не поддерживающих CSP (например, Internet Explorer). Данный заголовок разрешает браузеру использовать встроенный механизм борьбы с XSS (если браузер имеет таковой) в режиме блокирования страницы либо удаления потенциально опасного кода.

    Пример

    Конфигурация ниже блокирует страницы, содержащие подозрительный (с точки зрения возможной XSS-атаки) код:

    X-XSS-Protection: 1; mode=block
  • X-Content-Type-Options
    Данный заголовок может быть полезен в контексте борьбы с различными атаками, связанными с выполнением содержимого файлов приложения. Использование данного заголовка полезно в том случае, если приложение предоставляет возможность хранения/работы с файлами (подробнее в разделе «Правильная организация хранения файлов и доступа к ним»).

  • HTTP-заголовки не должны быть основным и единственным средством защиты
    Насколько перечисленные выше HTTP-заголовки эффективны — целиком и полностью зависит от уровня поддержки и качества реализации в конкретном браузере. Таким образом, HTTP-заголовки не должны рассматриваться как единственная мера защиты для каждого конкретного вектора атаки, и механизмы защиты на стороне сервера тоже необходимы.

Управление пользовательской сессией

Что это?

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

Что делать?

  • Значение идентификатора сессии должно быть случайным
    Значение должно быть случайным, сложным для предугадывания и не поддаваться простому перечислению.

  • Ротация идентификатора сессии
    При каждом новом входе в систему пользователю должен присваиваться новый идентификатор сессии. Предыдущий идентификатор должен быть инвалидирован.

    Пример

    Чтобы предотвратить переиспользование идентификатора сессии в ASP.NET, куки с идентификатором необходимо инвалидировать в процессе разлогирования пользователя:

    Session.Abandon();
    Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
  • Защита куки сессии
    Куки сессии должны использовать следующие атрибуты:

    • HttpOnly
      Использование данного атрибута поможет предотвратить возможность получения значения идентификатора сессии с помощью клиентского скрипта.

    • Secure
      Данный флаг предотвратит передачу идентификатора сессии по незащищённому HTTP-протоколу.

    • SameSite
      Значения Strict и Lax данного атрибута предотвращают отправку куки с запросами, источниками которых не является сайт, создавший куки. Это помогает предотвратить CSRF-атаки.

    • Также для таких куки не должно быть установлено свойство Domain
      Установка данного свойства открывает доступ к куки для поддоменов, что нежелательно, если не все поддомены находятся под контролем автора приложения.

    Пример

    Конфигурация для ASP.NET в файле web.config:

    <system.web>
    ...
      <httpCookies sameSite="Lax" requireSSL="true" httpOnlyCookies="true" />
      <sessionState … cookieSameSite="Lax" />
    ...
    <system.web>
  • Избегать передачи незакодированного идентификатора сессии в URL
    Некоторые приложения могут передавать идентификатор сессии как часть URL. Если идентификатор не закодирован, это может привести к утечке идентификатора, например через лог запросов на сервере, историю браузера. Использование куки для хранения идентификатора сессии является предпочтительным.

Использование HTTPS

Что это?

Приложение, не использующее протокол HTTPS, по умолчанию является уязвимым: данные передаются по сети в незашифрованном виде, что делает приложение подверженным таким атакам, как Man in the middle или content sniffing. Возможна утечка персональных данных, паролей и т. д. Любое приложение, имеющее дело с персональными данными и/или паролями, должно использовать протокол HTTPS.

Что делать?

  • Перенаправление всех запросов с HTTP на HTTPS либо отказ в обслуживании запросов HTTP
    Веб-приложение должно осуществлять перенаправление всех запросов с HTTP на HTTPS. Данный подход работает в случае, если клиентом является браузер. В остальных случаях (например, мобильное или десктопное приложение) вместо перенаправления необходимо отвергать запрос с ошибкой.

  • HTTP-заголовок Strict-Transport-Security
    Позволяет обеспечить доступ к ресурсу исключительно по протоколу HTTPS в случае, если клиентом ресурса является браузер. После ответа на первый запрос, содержащий данный заголовок, все последующие запросы браузер будет выполнять с использованием защищённого протокола HTTPS.

    Пример

    Strict-Transport-Security: max-age=63072000; includeSubDomains
  • Использование HSTS с директивой preload
    Браузер не имеет возможности заранее, до первого запроса, определить, требует ли ресурс доступа только по HTTPS. Например, пользователь запрашивает в браузере ресурс http://mysite.com. Данный запрос выполняется по протоколу HTTP. Если в ответе на этот запрос содержится заголовок Strict-Transport-Security, все последующие запросы будут выполняться по протоколу HTTPS. Очевидно, что первый запрос всё ещё может быть уязвим. Предотвратить этот риск помогает использование директивы preload и добавление ресурса в так называемый preload list (подробнее см. https://hstspreload.org).

    Пример

    Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  • Запрет HTTP-запросов к API — только HTTPS
    В случае если клиентом API является не только браузер, существует вероятность, что перенаправление с HTTP на HTTPS (например, с помощью HSTS) не будет корректно обработано клиентом. В целом для API предпочтительно отвергать запросы по HTTP.

Защищённые куки

Что это?

Куки могут содержать важную с точки зрения безопасности информацию, например идентификатор пользовательской сессии. Передача таких куки по незащищённому HTTP-соединению и возможность доступа из клиентского кода могут стать источником таких атак, как session hijacking.

Что делать?

  • Флаг HttpOnly
    Предотвращает возможность доступа к куки с помощью клиентского скрипта. Флаг должен быть установлен для всех куки, доступ к которым из клиентского скрипта не предполагается.

  • Флаг Secure
    Куки с данным флагом будут переданы с запросом только в случае, если запрос выполняется по протоколу HTTPS.

  • Атрибут SameSite
    Данный атрибут может принимать несколько значений. Значения Lax или Strict предотвратят отправку куки с запросами, выполняемыми не из приложения, что может сильно затруднить атаку типа CSRF.

Пример

HTTP-заголовок, устанавливающий куки “SessionId”:

Set-Cookie: SessionId=fxy40phg0wejmfpnlwfwevmi; Secure; HttpOnly; SameSite=Strict

Пример конфигурации в web.config для ASP.NET:

<configuration>
 <system.web>
  <httpCookies sameSite="Strict" requireSSL="true" />
  <anonymousIdentification cookieRequireSSL="true" /> 
  <authentication>
    <forms cookieSameSite="Lax" requireSSL="true" />
  </authentication>
  <sessionState cookieSameSite="Lax" />
  <roleManager cookieRequireSSL="true" />
 <system.web>
<configuration>

Правильная организация хранения файлов и доступа к ним

Что это?

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

Что делать?

  • Валидация и санитайзинг имён файлов
    Имена файлов как минимум не должны иметь расширений, приводящих к выполнению кода на стороне сервера или на клиенте (например, .bat, .cmd, .vbs).

  • Ограничение типов файлов, разрешённых для загрузки
    Типы файлов должны быть ограничены с использованием подхода whitelisting (явное задание списка разрешённых типов файлов).

  • Использование HTTP-заголовка X-Content-Type-Options
    Использование данного заголовка помогает предотвратить выполнение содержимого файлов в браузере.

    Пример

    X-Content-Type-Options: nosniff
  • Хранение файлов в домене, отличном от домена приложения
    Файлы должны храниться в домене, отличном от домена приложения (не в том же домене и не в его поддомене), с как можно более строгими политиками CSP и Permissions Policy, по возможности полностью запрещающими выполнение скриптов и использование различных клиентских API.

    Пример

    https://mywebapp.com — веб-приложение
    
    https://filesmywebapp.com — файловое хранилище

Данные, с утечкой которых надо бороться

Что это?

К таким данным относятся пользовательские данные и технические данные о работе приложения (сообщения об ошибках, стеки ошибок и т. п.).

Что делать?

  • Контроль над сообщениями об ошибках
    Данная техника предполагает строгое ограничение информации об ошибках, доступной пользователям системы. Содержимое сообщения об ошибках должно быть строго определено. Сообщения об ошибках должны быть полезны для конечного пользователя, но не должны содержать излишних технических подробностей о системе либо других важных с точки зрения безопасности, но не важных для конечного пользователя данных (идентификаторов, информации о структуре хранилища данных, стеков ошибок и т. д.).

    Пример

    Для ASP.NET-приложения обработчик Application_Error в Global.asax файле может быть использован для перехвата необработанных ошибок:

    void Application_Error(object sender, EventArgs e)
    {
       Response.Redirect("ErrorPage.aspx", true);
    }

    Для ASP.NET Core обработчик исключений может быть зарегистрирован в методе Configure класса Startup: 

    public void Configure(IApplicationBuilder app)
    {
       app.UseExceptionHandler("/Error");
    }
  • URL не должен содержать конфиденциальные данные
    В целом URL не должны содержать потенциально конфиденциальные данные, т. к. URL могут быть сохранены как часть внешней инфраструктуры логирования (история браузера, логи серверов; такие HTTP-заголовки, как Referer, и т. д.). В случае если передача таких данных через URL необходима, данные должны быть зашифрованы.

  • Использование HTTP-заголовка Referrer-Policy
    Данный заголовок позволяет ограничить информацию, передаваемую в заголовке Referer. Может быть использован для исключения передачи параметров строки запроса либо URL целиком.

    Пример

    Referrer-Policy: no-referrer
  • Исключение из ответа на HTTP-запрос HTTP-заголовков, раскрывающих детали серверной реализации приложения
    Некоторые технологии и платформы по умолчанию добавляют в ответы на HTTP-запросы определённое количество различных HTTP-заголовков. Некоторые из них могут раскрывать такие технические подробности, как версия сервера, платформы и т. д. Данные заголовки, если они не несут функциональности для конечного пользователя, должны быть убраны из ответа на запрос.

    Пример

    Некоторые заголовки для IIS + ASP.NET, по умолчанию включённые в запрос:

    Server: Microsoft-IIS/7.5
    
    X-AspNetMvc-Version: 3.0
    
    X-AspNet-Version: 4.0.30319
    
    X-Powered-By: ASP.NET

    Заголовок “Server” может быть удалён с помощью URL Rewrite rule:

    <rewrite>    
      <outboundRules rewriteBeforeCache="true">
        <rule name="Remove Server header">
          <match serverVariable="RESPONSE_Serve-r" pattern=".+" />
          <action type="Rewrite" value="" />
        </rule>
      </outboundRules>
    </rewrite>

    Заголовок “XPowered-By” может быть удалён через конфигурацию IIS (“HTTP response headers item”). 

    Заголовок “X-AspNetMvc-Version" может быть удалён при использовании следующего кода в событии Application_Start:

    MvcHandler.DisableMvcResponseHeader = true;

    Заголовок “X-AspNet-Version“ может быть удалён при помощи конфигурации в файле web.config:

    <system.web>
      <httpRuntime enableVersionHeader="false" />
    </system.web>

Контроль за перенаправлением на внешние ресурсы

Что это?

Если приложение может перенаправлять пользователя на внешние ресурсы и такие перенаправления не валидируются, это может способствовать фишинговым атакам, особенно в случае, если URL внешнего ресурса является частью ввода (например, параметр строки запроса URL). 

Что делать?

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

Безопасное использование пользовательского ввода в параметрах запросов к БД

Что это?

Если какой-либо запрос к БД принимает один или более параметров, параметры должны быть должным образом закодированы (в контексте SQL) перед использованием. В противном случае система может быть уязвима к SQL-инъекциям. Это особенно актуально для SQL-запросов, использующих данные пользовательского ввода (поля форм, часть строки запроса URL и т. д.) в качестве параметров запроса.

Что делать?

  • Параметризация запросов
    Большинство платформ для работы с БД предоставляют такие возможности. Например, Microsoft Entity Framework по умолчанию не является уязвимым к такому типу атаки, т. к. весь пользовательский ввод преобразуется в SQL-параметры. Но в то же время фреймворк даёт возможность выполнения «сырых» запросов напрямую, и в таком случае обязанностью разработчика является передача ввода в качестве параметров запроса.

    Пример

    Запрос, уязвимый к SQL-инъекции:

    using var connection = new SqlConnection(ConnectionString);
    connection.Open();
    string sqlExpression = $"SELECT * FROM Users WHERE Name='{name}'";
    var command = new SqlCommand(sqlExpression, connection);
    using var reader = command.ExecuteReader();

    Параметризованный запрос:

    using var connection = new SqlConnection(ConnectionString);
    connection.Open();
    string sqlExpression = $"SELECT * FROM Users WHERE Name=@Name";
    var command = new SqlCommand(sqlExpression, connection);
    command.Parameters.Add(new SqlParameter("@Name", SqlDbType.NVarChar) { Value = name });
    using var reader = command.ExecuteReader();
  • Политика предоставления наименьших необходимых разрешений
    Политика предоставления наименьших необходимых разрешений также является полезной мерой защиты с точки зрения минимизации вреда в случае SQL-инъекции. Например, использование различных аккаунтов на чтение и доступ может предотвратить возможность изменения данных даже в случае успешной SQL-инъекции.

Заключение

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

Комментарии (3)


  1. visirok
    24.08.2021 23:34

    Очень полезная по содержанию и хорошо организованная дидактически статья. Очень большое спасибо!

    А что можно сказать о неописанных здесь рисках, специфических для общения мобильных приложений со своими серверами?


    1. SecurityForAll Автор
      12.09.2021 17:10
      +1

      Большая часть материала, приведённого в статье, имеет непосредственное применение также и в контексте мобильных приложений (использование HTTPS, авторизация и аутентификация, валидация источника запросов, валидация и санитайзинг пользовательского ввода, предотвращение утечки данных, конфигурация HTTP заголовков и т.д.).

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


  1. grebenyukov
    01.12.2021 21:13

    Добрый день! Подскажите, а что дает хранение файлов в отдельном домене?