Home/Blog/Cybersecurity/TLS Configuration Hardening: Cipher Suites, Protocols, and Security Headers
Cybersecurity

TLS Configuration Hardening: Cipher Suites, Protocols, and Security Headers

Harden your TLS configuration with secure cipher suites, protocol selection, and security headers. Covers Nginx, Apache, and HAProxy with testing and verification.

By Inventive HQ Team
TLS Configuration Hardening: Cipher Suites, Protocols, and Security Headers

A properly hardened TLS configuration protects against protocol downgrade attacks, weak cipher exploitation, and man-in-the-middle attacks. This guide covers protocol selection, cipher suites, and security headers for production deployments.

TLS Configuration Overview

┌─────────────────────────────────────────────────────────────────────────┐
│                    TLS SECURITY LAYERS                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  Layer 4: Security Headers                                              │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ HSTS │ CSP │ X-Frame-Options │ Referrer-Policy │ Permissions   │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  Layer 3: Certificate Configuration                                      │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ ECDSA/RSA │ Key Size │ Chain │ OCSP Stapling │ CT Compliance   │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  Layer 2: Cipher Suites                                                 │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ Key Exchange │ Authentication │ Encryption │ MAC/Hash          │    │
│  │ ECDHE        │ ECDSA/RSA      │ AES-GCM    │ SHA-384           │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  Layer 1: Protocol Version                                              │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │ TLS 1.3 (preferred) │ TLS 1.2 (fallback) │ ❌ TLS 1.1/1.0/SSL │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

Protocol Version Selection

ProtocolStatusSupport
TLS 1.3✅ RecommendedRequired for A+ grade
TLS 1.2✅ AcceptableCompatibility fallback
TLS 1.1❌ DeprecatedDisabled by browsers
TLS 1.0❌ DeprecatedPCI DSS prohibited
SSL 3.0❌ InsecurePOODLE vulnerable
SSL 2.0❌ InsecureMultiple vulnerabilities

Cipher Suite Selection

TLS 1.3 Cipher Suites (All Secure)

TLS_AES_256_GCM_SHA384         # Strongest, slightly slower
TLS_CHACHA20_POLY1305_SHA256   # Fastest on mobile (no AES-NI)
TLS_AES_128_GCM_SHA256         # Good balance of security/performance

TLS 1.2 Cipher Suites (Curated)

# ECDSA Certificate Ciphers (preferred)
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES128-GCM-SHA256

# RSA Certificate Ciphers (fallback)
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-RSA-AES128-GCM-SHA256

Cipher Suite Components

┌─────────────────────────────────────────────────────────────────────────┐
│           ECDHE - ECDSA - AES256 - GCM - SHA384                         │
│             │       │       │       │      │                            │
│             │       │       │       │      └─ Hash: SHA-384             │
│             │       │       │       └──────── Mode: Galois/Counter      │
│             │       │       └──────────────── Cipher: AES 256-bit       │
│             │       └──────────────────────── Auth: ECDSA signature     │
│             └──────────────────────────────── Key Exchange: Ephemeral EC│
│                                                                          │
│  SECURE CHOICES:                                                         │
│  Key Exchange: ECDHE (forward secrecy)                                  │
│  Authentication: ECDSA (fast), RSA (compatible)                         │
│  Encryption: AES-GCM, ChaCha20-Poly1305 (AEAD modes)                    │
│  Hash: SHA-256, SHA-384 (SHA-2 family)                                  │
│                                                                          │
│  AVOID:                                                                  │
│  Key Exchange: RSA (no forward secrecy), DHE (slow)                     │
│  Encryption: CBC mode (BEAST, POODLE), 3DES (weak), RC4 (broken)        │
│  Hash: MD5 (broken), SHA-1 (deprecated)                                 │
└─────────────────────────────────────────────────────────────────────────┘

Nginx Configuration

Modern Configuration (A+ Grade)

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com;

    # Certificates
    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    # Protocol versions - TLS 1.2 and 1.3 only
    ssl_protocols TLSv1.2 TLSv1.3;

    # Cipher suites - TLS 1.3 ciphers are configured automatically
    # TLS 1.2 ciphers ordered by preference
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers on;

    # ECDH curve
    ssl_ecdh_curve X25519:secp384r1;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/ssl/fullchain.pem;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    # Session configuration
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;  # Disable for better forward secrecy

    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "DENY" 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=()" always;

    # ... rest of configuration
}

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

Dual Certificate Configuration (ECDSA + RSA)

server {
    listen 443 ssl http2;
    server_name example.com;

    # ECDSA certificate (preferred)
    ssl_certificate /etc/nginx/ssl/ecdsa-fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/ecdsa-privkey.pem;

    # RSA certificate (fallback for older clients)
    ssl_certificate /etc/nginx/ssl/rsa-fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/rsa-privkey.pem;

    # Nginx automatically selects based on client capability
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
    ssl_prefer_server_ciphers on;
}

Apache Configuration

Modern Configuration (Apache 2.4+)

# Global SSL configuration
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite TLSv1.3 TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
SSLHonorCipherOrder on
SSLCompression off
SSLSessionTickets off

# OCSP Stapling
SSLStaplingCache shmcb:/var/run/apache2/ocsp(128000)
SSLUseStapling On
SSLStaplingResponderTimeout 5
SSLStaplingReturnResponderErrors Off

<VirtualHost *:443>
    ServerName example.com

    SSLEngine on
    SSLCertificateFile /etc/apache2/ssl/server.crt
    SSLCertificateKeyFile /etc/apache2/ssl/server.key
    SSLCertificateChainFile /etc/apache2/ssl/chain.crt

    # Security headers
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-Frame-Options "DENY"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

    # ... rest of configuration
</VirtualHost>

# Redirect HTTP to HTTPS
<VirtualHost *:80>
    ServerName example.com
    Redirect permanent / https://example.com/
</VirtualHost>

HAProxy Configuration

global
    # SSL/TLS defaults
    ssl-default-bind-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
    ssl-default-bind-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

    ssl-default-server-ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
    ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets

    tune.ssl.default-dh-param 2048

frontend https
    bind *:443 ssl crt /etc/haproxy/certs/combined.pem alpn h2,http/1.1

    # HSTS header
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # Security headers
    http-response set-header X-Content-Type-Options "nosniff"
    http-response set-header X-Frame-Options "DENY"
    http-response set-header Referrer-Policy "strict-origin-when-cross-origin"

    default_backend webservers

frontend http
    bind *:80
    redirect scheme https code 301

backend webservers
    server web1 10.0.0.1:8080 check

Security Headers Deep Dive

HSTS (HTTP Strict Transport Security)

# Start with short max-age for testing
add_header Strict-Transport-Security "max-age=300" always;

# After testing, increase to 1 year with subdomains
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# For HSTS preload list submission (permanent)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

HSTS Preload Requirements:

  1. Valid HTTPS certificate
  2. Redirect HTTP to HTTPS
  3. All subdomains served over HTTPS
  4. HSTS header with max-age >= 1 year, includeSubDomains, preload
  5. Submit at hstspreload.org

Content Security Policy

# Basic CSP (adjust for your application)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.example.com; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;

# Report-only mode for testing
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report" always;

Complete Header Set

# Essential security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Privacy and feature control
add_header Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;

# Legacy XSS protection (mostly obsolete with CSP)
add_header X-XSS-Protection "1; mode=block" always;

# Content Security Policy (customize for your app)
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; frame-ancestors 'none';" always;

Testing Your Configuration

SSL Labs Test

# Online test (comprehensive, public)
# Visit: https://www.ssllabs.com/ssltest/analyze.html?d=example.com

# Expected grade: A+ with proper configuration

testssl.sh (Command Line)

# Install
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
cd testssl.sh

# Basic test
./testssl.sh example.com

# Full test with all checks
./testssl.sh --full example.com

# Check specific vulnerabilities
./testssl.sh --vulnerable example.com

# Check cipher suites only
./testssl.sh --cipher-per-proto example.com

OpenSSL Manual Tests

# Test TLS 1.3 support
openssl s_client -connect example.com:443 -tls1_3

# Test TLS 1.2 support
openssl s_client -connect example.com:443 -tls1_2

# Verify TLS 1.1 is disabled (should fail)
openssl s_client -connect example.com:443 -tls1_1
# Expected: "no protocols available" or handshake failure

# List supported cipher suites
openssl s_client -connect example.com:443 -cipher 'ALL' 2>/dev/null | grep -E "Cipher|Protocol"

# Check certificate chain
openssl s_client -connect example.com:443 -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates

# Test OCSP stapling
openssl s_client -connect example.com:443 -status </dev/null 2>/dev/null | grep -A 20 "OCSP Response"

Mozilla Observatory

# Online test for security headers
# Visit: https://observatory.mozilla.org/

# Or use CLI
pip install httpobs-cli
httpobs example.com

Security Headers Test

# Quick header check
curl -I https://example.com 2>/dev/null | grep -iE "strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy"

Common Vulnerabilities to Check

VulnerabilityRiskMitigation
BEASTMediumUse TLS 1.2+ with AEAD ciphers
POODLEHighDisable SSL 3.0 and TLS 1.0
FREAKHighDisable export ciphers
LogjamMediumUse 2048-bit DH params or ECDHE
ROBOTHighDisable RSA key exchange
HeartbleedCriticalUpdate OpenSSL, check with testssl.sh
DROWNHighDisable SSL 2.0 on all servers
CRIME/BREACHMediumDisable TLS compression
Lucky13LowUse AEAD ciphers (GCM, ChaCha20)

Performance Optimization

Session Resumption

# TLS 1.2 session caching
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;

# Disable session tickets for better forward secrecy
# (trade-off: slightly more CPU usage)
ssl_session_tickets off;

# Or enable with key rotation for performance
# ssl_session_tickets on;
# ssl_session_ticket_key /etc/nginx/ssl/ticket.key;
# Rotate key every 24-48 hours

TLS 1.3 0-RTT (Use Carefully)

# Enable 0-RTT for improved latency (TLS 1.3)
# WARNING: Vulnerable to replay attacks
ssl_early_data on;

# In application, check for replays
proxy_set_header Early-Data $ssl_early_data;

# Application must reject sensitive requests with Early-Data: 1

ECDSA for Performance

# Generate ECDSA key (faster than RSA)
openssl ecparam -genkey -name prime256v1 -out ecdsa-key.pem

# Generate CSR
openssl req -new -key ecdsa-key.pem -out ecdsa.csr

# ECDSA handshakes are ~3x faster than RSA 2048

Automation and Monitoring

Configuration Checker Script

#!/bin/bash
# tls-check.sh - Quick TLS configuration verification

DOMAIN=${1:-"example.com"}

echo "=== TLS Configuration Check for $DOMAIN ==="

# Check TLS versions
echo -e "\n[Protocol Support]"
for proto in tls1 tls1_1 tls1_2 tls1_3; do
    result=$(echo | timeout 5 openssl s_client -connect $DOMAIN:443 -$proto 2>&1)
    if echo "$result" | grep -q "Protocol.*TLSv"; then
        version=$(echo "$result" | grep "Protocol" | head -1)
        echo "  $proto: ENABLED ($version)"
    else
        echo "  $proto: disabled"
    fi
done

# Check certificate
echo -e "\n[Certificate]"
cert_info=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -subject -dates -issuer)
echo "$cert_info" | sed 's/^/  /'

# Check OCSP stapling
echo -e "\n[OCSP Stapling]"
ocsp=$(echo | openssl s_client -connect $DOMAIN:443 -status 2>/dev/null | grep -A 1 "OCSP Response Status")
if [ -n "$ocsp" ]; then
    echo "  Enabled"
    echo "$ocsp" | sed 's/^/  /'
else
    echo "  Not enabled or no response"
fi

# Check security headers
echo -e "\n[Security Headers]"
headers=$(curl -sI https://$DOMAIN | grep -iE "strict-transport|x-frame|x-content-type|referrer-policy")
if [ -n "$headers" ]; then
    echo "$headers" | sed 's/^/  /'
else
    echo "  No security headers found"
fi

echo -e "\n=== Run SSL Labs test for comprehensive analysis ==="
echo "https://www.ssllabs.com/ssltest/analyze.html?d=$DOMAIN"

Best Practices Summary

  1. Use TLS 1.3 + TLS 1.2 only - Disable all older versions
  2. ECDHE key exchange - Ensures forward secrecy
  3. AEAD ciphers only - AES-GCM or ChaCha20-Poly1305
  4. Enable HSTS - With preload after testing
  5. Enable OCSP stapling - Improves performance and privacy
  6. Set security headers - CSP, X-Frame-Options, etc.
  7. Use ECDSA certificates - Faster than RSA
  8. Test regularly - SSL Labs, testssl.sh, Mozilla Observatory
  9. Monitor for vulnerabilities - Subscribe to security advisories
  10. Automate certificate renewal - Let's Encrypt/ACME

Next Steps

Need Expert Cybersecurity Guidance?

Our team of security experts is ready to help protect your business from evolving threats.