Skip to content

Commit 39700e6

Browse files
authored
Merge pull request #7 from nimK1987/main
2 parents aa7f69a + ce4d2ef commit 39700e6

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

secure-dev-c-sharp.mdc

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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

Comments
 (0)