TLS Configuration Guide


Introduction

Transport Layer Security (TLS) is the foundation of secure internet communication. However, TLS is only as strong as its configuration. Weak cipher suites, outdated protocol versions, and missing security headers leave connections vulnerable to downgrade attacks, protocol flaws, and traffic interception.

Cipher Suites

A cipher suite defines the cryptographic algorithms used for key exchange, authentication, encryption, and message authentication.




# Modern Nginx TLS configuration


server {


listen 443 ssl http2;


server_name example.com;




ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;


ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;




# Modern cipher suite selection


ssl_protocols TLSv1.2 TLSv1.3;


ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;


ssl_prefer_server_ciphers off; # Let client negotiate for TLS 1.3




# Modern key exchange


ssl_ecdh_curve X25519:prime256v1:secp384r1;




# OCSP stapling


ssl_stapling on;


ssl_stapling_verify on;


resolver 1.1.1.1 8.8.8.8 valid=300s;


resolver_timeout 5s;


}





Cipher Suite Breakdown




ECDHE - Ephemeral Diffie-Hellman (forward secrecy)


ECDSA - Elliptic Curve Digital Signature Algorithm (authentication)


AES128 - AES with 128-bit key (symmetric encryption)


GCM - Galois/Counter Mode (authenticated encryption)


SHA256 - SHA-256 HMAC (integrity)





Deprecated Ciphers




# NEVER use these


ssl_protocols SSLv3 TLSv1 TLSv1.1; # All broken


ssl_ciphers RC4:3DES:EXPORT:NULL; # Weak or broken





HSTS (HTTP Strict Transport Security)

HSTS instructs browsers to always connect via HTTPS, preventing SSL stripping attacks.




# Strict HSTS for production


add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;




# Explanation:


# max-age=63072000 - 2 years in seconds


# includeSubDomains - Applies to all subdomains


# preload - Allow inclusion in browser preload lists








# Flask HSTS implementation


from flask import Flask, make_response




app = Flask(__name__)




@app.after_request


def add_security_headers(response):


if request.is_secure:


response.headers['Strict-Transport-Security'] = \


'max-age=63072000; includeSubDomains; preload'


return response





Certificate Pinning

While HTTP Public Key Pinning (HPKP) is deprecated, certificate pinning techniques remain useful in controlled environments.




# TLS fingerprint validation (alternative to deprecated HPKP)


import ssl


import hashlib


import requests


from cryptography import x509


from cryptography.hazmat.primitives import hashes




def validate_certificate_fingerprint(hostname, port=443, expected_hash=None):


cert_pem = ssl.get_server_certificate((hostname, port))


cert = x509.load_pem_x509_certificate(cert_pem.encode())




# Calculate SHA-256 fingerprint


fingerprint = cert.fingerprint(hashes.SHA256())


fingerprint_hex = fingerprint.hex()




if expected_hash and fingerprint_hex != expected_hash:


raise ValueError(


f"Certificate fingerprint mismatch for {hostname}: "


f"expected {expected_hash}, got {fingerprint_hex}"


)




return fingerprint_hex




# Usage: pin to expected fingerprint


PINNED_FINGERPRINTS = {


'api.example.com': 'a1b2c3d4e5f6...',


}





TLS 1.3 Benefits

TLS 1.3 simplifies the handshake to one round trip (or zero with pre-shared keys), removes insecure features, and mandates forward secrecy.




# Test TLS 1.3 support


openssl s_client -connect example.com:443 -tls1_3





Testing with SSL Labs




# Using ssllabs-scan


docker run --rm -t jumanjiman/ssllabs-scan example.com




# Using testssl.sh


testssl.sh --quiet --htmlfile report.html example.com




# Quick curl-based check


curl -sI https://example.com | grep -i "strict-transport-security\|x-frame-options\|content-security-policy"





Full Hardened Configuration




# /etc/nginx/conf.d/ssl.conf - hardened TLS


ssl_protocols TLSv1.2 TLSv1.3;


ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;


ssl_prefer_server_ciphers off;


ssl_ecdh_curve X25519:prime256v1:secp384r1;


ssl_session_cache shared:SSL:10m;


ssl_session_timeout 1d;


ssl_session_tickets off;




# OCSP Stapling


ssl_stapling on;


ssl_stapling_verify on;


resolver 1.1.1.1 8.8.8.8 valid=300s;




# Security Headers


add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;


add_header X-Content-Type-Options nosniff always;


add_header X-Frame-Options DENY always;





Conclusion

A proper TLS configuration requires modern protocol versions (TLS 1.2 and 1.3), forward-secrecy cipher suites, HSTS enforcement, and regular testing with tools like SSL Labs and testssl.sh. Disable all legacy protocols and ciphers, enable OCSP stapling for performance, and consider certificate pinning for high-security APIs.