Безопасность C# приложений: топ-10 уязвимостей на практике
Безопасность — одна из важнейших составляющих разработки современных приложений на C# и .NET. Даже небольшой пропущенный баг может привести к утечке данных или полной компрометации системы. В этой статье мы разберём 10 самых распространённых уязвимостей, с которыми сталкиваются разработчики C#, и покажем, как их избежать на практике.
1. SQL-инъекции (SQL Injection)
Самая старая и опасная уязвимость. Возникает, когда пользовательский ввод напрямую подставляется в SQL-запрос.
Уязвимый код:
string login = txtLogin.Text;
string query = "SELECT * FROM Users WHERE Login = '" + login + "'";
SqlCommand cmd = new SqlCommand(query, connection);
Безопасный код (параметризованные запросы):
string login = txtLogin.Text;
string query = "SELECT * FROM Users WHERE Login = @login";
SqlCommand cmd = new SqlCommand(query, connection);
cmd.Parameters.AddWithValue("@login", login);
Дополнительная защита: используйте ORM (Entity Framework) — он автоматически параметризует запросы.
2. Межсайтовый скриптинг (XSS)
Злоумышленник внедряет вредоносный JavaScript-код на страницу через пользовательский ввод.
Уязвимый код:
string comment = Request.Form["comment"];
Response.Write("<div>" + comment + "</div>");
Безопасный код (кодирование вывода):
string comment = Request.Form["comment"];
Response.Write("<div>" + HttpUtility.HtmlEncode(comment) + "</div>");
В ASP.NET Core используйте @Html.Encode() или Razor-синтаксис с автоматическим кодированием.
3. Подделка межсайтовых запросов (CSRF)
Атака, при которой злоумышленник заставляет пользователя выполнить нежелательное действие на сайте, где он авторизован.
Уязвимый код (отсутствие защиты):
[HttpPost]
public IActionResult DeleteAccount(int userId)
{
// Удаление аккаунта без проверки токена
return Ok();
}
Защита с помощью антифордж-токенов в ASP.NET Core:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult DeleteAccount(int userId)
{
// Безопасное удаление
return Ok();
}
На клиенте токен передаётся через скрытое поле формы или заголовок.
4. Небезопасная десериализация
При десериализации данных из ненадёжного источника злоумышленник может выполнить произвольный код.
Уязвимый код (BinaryFormatter):
BinaryFormatter formatter = new BinaryFormatter();
using (FileStream fs = new FileStream("data.bin", FileMode.Open))
{
var obj = formatter.Deserialize(fs); // Опасно!
}
Безопасные альтернативы:
- Используйте
System.Text.JsonилиNewtonsoft.Jsonс ограничением типов. - Включите
TypeNameHandling.None. - Избегайте
BinaryFormatter,SoapFormatterиLosFormatter.
5. Недостатки аутентификации и управления сессиями
Слабая аутентификация, хранение паролей в открытом виде, предсказуемые токены сессий.
Ошибки:
- Хранение пароля в открытом виде:
User.Password = txtPassword.Text; - Собственная реализация хеширования без соли.
- Использование
Guid.NewGuid()для генерации токенов — слишком предсказуемо.
Правильный подход:
- Используйте
ASP.NET Core Identity— встроенное решение. - Хешируйте пароли с помощью
BCrypt.NetилиPBKDF2. - Токены сессий генерируйте через
RandomNumberGenerator.
6. Неправильная обработка ошибок и раскрытие информации
Вывод стека вызовов, строк подключения или путей к файлам в ответе сервера.
Уязвимый код:
try
{
// код
}
catch (Exception ex)
{
Response.Write(ex.ToString()); // Опасно!
}
Безопасный код:
try
{
// код
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка при обработке");
return StatusCode(500, "Внутренняя ошибка сервера");
}
Настройте UseDeveloperExceptionPage только для разработки, в продакшене используйте UseExceptionHandler.
7. Недостатки контроля доступа
Пользователь может получить доступ к данным или функциям, на которые у него нет прав.
Уязвимый код:
[HttpGet("{id}")]
public IActionResult GetOrder(int id)
{
var order = _context.Orders.Find(id);
return Ok(order); // Нет проверки, принадлежит ли заказ текущему пользователю
}
Безопасный код:
[HttpGet("{id}")]
public IActionResult GetOrder(int id)
{
var userId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
var order = _context.Orders
.FirstOrDefault(o => o.Id == id && o.UserId == userId);
if (order == null) return Forbid();
return Ok(order);
}
8. Небезопасное хранение конфиденциальных данных
Хранение строк подключения, API-ключей или паролей в коде или конфигурационных файлах без шифрования.
Ошибка:
<connectionStrings>
<add name="MyDb" connectionString="Server