В данной заметке мы обсудим, почему PVS-Studio считает параметры общедоступных методов источниками небезопасных данных и может выдавать предупреждения, если такие параметры используются без проверок.
Дело в том, что излишнее доверие к внешним данным может быть причиной многих уязвимостей - SQLI, XSS, path traversal и т.п. Наиболее очевидные примеры источников внешних данных - значения параметров запросов или текст, который вводит пользователь (например, в некоторое поле).
Однако излишнее доверие к параметрам общедоступных методов также может быть опасно. Под общедоступными имеются в виду методы, которые могут быть вызваны из других сборок. Например, это public методы public классов. Скорее всего, подобные методы представляют собой API для взаимодействия с библиотекой.
В чём же опасность?
Разработчики, использующие API библиотеки, могут рассчитывать, что валидация данных будет происходить внутри вызываемого метода. Соответственно, данные из внешнего источника не будут проверены перед передачей в метод.
Разработчики же самой библиотеки могут рассчитывать на то, что на вход приходят уже проверенные данные. Следовательно, необходимости в их проверке нет.
Из-за этого может возникнуть ситуация, когда ни пользователь библиотеки, ни её разработчик не провели валидацию входных данных. Это может стать причиной возникновения уязвимостей в случае передачи злоумышленником скомпрометированных данных.
Примечание. Ниже разбираются примеры с диагностикой V5608 (поиск возможных SQL инъекций). Тем не менее, эта информация актуальная и для других диагностик из группы OWASP, считающих параметры публично доступных методов источниками заражённых данных.
Посмотрим, как это может выглядеть в коде:
public class DBHelper
{
public void ProcessUserInfo(String userName)
{
....
var command = "SELECT * FROM Users WHERE userName = '" + userName + "'";
ExecuteCommand(command);
....
}
private void ExecuteCommand(String rawCommand)
{
using (SqlConnection connection = new SqlConnection(_connectionString))
{
....
using (var sqlCommand = new SqlCommand(rawCommand, connection))
{
using (var reader = sqlCommand.ExecuteReader())
....
}
}
}
}
Класс DBHelper предоставляет метод ProcessUserInfo для внешнего использования, так как он доступен из других сборок. Обратите внимание, что параметр этого метода - userName - никак не проверяется перед использованием. Значение, полученное извне, напрямую используется для создания команды (переменная command). Далее полученная команда передаётся в метод ExecuteCommand, где без проверки используется для создания объекта типа SqlCommand.
В данном случае анализатор сможет выдать предупреждение о возможной SQLI, если принять userName за источник опасных данных.
Теперь рассмотрим возможный вариант использования метода ProcessUserInfo внешним приложением:
static void TestHelper(DBHelper helper)
{
var userName = Request.Form["userName"];
helper.ProcessUserInfo(userName);
}
Разработчик, написавший данный фрагмент кода, может не иметь доступа к коду класса DBHelper и рассчитывать на то, что валидация входных данных будет происходить внутри метода ProcessUserInfo. Возникает ситуация, при которой ни текущий код, ни код метода ProcessUserInfo не провёл валидацию данных, а значит, приложение будет уязвимо перед SQL инъекциями.
При анализе кода метода TestHelper анализатор не сможет предупредить о возможной SQL инъекции, т.к. не имеет доступа к исходному коду метода ProcessUserInfo. Однако, как мы видим, ситуация опасная, и сообщить о ней хочется.
Поэтому анализатор выдаст предупреждение там, где он сможет это сделать, - при анализе исходного кода метода ProcessUserInfo. В данном случае будет выдано предупреждение V5608 на низком уровне достоверности.
Если вы не хотите видеть подобных предупреждений, то можете отключить их с помощью комментария вида //-V::5608:3 в .pvsconfig файле. Тогда предупреждения V5608 (SQLI) низкого уровня достоверности не попадут в отчёт. Более подробно про .pvsconfig-файлы можно почитать в документации (раздел "Подавление ложных предупреждений с помощью файлов конфигурации диагностик (.pvsconfig)").
Если же вы, напротив, считаете такие предупреждения крайне важными, то можете повысить их уровень достоверности до высокого, используя комментарий вида //V_LEVEL_1::5608. Подробности приводятся в главе "Как задать свой уровень для конкретной диагностики" в документации.