Безопасность C# приложений: топ-10 уязвимостей для начинающих
Разработка безопасных приложений на C# — это не просто тренд, а необходимость. Каждый день в мире обнаруживаются тысячи уязвимостей, и ваше приложение может стать следующей мишенью. В этой статье мы разберем 10 самых распространенных уязвимостей в C# (.NET) приложениях, с которыми сталкиваются начинающие разработчики. Вы узнаете, как их обнаружить и, главное, как защитить свой код.
1. SQL-инъекции (SQL Injection)
Самая старая и до сих пор опасная уязвимость. Она возникает, когда пользовательский ввод напрямую подставляется в SQL-запрос.
Уязвимый код:
string userName = Request["username"];
string query = "SELECT * FROM Users WHERE Name = '" + userName + "'";
SqlCommand cmd = new SqlCommand(query, connection);
Защита: Используйте параметризованные запросы или Entity Framework.
string userName = Request["username"];
string query = "SELECT * FROM Users WHERE Name = @name";
SqlCommand cmd = new SqlCommand(query, connection);
cmd.Parameters.AddWithValue("@name", userName);
2. Межсайтовый скриптинг (XSS)
XSS-атаки позволяют злоумышленнику внедрить вредоносный скрипт на страницу, которая отображается другим пользователям.
Уязвимый код:
string comment = Request["comment"];
Response.Write("<div>" + comment + "</div>");
Защита: Всегда кодируйте вывод. Используйте HttpUtility.HtmlEncode():
string comment = Request["comment"];
Response.Write("<div>" + HttpUtility.HtmlEncode(comment) + "</div>");
3. Отсутствие аутентификации и авторизации
Многие новички забывают проверять права доступа к ресурсам. Например, пользователь может получить доступ к чужому профилю, просто изменив ID в URL.
Уязвимый код:
public IActionResult Profile(int userId)
{
var user = _context.Users.Find(userId);
return View(user);
}
Защита: Всегда проверяйте, что текущий пользователь имеет право на запрошенный ресурс:
public IActionResult Profile(int userId)
{
var currentUserId = User.FindFirst(ClaimTypes.NameIdentifier).Value;
if (userId != currentUserId) return Forbid();
var user = _context.Users.Find(userId);
return View(user);
}
4. Утечка конфиденциальных данных через исключения
Разработчики часто выводят полную информацию об ошибках, включая стек вызовов и строки подключения.
Уязвимый код:
try
{
// код
}
catch (Exception ex)
{
Response.Write(ex.ToString()); // ОПАСНО!
}
Защита: В продакшене используйте общие сообщения об ошибках, а детали логируйте в безопасное место:
try
{
// код
}
catch (Exception ex)
{
_logger.LogError(ex, "Ошибка при обработке запроса");
return StatusCode(500, "Произошла внутренняя ошибка сервера");
}
5. Небезопасное хранение паролей
Хранение паролей в открытом виде или с использованием слабых хэшей (MD5, SHA1) — грубая ошибка.
Уязвимый код:
string passwordHash = MD5.Hash(password); // НЕЛЬЗЯ!
Защита: Используйте современные алгоритмы хэширования с солью, например, BCrypt или ASP.NET Core Identity:
var passwordHasher = new PasswordHasher<IdentityUser>();
string passwordHash = passwordHasher.HashPassword(user, password);
6. Недостаточная защита от CSRF (Cross-Site Request Forgery)
Атака, при которой злоумышленник заставляет браузер жертвы выполнить нежелательное действие на другом сайте, где жертва аутентифицирована.
Защита: В ASP.NET Core используйте антифоржные токены:
// В представлении
@Html.AntiForgeryToken()
// В контроллере
[ValidateAntiForgeryToken]
public IActionResult DeleteAccount() { ... }
7. Небезопасная десериализация
При десериализации данных из ненадёжного источника злоумышленник может выполнить произвольный код.
Уязвимый код:
var serializer = new BinaryFormatter();
using (var stream = new MemoryStream(data))
{
var obj = serializer.Deserialize(stream); // ОПАСНО!
}
Защита: Избегайте BinaryFormatter, JavaScriptSerializer и LosFormatter. Используйте System.Text.Json с явной моделью:
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var user = JsonSerializer.Deserialize<UserDto>(json, options);
8. Небезопасное управление сессиями
Использование предсказуемых или незащищённых идентификаторов сессий, а также их хранение в URL.
Проблема: Сессионный ключ в URL легко перехватить или предугадать.
Защита:
- Храните идентификатор сессии только в cookies с флагами
HttpOnlyиSecure. - Используйте случайные, длинные идентификаторы.
- Настройте тайм-аут сессии.
services.AddSession(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.IdleTimeout = TimeSpan.FromMinutes(20);
});
9. Отсутствие проверки входных данных
Пользователь может отправить неожиданные данные: слишком длинные строки, отрицательные числа, специальные символы.
Защита: Используйте аннотации данных и валидацию на стороне сервера:
public class UserModel