Входные и выходные данные
Наиболее распространенной уязвимостью текущих приложений является некорректная обработка данных из внешних источников, особенно вводимых пользователем данных. Нужно всегда хорошо изучать входные данные, чтобы гарантировать их проверку перед использованием. Неспособность проанализировать подверженность вводимых данных атакам может привести к потере или раскрытию данных, эскалации привилегий или выполнению вредоносного кода на компьютерах пользователей.
Трагизм этой ситуации в том, что подобную проблему можно легко решить. В этом уроке мы рассмотрим, как обрабатывать данные при получении, когда он отображается на экране, и когда он хранится для последующего использования.
Зачем проверять входные данные?
Представьте, что вы создаете интерфейс, чтобы разрешить пользователю создавать учетную запись на веб-сайте. Данные профиля включают имя, электронную почту и псевдоним, который будет отображаться всем, кто посещает сайт. Что произойдет, если пользователь при создании профиля включит в этот псевдоним команды языка SQL? Например, что, если злоумышленник вводит примерно следующее фрагмент:
Eve'); DROP TABLE Users;--
Если такое значение просто вставить в базу данных, это может переопределить инструкцию SQL для выполнения таких команд, которые нам совершенно не нужны. Этот пример известен как "Атака путем внедрения кода SQL", но он лишь один из многих типов атак, которые становятся возможны без правильной обработки вводимых пользователем данных. Как мы можем выйти из этой ситуации? В этом уроке мы научим вас проверять входные данные, кодировать выходные данные и создавать параметризованные запросы (это полностью устранит описанный выше эксплойт). Далее приведены три основных метода защиты от злонамеренных действий при вводе данных в приложения.
Когда нужно проверять входные данные?
Правильный ответ — всегда. Необходимо проверять каждый элемент данных, вводимых в приложение. Это включает параметры в URL-адресе, входные данные пользователя, данные из базы данных, данные из API и все, что передается в ясном виде, что пользователь может потенциально манипулировать. Всегда используйте подход списка разрешений, что означает, что вы принимаете только "известные хорошие" входные данные, а не блок-список (где вы специально ищете неправильные входные данные), так как невозможно представить полный список потенциально опасных входных данных. Выполните эту работу на сервере, а не на стороне клиента (или в дополнение к стороне клиента), чтобы гарантировать, что защита не может быть обойтись. Обрабатывать все данные как ненадежные, и вы будете защищать себя от большинства распространенных уязвимостей веб-приложения.
Если вы используете ASP.NET, платформа обеспечивает большую поддержку проверки входных данных на стороне клиента и сервера.
Если вы используете другую веб-платформу, существуют некоторые отличные методы для проверки входных данных, доступных на памятку проверки входных данных OWASP.
Всегда используйте параметризованные запросы
Базы данных SQL обычно используются для хранения данных; Например, приложение может хранить сведения о профиле пользователя в базе данных. Вы никогда не должны создавать встроенные запросы SQL или других баз данных в коде с помощью необработанных пользовательских входных данных и отправлять их непосредственно в базу данных; это поведение является рецептом аварии, как мы видели ранее.
Например, не создавайте код, как показано в следующем встроенном примере SQL:
string userName = Request.QueryString["username"]; // receive input from the user BEWARE!
...
string query = "SELECT * FROM [dbo].[users] WHERE userName = '" + userName + "'";
Здесь мы создаем запрос, сцепляя несколько текстовых строк из входных данных от пользователя, и используем этот динамический SQL-запрос для поиска пользователя. Если злоумышленник заподозрит, что мы используем такой подход, или простым перебором методов наткнется на эту уязвимость, это обойдется нам очень дорого. Вместо этого используйте параметризованные инструкции SQL или хранимые процедуры, например:
-- Lookup a user
CREATE PROCEDURE sp_findUser
(
@UserName varchar(50)
)
SELECT * FROM [dbo].[users] WHERE userName = @UserName
С помощью этого метода можно безопасно вызвать процедуру из кода, передавая ее userName
строку, не беспокоясь о том, что она обрабатывается как часть инструкции SQL.
Всегда кодируйте выходные данные
Любые выходные данные, которые вы отображаете на экране или сохраняете в документах, необходимо кодировать и экранировать. Это может защитить вас в случае, если что-то не было пропущено в проходе очистки или код случайно создает что-то, что можно использовать злонамеренно. Этот принцип разработки гарантирует, что все данные отображаются как выходные и не будут случайно интерпретированы как нечто подлежащее выполнению. Последний момент относится к еще одной распространенной атаке, называемой "межсайтовые сценарии" (XSS).
Так как предотвращение атак XSS является стандартным требованием приложений, в этом направлении безопасности можно положиться на функционал ASP.NET. По умолчанию все выходные данные уже закодированы. Если вы используете другую веб-платформу, вы можете проверить параметры кодирования выходных данных на веб-сайтах с помощью памятки защиты OWASP XSS.
Итоги
Очистка и проверка входных данных — это обязательное требование, позволяющее считать входные данные допустимыми и безопасными для использования и хранения. Большинство современных веб-платформ предлагают встроенные функции для автоматизации некоторых из этих действий. Узнать, какие функции предлагает предпочитаемая вами платформа, можно из прилагающейся к ней документации. Чаще всего нападениям подвергаются веб-приложения, но следует помнить, что и другие типы приложений могут быть не менее уязвимы. Не считайте, что вы в безопасности, только потому, что ваше новое приложение устанавливается локально. Вам по-прежнему потребуется правильно обрабатывать входные данные пользователей, чтобы убедиться, что кто-то не использует ваше приложение для повреждения данных или повреждения репутации вашей компании.