Introduction
Before delivering a solution to end users, ensuring its security is critical. Two common practices, especially in Hong Kong, are:
- Security Risk Assessment and Audit (SRAA): Evaluates the security risks inherent in the system.
- Privacy Impact Assessment (PIA): Ensures the system complies with privacy regulations and safeguards sensitive data.
While these assessments are crucial, they often require external audits or third-party tools. To smoothen the process and identify vulnerabilities early, developers can step into the realm of penetration testing (pen-testing).
Penetration testing is a proactive approach where developers simulate real-world attacks on their applications. This helps uncover vulnerabilities like injection flaws, weak authentication, and insecure configurations before attackers exploit them.
In this guide, we'll explore:
- Common attacks your application may face.
- Preventive measures to secure your application.
- How to conduct penetration testing yourself using a reusable C# helper class.
What Attacks Could You Face?
Web applications face various types of attacks. Here are the most common ones:
-
Injection Attacks
- Attackers inject malicious input to manipulate queries or commands.
- Examples: SQL Injection, Command Injection.
- Prevention: Use parameterized queries and input validation.
-
Cross-Site Scripting (XSS)
- Injected scripts execute in a user's browser, stealing cookies or manipulating content.
-
Prevention: Encode all output, use a
Content-Security-Policy
(CSP), and validate inputs.
-
Cross-Site Request Forgery (CSRF)
- Tricks users into performing unintended actions while authenticated.
-
Prevention: Use anti-forgery tokens and enforce
SameSite
cookie policies.
-
Insecure Authentication and Session Management
- Weak password policies, session hijacking, or missing
HttpOnly
/Secure
cookies. - Prevention: Enforce strong password policies, secure session cookies, and implement multi-factor authentication (MFA).
- Weak password policies, session hijacking, or missing
-
Security Misconfigurations
- Missing security headers, default credentials, or unnecessary services exposed.
- Prevention: Use security headers, disable unused services, and regularly patch your environment.
Step 1: Build Your Penetration Testing Helper
To automate penetration testing tasks, we'll create a reusable C# utility class. This class will help you:
- Perform reconnaissance to gather information about your app.
- Check for missing security headers.
- Validate cookies for critical flags like
HttpOnly
andSecure
. - Test for CSRF protection.
- Conduct fuzzing to evaluate input validation.
Here's the full code for the PenTestingHelper
class:
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
namespace MyPlaygroundApp.Utils
{
public class PenTestingHelper
{
private readonly HttpClient _httpClient;
public PenTestingHelper(string baseUrl)
{
_httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
}
public async Task RunReconnaissanceAsync()
{
Console.WriteLine("\n[Reconnaissance]");
var endpoints = new[] { "/robots.txt", "/.well-known/security.txt", "/favicon.ico" };
foreach (var ep in endpoints)
{
var res = await _httpClient.GetAsync(ep);
Console.WriteLine($"{ep} -> {(int)res.StatusCode} {res.ReasonPhrase}");
}
}
public async Task CheckSecurityHeadersAsync()
{
Console.WriteLine("\n[Security Header Check]");
var res = await _httpClient.GetAsync("/");
var headers = new[] { "X-Frame-Options", "X-Content-Type-Options", "Content-Security-Policy", "Referrer-Policy" };
foreach (var header in headers)
{
Console.WriteLine($"{header}: {(res.Headers.Contains(header) ? "Present" : "Missing")}");
}
}
public async Task CheckCookieFlagsAsync(string loginUrl)
{
Console.WriteLine("\n[Cookie Flags Validation]");
var res = await _httpClient.GetAsync(loginUrl);
if (res.Headers.TryGetValues("Set-Cookie", out var setCookies))
{
foreach (var cookie in setCookies)
{
Console.WriteLine($"Set-Cookie -> {cookie}");
if (!cookie.Contains("HttpOnly")) Console.WriteLine(" Warning: Cookie missing HttpOnly");
if (!cookie.Contains("Secure")) Console.WriteLine(" Warning: Cookie missing Secure");
}
}
else
{
Console.WriteLine("No cookies found in the response.");
}
}
public async Task TestCsrfProtectionAsync(string endpoint)
{
Console.WriteLine("\n[CSRF Protection Test]");
var res = await _httpClient.PostAsync(endpoint, new StringContent("{\"dummy\":\"x\"}", Encoding.UTF8, "application/json"));
Console.WriteLine($"{endpoint} -> {(int)res.StatusCode} {res.ReasonPhrase}");
}
public async Task FuzzEndpointAsync(string endpoint)
{
Console.WriteLine("\n[Fuzzing Endpoint]");
var payloads = new[]
{
new { name = "normal" },
new { name = "' OR '1'='1" },
new { name = "<script>alert(1)</script>" },
new { name = new string('A', 5000) }
};
foreach (var payload in payloads)
{
var res = await _httpClient.PostAsJsonAsync(endpoint, payload);
Console.WriteLine($"Payload: {JsonSerializer.Serialize(payload)} -> {(int)res.StatusCode} {res.ReasonPhrase}");
}
}
}
}
Step 2: Use the Helper in Your Application
Here's how to use the PenTestingHelper
to test your application:
using MyPlaygroundApp.Utils;
class Program
{
static async Task Main(string[] args)
{
var baseUrl = "https://your-app-url.com";
var helper = new PenTestingHelper(baseUrl);
// Run penetration tests
await helper.RunReconnaissanceAsync();
await helper.CheckSecurityHeadersAsync();
await helper.CheckCookieFlagsAsync("/login");
await helper.TestCsrfProtectionAsync("/bank/transfer");
await helper.FuzzEndpointAsync("/api/test");
}
}
Step 3: Run and Analyze Results
- Run the application:
dotnet run
- Analyze the results to identify vulnerabilities and areas for improvement.
Here's what to expect when running the PenTestingHelper
against a secure application versus a vulnerable one:
Reconnaissance
Endpoint | Expected Response in Secure App | Vulnerable App Response |
---|---|---|
/robots.txt |
200 OK (Minimal or no sensitive paths exposed) |
200 OK (Sensitive paths like /admin exposed) |
/.well-known/security.txt |
200 OK (Contains security contact details) |
404 Not Found (No security.txt file present) |
/favicon.ico |
200 OK or 404 Not Found (Optional) |
404 Not Found (No impact) |
Security Header Check
Header | Secure App | Vulnerable App |
---|---|---|
X-Frame-Options |
Present (e.g., DENY ) |
Missing |
X-Content-Type-Options |
Present (e.g., nosniff ) |
Missing |
Content-Security-Policy |
Present (e.g., default-src 'self' ) |
Missing |
Referrer-Policy |
Present (e.g., no-referrer ) |
Missing |
Cookie Flags Validation
Observation | Secure App | Vulnerable App |
---|---|---|
Cookies Returned |
HttpOnly , Secure , SameSite
|
Missing these flags |
Example Cookie Output | Set-Cookie -> sessionid=xyz; HttpOnly; Secure; SameSite=Strict |
Set-Cookie -> sessionid=xyz |
CSRF Protection Testing
Test | Secure App | Vulnerable App |
---|---|---|
CSRF Test |
403 Forbidden (Reject without token) |
200 OK or 400 Bad Request (No CSRF protection) |
CSRF Test with Token | 200 OK |
400 Bad Request (Incorrect implementation) |
Fuzzing
Payload | Secure App | Vulnerable App |
---|---|---|
Normal Input | 200 OK |
200 OK |
SQL Injection (' OR '1'='1 ) |
400 Bad Request (Rejected) |
500 Internal Server Error (SQL Injection Possible) |
XSS (<script>alert(1)</script> ) |
400 Bad Request (Rejected) |
200 OK (XSS Possible) |
Large Input | 400 Bad Request |
500 Internal Server Error (DoS Possible) |
Step 4: Prevent Vulnerabilities
- Injection Attacks: Use parameterized queries and validate inputs.
-
XSS: Use output encoding and a strong
Content-Security-Policy
. -
CSRF: Implement anti-forgery tokens and enforce
SameSite
cookie policies. -
Cookies: Secure cookies with
HttpOnly
,Secure
, andSameSite
flags. - Security Headers: Ensure all recommended headers are present.
Step 5: Sample Output
Here's an example of the output you'd see when running a secure application:
[Reconnaissance]
/robots.txt -> 200 OK
/.well-known/security.txt -> 404 Not Found
/favicon.ico -> 404 Not Found
[Security Header Check]
X-Frame-Options: Present
X-Content-Type-Options: Present
Content-Security-Policy: Present
Referrer-Policy: Present
[Cookie Flags Validation]
Set-Cookie -> sessionid=xyz; HttpOnly; Secure; SameSite=Strict
[CSRF Protection Test]
/bank/transfer -> 403 Forbidden
[Fuzzing Endpoint]
Payload: {"name":"normal"} -> 200 OK
Payload: {"name":"' OR '1'='1"} -> 400 Bad Request
Payload: {"name":"<script>alert(1)</script>"} -> 400 Bad Request
Payload: {"name":"AAAA..."} -> 400 Bad Request
Additional: How to Add Security Headers Middleware
To further enhance your application's security, you can add middleware in Program.cs
to configure essential security headers. These headers help protect your application against attacks like clickjacking, XSS, and MIME sniffing.
Here's how you can set up the middleware:
// Security headers middleware
app.Use(async (context, next) =>
{
// X-Content-Type-Options: Prevent MIME type sniffing
context.Response.Headers.Append("X-Content-Type-Options", "nosniff");
// X-Frame-Options: Prevent clickjacking
context.Response.Headers.Append("X-Frame-Options", "DENY");
// Referrer-Policy: Control referrer information
context.Response.Headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");
// X-XSS-Protection: Enable XSS protection (legacy browsers)
context.Response.Headers.Append("X-XSS-Protection", "1; mode=block");
// Permissions-Policy: Control browser features
context.Response.Headers.Append("Permissions-Policy",
"camera=(), microphone=(), geolocation=(), payment=(), usb=()");
// Content-Security-Policy: Define allowed resources
var connectSrc = app.Environment.IsDevelopment()
? "'self' ws: wss: http://localhost:* https://localhost:* https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://ajax.googleapis.com https://ajax.aspnetcdn.com"
: "'self' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://ajax.googleapis.com https://ajax.aspnetcdn.com";
var csp = "default-src 'self'; " +
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://ajax.googleapis.com https://ajax.aspnetcdn.com; " +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://ajax.googleapis.com https://ajax.aspnetcdn.com; " +
"font-src 'self' https://fonts.gstatic.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net https://ajax.googleapis.com; " +
"img-src 'self' data: https:; " +
$"connect-src {connectSrc}; " +
"frame-ancestors 'none'; " +
"base-uri 'self'; " +
"form-action 'self'; " +
"upgrade-insecure-requests";
context.Response.Headers.Append("Content-Security-Policy", csp);
// Strict-Transport-Security: Force HTTPS (only in production)
if (!app.Environment.IsDevelopment())
{
context.Response.Headers.Append("Strict-Transport-Security",
"max-age=31536000; includeSubDomains; preload");
}
// Remove server information
context.Response.Headers.Remove("Server");
context.Response.Headers.Remove("X-Powered-By");
await next();
});
Benefits of the Middleware
This middleware provides the following benefits:
- X-Content-Type-Options: Prevents MIME sniffing.
- X-Frame-Options: Blocks clickjacking attacks.
- Referrer-Policy: Limits sensitive information in referrer headers.
- X-XSS-Protection: Enables XSS protection for legacy browsers.
- Permissions-Policy: Restricts the use of unnecessary browser features.
- Content-Security-Policy: Mitigates XSS and injection attacks.
- Strict-Transport-Security: Enforces HTTPS connections.
By adding this middleware, you can ensure that your app adheres to modern security standards.
Conclusion
Penetration testing is an essential step in securing your application. With the PenTestingHelper
, you can quickly identify common vulnerabilities and address them before attackers exploit them.
By incorporating penetration testing into your development workflow, you'll not only improve security but also build trust with your users.
Try it out, and let me know how it works for you!
Reference
Love C#!
Top comments (0)