Security of C# applications: top 10 vulnerabilities every developer should know
Development in C# and the .NET platform is a powerful tool for creating enterprise and web applications. However, even the most reliable code can contain holes that lead to data leaks, loss of reputation, and financial losses. In this article, we will analyze the 10 most common vulnerabilities of C# applications based on OWASP Top 10, with practical examples and methods of protection.
1. SQL Injection
This is one of the oldest, but still relevant threats. The vulnerability occurs when user input is directly concatenated into an SQL query.
Vulnerable code:
string userName = Request.QueryString["name"];string query = "SELECT * FROM Users WHERE Name = '" + userName + "'";SqlCommand cmd = new SqlCommand(query, connection);Secure code (parameterized queries):
string userName = Request.QueryString["name"];string query = "SELECT * FROM Users WHERE Name = @name";SqlCommand cmd = new SqlCommand(query, connection);cmd.Parameters.AddWithValue("@name", userName);Recommendation: Always use parameterized queries or Entity Framework, which automatically escapes input.
2. Cross-Site Scripting (XSS)
XSS attacks allow an attacker to inject a malicious script into a web page. In C#, this often occurs when outputting unchecked data via @Html.Raw().
Vulnerable code:
@Html.Raw(Model.Comment)Secure code (output encoding):
@Html.Encode(Model.Comment)Recommendation: Use Razor syntax @Model.Comment (it automatically encodes HTML) and never use @Html.Raw() without validation.
3. Cross-Site Request Forgery (CSRF)
CSRF forces an authenticated user to perform an unwanted action on the site (e.g., money transfer). In ASP.NET Core, protection is built-in, but it needs to be properly enabled.
Vulnerable code (missing anti-forgery token):
<form method="post" action="/Account/Delete"> <button type="submit">Delete account</button></form>Secure code (with AntiForgeryToken):
<form method="post" action="/Account/Delete"> @Html.AntiForgeryToken() <button type="submit">Delete account</button></form>Recommendation: Add the [ValidateAntiForgeryToken] attribute to your POST methods and use @Html.AntiForgeryToken() in forms.
4. Insecure Password Storage
Storing passwords in plain text or using weak hashes (MD5, SHA1) is a serious mistake.
Vulnerable code:
string passwordHash = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(password));Secure code (using bcrypt/Argon2):
using BCrypt.Net;string passwordHash = BCrypt.HashPassword(password, workFactor: 12);Recommendation: Use libraries BCrypt.Net or Microsoft.AspNetCore.Identity, which automatically generate a salt and use modern algorithms.
5. Exposure of Sensitive Data in Exceptions
Displaying the full error stack to the user can reveal the application structure, connection strings, and file paths.
Vulnerable code (in production):
catch (Exception ex){ return Content(ex.ToString());}Secure code:
catch (Exception ex){ _logger.LogError(ex, "Error processing request"); return StatusCode(500, "Internal server error");}Recommend