Task 30: Add Security Headers
Role
DevOpsOverview
Configure HTTP security headers to protect the MicDots application from common web vulnerabilities including XSS, clickjacking, MIME-sniffing, and other attacks. Implement OWASP-recommended security headers across all environments.
Objectives
- Protect against Cross-Site Scripting (XSS) attacks
- Prevent clickjacking attacks
- Disable MIME-type sniffing
- Enforce HTTPS connections (HSTS)
- Control resource loading (CSP)
- Implement referrer policy
- Add permissions policy
Required Security Headers
Header Configuration
| Header | Value | Purpose |
|---|---|---|
| Strict-Transport-Security | max-age=31536000; includeSubDomains; preload | Force HTTPS for 1 year |
| X-Frame-Options | DENY | Prevent clickjacking |
| X-Content-Type-Options | nosniff | Prevent MIME-sniffing |
| X-XSS-Protection | 1; mode=block | Enable browser XSS filter (legacy) |
| Content-Security-Policy | See detailed policy below | Control resource loading |
| Referrer-Policy | strict-origin-when-cross-origin | Control referrer information |
| Permissions-Policy | See detailed policy below | Control browser features |
Content Security Policy (CSP)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self' https://api.micdots.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
Permissions Policy
Permissions-Policy:
geolocation=(),
microphone=(),
camera=(),
payment=(),
usb=(),
interest-cohort=()
Implementation
1. ASP.NET Core Middleware
public class SecurityHeadersMiddleware
{
private readonly RequestDelegate _next;
public SecurityHeadersMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// HSTS - Strict Transport Security
context.Response.Headers.Add(
"Strict-Transport-Security",
"max-age=31536000; includeSubDomains; preload"
);
// X-Frame-Options - Prevent clickjacking
context.Response.Headers.Add("X-Frame-Options", "DENY");
// X-Content-Type-Options - Prevent MIME-sniffing
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
// X-XSS-Protection (legacy browsers)
context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
// Content Security Policy
context.Response.Headers.Add(
"Content-Security-Policy",
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; " +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
"font-src 'self' https://fonts.gstatic.com; " +
"img-src 'self' data: https:; " +
"connect-src 'self' https://api.micdots.com; " +
"frame-ancestors 'none'; " +
"base-uri 'self'; " +
"form-action 'self'; " +
"upgrade-insecure-requests;"
);
// Referrer Policy
context.Response.Headers.Add(
"Referrer-Policy",
"strict-origin-when-cross-origin"
);
// Permissions Policy
context.Response.Headers.Add(
"Permissions-Policy",
"geolocation=(), microphone=(), camera=(), payment=(), usb=(), interest-cohort=()"
);
// Remove server info headers
context.Response.Headers.Remove("Server");
context.Response.Headers.Remove("X-Powered-By");
context.Response.Headers.Remove("X-AspNet-Version");
context.Response.Headers.Remove("X-AspNetMvc-Version");
await _next(context);
}
}
2. Register Middleware
// Program.cs or Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<SecurityHeadersMiddleware>();
// Other middleware
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
3. Alternative: Using NWebsec Package
dotnet add package NWebsec.AspNetCore.Middleware
public void Configure(IApplicationBuilder app)
{
// HSTS
app.UseHsts(options => options
.MaxAge(days: 365)
.IncludeSubdomains()
.Preload()
);
// X-Frame-Options
app.UseXfo(options => options.Deny());
// X-Content-Type-Options
app.UseXContentTypeOptions();
// X-XSS-Protection
app.UseXXssProtection(options => options.EnabledWithBlockMode());
// Content Security Policy
app.UseCsp(options => options
.DefaultSources(s => s.Self())
.ScriptSources(s => s
.Self()
.UnsafeInline()
.UnsafeEval()
.CustomSources("https://cdn.jsdelivr.net")
)
.StyleSources(s => s
.Self()
.UnsafeInline()
.CustomSources("https://fonts.googleapis.com")
)
.FontSources(s => s
.Self()
.CustomSources("https://fonts.gstatic.com")
)
.ImageSources(s => s
.Self()
.Data()
.CustomSources("https:")
)
.ConnectSources(s => s
.Self()
.CustomSources("https://api.micdots.com")
)
.FrameAncestors(s => s.None())
.BaseUris(s => s.Self())
.FormActions(s => s.Self())
.UpgradeInsecureRequests()
);
// Referrer Policy
app.UseReferrerPolicy(options => options.StrictOriginWhenCrossOrigin());
// Remove server headers
app.Use(async (context, next) =>
{
context.Response.Headers.Remove("Server");
context.Response.Headers.Remove("X-Powered-By");
await next();
});
}
Nginx Configuration
server {
listen 443 ssl http2;
server_name micdots.com;
# SSL configuration
ssl_certificate /etc/letsencrypt/live/micdots.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/micdots.com/privkey.pem;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), interest-cohort=()" always;
# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.micdots.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;" always;
# Remove server tokens
server_tokens off;
more_clear_headers Server;
more_clear_headers X-Powered-By;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Don't pass security headers from upstream
proxy_hide_header X-Powered-By;
proxy_hide_header Server;
}
}
Environment-Specific Configuration
Development
{
"SecurityHeaders": {
"Enabled": true,
"ContentSecurityPolicy": {
"ReportOnly": true,
"AllowUnsafeInline": true,
"AllowUnsafeEval": true
}
}
}
Production
{
"SecurityHeaders": {
"Enabled": true,
"ContentSecurityPolicy": {
"ReportOnly": false,
"AllowUnsafeInline": false,
"AllowUnsafeEval": false
},
"HSTS": {
"MaxAge": 31536000,
"IncludeSubDomains": true,
"Preload": true
}
}
}
Testing & Validation
1. Online Security Header Scanners
# SecurityHeaders.com
https://securityheaders.com/?q=micdots.com
# Mozilla Observatory
https://observatory.mozilla.org/analyze/micdots.com
# SSL Labs
https://www.ssllabs.com/ssltest/analyze.html?d=micdots.com
2. Manual Testing with curl
# Check all security headers
curl -I https://micdots.com
# Check specific header
curl -I https://micdots.com | grep Strict-Transport-Security
# Verbose output
curl -v https://micdots.com 2>&1 | grep -E '<|>' | grep -E 'Strict-Transport-Security|X-Frame-Options|Content-Security-Policy'
3. Browser DevTools
// Check CSP violations in console
window.addEventListener('securitypolicyviolation', (e) => {
console.log('CSP Violation:', e.blockedURI, e.violatedDirective);
});
4. Automated Testing Script
#!/bin/bash
URL="https://micdots.com"
echo "Testing Security Headers for $URL"
echo "=================================="
headers=(
"Strict-Transport-Security"
"X-Frame-Options"
"X-Content-Type-Options"
"X-XSS-Protection"
"Content-Security-Policy"
"Referrer-Policy"
"Permissions-Policy"
)
for header in "${headers[@]}"; do
value=$(curl -s -I "$URL" | grep -i "^$header:" | cut -d' ' -f2-)
if [ -n "$value" ]; then
echo "✓ $header: $value"
else
echo "✗ $header: MISSING"
fi
done
CSP Reporting
Set up CSP Report Endpoint
[ApiController]
[Route("api/v1/csp")]
public class CspReportController : ControllerBase
{
private readonly ILogger<CspReportController> _logger;
public CspReportController(ILogger<CspReportController> logger)
{
_logger = logger;
}
[HttpPost("report")]
public IActionResult Report([FromBody] CspReportRequest report)
{
_logger.LogWarning(
"CSP Violation: {DocumentUri}, {BlockedUri}, {ViolatedDirective}",
report.CspReport.DocumentUri,
report.CspReport.BlockedUri,
report.CspReport.ViolatedDirective
);
return NoContent();
}
}
Update CSP Header to Include Reporting
Content-Security-Policy: ...existing-policy...; report-uri /api/v1/csp/report
Acceptance Criteria
- All required security headers configured
- HSTS header includes preload directive
- CSP policy blocks unauthorized resources
- X-Frame-Options prevents embedding
- Server identification headers removed
- Headers work in all environments
- SecurityHeaders.com score: A+
- Mozilla Observatory score: A+
- SSL Labs score: A or A+
- No CSP violations in browser console
- Documentation updated
- Configuration externalized
- Monitoring/alerting for violations set up
Testing Checklist
Manual Testing
- Run SecurityHeaders.com scan
- Run Mozilla Observatory scan
- Check headers in browser DevTools
- Test CSP with inline scripts (should block)
- Test iframe embedding (should be blocked)
- Verify HSTS redirects HTTP to HTTPS
- Test in multiple browsers
Automated Testing
- Unit tests for middleware
- Integration tests verify headers present
- E2E tests check security behavior
- Script to check headers on deploy
Estimated Time
6 hours
Dependencies
- Task 26: HTTPS configuration must be complete
- SSL certificate installed
- Web server/reverse proxy configured
Related Content
Related Tasks
- Task 26: Implement HTTPS-Only Cookies
- Task 27: Add CSRF Protection (Coming Soon)
- Task 31: Implement Input Sanitization (Coming Soon)