|
| 1 | +--- |
| 2 | +description: |
| 3 | +globs: **/*.cs, **/*.sln, **/*.csproj |
| 4 | +alwaysApply: false |
| 5 | +--- |
| 6 | +# Secure C#/.NET Development |
| 7 | + |
| 8 | +These rules apply to all C#/.NET code in the repository and aim to prevent common security risks through disciplined use of input validation and deserialization, output encoding, and safe APIs. |
| 9 | + |
| 10 | +All violations must include a clear explanation of which rule was triggered and why, to help developers understand and fix the issue effectively. |
| 11 | +Generated code must not violate these rules. If a rule is violated, a comment must be added explaining the issue and suggesting a correction. |
| 12 | + |
| 13 | +## 1. Validate All external inputs |
| 14 | +- **Rule:** Validate all external inputs by manually checking the type, format and size of the input manually or by using libraries like `FluentValidation`. |
| 15 | +For file validation, utilize MIME Type Validation libraries, like `MimeDetective` or `HeyRed.Mime` to check whether a file's type and content actually matches the expected type. |
| 16 | + |
| 17 | +## 2. Use Parameterized Queries in EntityFramework |
| 18 | +- **Rule:** Parameterize queries in EntityFramework using LINQ and `FromSqlInterpolated`. |
| 19 | +- **Unsafe:** |
| 20 | + ```cs |
| 21 | + // Vulnerable to SQL Injection |
| 22 | + var user = dbContext.Users |
| 23 | + .FromSqlRaw($"SELECT * FROM Users WHERE Username = '{userInput}'") |
| 24 | + .FirstOrDefault(); |
| 25 | + ``` |
| 26 | +- **Safe:** |
| 27 | + ```cs |
| 28 | + //Input is interpolated and parameterized internally |
| 29 | + var user = dbContext.Users |
| 30 | + .FromSqlInterpolated($"SELECT * FROM Users WHERE Username = {userInput}") |
| 31 | + .FirstOrDefault(); |
| 32 | + ``` |
| 33 | +- **Safe:** |
| 34 | + ```cs |
| 35 | + //LINQ is translated into parameterized SQL query |
| 36 | + var user = dbContext.Users |
| 37 | + .Where(u => u.Username == userInput) |
| 38 | + .FirstOrDefault(); |
| 39 | + ``` |
| 40 | + |
| 41 | +## 3. Avoid Singleton Dependency Injection On User-Specific Services |
| 42 | +- **Rule:** When registering services in ASP.NET Dependency Injection, choose the correct service lifetime to avoid exposing unauthorized users to other users' requests. |
| 43 | + |
| 44 | +- **Unsafe:** |
| 45 | + ```cs |
| 46 | + var builder = WebApplication.CreateBuilder(args); |
| 47 | + // One instance is shared for all users - sensitive data is exposed to others |
| 48 | + builder.Services.AddSingleton<UserAuthService>(); |
| 49 | + ``` |
| 50 | +- **Safe:** |
| 51 | + ```cs |
| 52 | + var builder = WebApplication.CreateBuilder(args); |
| 53 | + // Each created instance is scoped for a single request |
| 54 | + builder.Services.AddScoped<UserAuthService>(); |
| 55 | + ``` |
| 56 | + |
| 57 | +## 4. Avoid Handling Mutable Data in Singletons |
| 58 | +- **Rule:** Handling of mutable data in Singleton services should be avoided to prevent data inconsistencies. Ensure thread safety in Singletons to avoid race conditions that can cause logic bypass, for example by escalating privilages in authorization logic. |
| 59 | + |
| 60 | +## 5. Ensure Solution's Project Paths Are Within the Expected Directory Structure |
| 61 | +- **Rule:** Check that referenced projects inside .sln files do not point to suspicious project files outside the expected directory structure. |
| 62 | + |
| 63 | +## 6. Use Secure Deserialization Methods |
| 64 | +- **Rule:** When deserializing data use type-safe methods to avoid malicious code injection. |
| 65 | + |
| 66 | +- **Unsafe:** |
| 67 | + ```cs |
| 68 | + var formatter = new BinaryFormatter(); // No validation of untrusted data |
| 69 | + using var ms = new MemoryStream(data); |
| 70 | + return formatter.Deserialize(ms); |
| 71 | + ``` |
| 72 | +- **Safe:** |
| 73 | + ```cs |
| 74 | + var options = new JsonSerializerOptions |
| 75 | + { |
| 76 | + PropertyNameCaseInsensitive = true |
| 77 | + }; |
| 78 | + return JsonSerializer.Deserialize<T>(json, options); // Type-safe |
| 79 | + ``` |
| 80 | + |
| 81 | +## 7. Validate and Normalize File Paths |
| 82 | +- **Rule:** To prevent file path manipulations, normalize and validate input file paths to prevent access to sensitive files. |
| 83 | + |
| 84 | +- **Unsafe:** |
| 85 | + ```cs |
| 86 | + string basePath = "/home/files/"; |
| 87 | + // Dangerous - filename can contain "../../etc/passwd" |
| 88 | + string fullPath = Path.Combine(basePath, filename); |
| 89 | + string content = System.IO.File.ReadAllText(fullPath); |
| 90 | + ``` |
| 91 | +- **Safe:** |
| 92 | + ```cs |
| 93 | + string basePath = "/home/files/"; |
| 94 | + // Absolute path is resolved and normalized |
| 95 | + string fullPath = Path.GetFullPath(Path.Combine(basePath, filename)); |
| 96 | + // Ensure the resolved path starts with the base path |
| 97 | + if (!fullPath.StartsWith(basePath, StringComparison.Ordinal)) |
| 98 | + { |
| 99 | + return BadRequest("Invalid file path."); |
| 100 | + } |
| 101 | + if (!System.IO.File.Exists(fullPath)) |
| 102 | + { |
| 103 | + return NotFound(); |
| 104 | + } |
| 105 | + string content = System.IO.File.ReadAllText(fullPath); |
| 106 | + ``` |
0 commit comments