Skip to main content

Task 26: Implement HTTPS-Only Cookies

Role​

DevOps

Overview​

Configure secure cookie settings for JWT tokens and session management, ensuring all authentication cookies are transmitted only over HTTPS and protected from common web vulnerabilities.

Objectives​

  • Enable HTTPS-only cookie transmission
  • Set secure cookie attributes (HttpOnly, Secure, SameSite)
  • Configure cookie domains and paths correctly
  • Prevent cookie theft and CSRF attacks
  • Ensure cookies work correctly across environments
AttributeValuePurpose
SecuretrueOnly transmit over HTTPS
HttpOnlytruePrevent JavaScript access (XSS protection)
SameSiteStrict or LaxCSRF protection
Domain.micdots.comAllow subdomain access
Path/Available site-wide
MaxAge3600 (1 hour)Token expiration
public class SecureCookieOptions
{
public string Name { get; set; } = "micdots_auth";
public bool HttpOnly { get; set; } = true;
public bool Secure { get; set; } = true;
public SameSiteMode SameSite { get; set; } = SameSiteMode.Strict;
public string Domain { get; set; } = ".micdots.com";
public string Path { get; set; } = "/";
public int MaxAgeSeconds { get; set; } = 3600;
}

Implementation​

// Startup.cs or Program.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => false; // No consent banner for auth cookies
options.MinimumSameSitePolicy = SameSiteMode.Strict;
options.HttpOnly = HttpOnlyPolicy.Always;
options.Secure = CookieSecurePolicy.Always; // Require HTTPS
});

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "micdots_auth";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.Domain = ".micdots.com";
options.Cookie.Path = "/";
options.ExpireTimeSpan = TimeSpan.FromHours(1);
options.SlidingExpiration = true;
});
}

public void Configure(IApplicationBuilder app)
{
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
}

2. Set Cookies in Authentication Response​

[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginRequest request)
{
var result = await _authService.AuthenticateAsync(request.Email, request.Password);

if (!result.Success)
{
return Unauthorized();
}

// Set access token cookie
Response.Cookies.Append("micdots_auth", result.AccessToken, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Domain = ".micdots.com",
Path = "/",
MaxAge = TimeSpan.FromHours(1),
IsEssential = true // Exempt from GDPR consent
});

// Set refresh token cookie
Response.Cookies.Append("micdots_refresh", result.RefreshToken, new CookieOptions
{
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Strict,
Domain = ".micdots.com",
Path = "/api/v1/auth/refresh", // Only accessible to refresh endpoint
MaxAge = TimeSpan.FromDays(7)
});

return Ok(new { success = true, user = result.User });
}

3. Environment-Specific Configuration​

// appsettings.Development.json
{
"CookieSettings": {
"Secure": false, // Allow HTTP in development
"Domain": "localhost",
"SameSite": "Lax"
}
}

// appsettings.Production.json
{
"CookieSettings": {
"Secure": true, // Require HTTPS in production
"Domain": ".micdots.com",
"SameSite": "Strict"
}
}
// Load from configuration
services.Configure<SecureCookieOptions>(Configuration.GetSection("CookieSettings"));

Infrastructure Configuration​

1. Enable HTTPS in Web Server​

Nginx Configuration​

server {
listen 443 ssl http2;
server_name micdots.com www.micdots.com;

ssl_certificate /etc/letsencrypt/live/micdots.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/micdots.com/privkey.pem;

# SSL Configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers HIGH:!aNULL:!MD5;

# HSTS Header
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# Proxy to application
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;
}
}

# Redirect HTTP to HTTPS
server {
listen 80;
server_name micdots.com www.micdots.com;
return 301 https://$server_name$request_uri;
}

Docker Compose Configuration​

version: '3.8'

services:
app:
image: micdots-app:latest
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=https://+:443;http://+:80
- ASPNETCORE_Kestrel__Certificates__Default__Path=/app/certs/cert.pfx
- ASPNETCORE_Kestrel__Certificates__Default__Password=${CERT_PASSWORD}
ports:
- "443:443"
- "80:80"
volumes:
- ./certs:/app/certs

2. SSL Certificate Setup (Let's Encrypt)​

# Install Certbot
sudo apt-get update
sudo apt-get install certbot python3-certbot-nginx

# Obtain SSL certificate
sudo certbot --nginx -d micdots.com -d www.micdots.com

# Auto-renewal (cron job)
sudo crontab -e
0 0 * * * certbot renew --quiet --post-hook "systemctl reload nginx"

3. Test HTTPS Configuration​

# Test SSL/TLS configuration
curl -I https://micdots.com

# Check certificate validity
openssl s_client -connect micdots.com:443 -servername micdots.com

# Verify HSTS header
curl -I https://micdots.com | grep Strict-Transport-Security

# Test cookie attributes
curl -v -c cookies.txt https://micdots.com/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"client01@micdots.com","password":"Mic1234!"}'

# View cookie file
cat cookies.txt

Security Verification Checklist​

  • Cookies are only sent over HTTPS (Secure flag)
  • Cookies are not accessible via JavaScript (HttpOnly flag)
  • CSRF protection enabled (SameSite=Strict)
  • Cookie domain is correctly set
  • Cookie path is restrictive where appropriate
  • MaxAge/Expires is set appropriately
  • Sensitive cookies use different expiration times

HTTPS Configuration​

  • Valid SSL certificate installed
  • Certificate auto-renewal configured
  • HTTP automatically redirects to HTTPS
  • HSTS header is set
  • TLS 1.2+ only (no TLS 1.0/1.1)
  • Strong cipher suites configured
  • SSL Labs test scores A or A+

Testing​

  • Cookies work in production environment
  • Cookies work across subdomains (if needed)
  • Browser console shows no cookie warnings
  • Developer tools show correct cookie attributes
  • Cross-browser testing completed
  • Mobile browser testing completed

Acceptance Criteria​

  • All cookies use Secure flag in production
  • All cookies use HttpOnly flag
  • SameSite attribute set to Strict or Lax
  • HTTPS enforced on all environments (except local dev)
  • Valid SSL certificate installed
  • HSTS header configured
  • HTTP→HTTPS redirect working
  • Cookies persist across page refreshes
  • Cookies expire correctly
  • Cookie domain configuration correct
  • Works in all major browsers
  • Documentation updated
  • Configuration externalized
  • Environment-specific settings work

Testing Checklist​

Manual Testing​

  • Login and verify cookie is set with correct attributes
  • Check cookie in browser DevTools
  • Verify cookie is not accessible via JavaScript console
  • Test logout clears cookies
  • Test cookie expiration
  • Test on different browsers (Chrome, Firefox, Safari, Edge)
  • Test on mobile devices

Security Testing​

  • Run SSL Labs test (https://www.ssllabs.com/ssltest/)
  • Verify no mixed content warnings
  • Test CSRF protection
  • Attempt XSS cookie theft (should fail)
  • Test cookie replay attacks

Automated Testing​

# Security headers test
curl -I https://micdots.com | grep -E 'Strict-Transport-Security|X-Frame-Options|X-Content-Type-Options'

# Cookie security test
curl -v https://micdots.com/api/v1/auth/login 2>&1 | grep -E 'Set-Cookie.*Secure|HttpOnly|SameSite'

Estimated Time​

1 day (8 hours)

Dependencies​

  • Valid SSL certificate
  • Domain DNS configured
  • Load balancer/reverse proxy configured
  • Task 18: Auth endpoint implemented

External Resources​