TLS/SSL configuration including TLS 1.2/1.3 setup, cipher suites, security best practices, and server configuration
Scope: TLS 1.2/1.3 setup, cipher suites, security configuration, server/client setup Lines: ~380 Last Updated: 2025-10-27 Format Version: 1.0 (Atomic)
Activate this skill when:
Key Differences:
TLS 1.2:
├─ 2-RTT handshake (longer)
├─ Many cipher suites (some weak)
├─ RSA key exchange option
└─ Renegotiation support
TLS 1.3:
├─ 1-RTT handshake (faster)
├─ 0-RTT option (even faster, but replay risk)
├─ Only modern ciphers (AES-GCM, ChaCha20-Poly1305)
├─ Perfect forward secrecy mandatory
└─ No renegotiation
Handshake Comparison:
TLS 1.2 (2-RTT):
Client → ClientHello
← ServerHello, Certificate, ServerKeyExchange, ServerHelloDone
Client → ClientKeyExchange, ChangeCipherSpec, Finished
← ChangeCipherSpec, Finished
[Application Data]
TLS 1.3 (1-RTT):
Client → ClientHello (with key share)
← ServerHello, Certificate, Finished
Client → Finished
[Application Data]
TLS 1.3 Cipher Suites (simplified):
TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS 1.2 Recommended Cipher Suites:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
Cipher Suite Components:
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
│ │ │ │ └─ Hash: SHA256
│ │ │ └─ Mode: GCM (AEAD)
│ │ └─ Cipher: AES 128-bit
│ └─ Authentication: RSA
└─ Key Exchange: ECDHE (Elliptic Curve Diffie-Hellman Ephemeral)
Modern Configuration:
server {
listen 443 ssl http2;
server_name example.com;
# Certificates
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# TLS versions
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher suites (TLS 1.2)
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers on;
# TLS 1.3 ciphers (explicit, though defaults are good)
ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-chain.crt;
# Session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_session_tickets off;
# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location / {
proxy_pass http://backend;
}
}
Modern Configuration:
<VirtualHost *:443>
ServerName example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
SSLCertificateChainFile /etc/ssl/certs/ca-chain.crt
# TLS versions
SSLProtocol -all +TLSv1.2 +TLSv1.3
# Cipher suites
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder on
# OCSP stapling
SSLUseStapling on
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"
# HSTS
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</VirtualHost>
HTTPS Server:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('/etc/ssl/private/example.com.key'),
cert: fs.readFileSync('/etc/ssl/certs/example.com.crt'),
ca: fs.readFileSync('/etc/ssl/certs/ca-chain.crt'),
// TLS version
minVersion: 'TLSv1.2',
maxVersion: 'TLSv1.3',
// Cipher suites (TLS 1.2)
ciphers: [
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-RSA-CHACHA20-POLY1305'
].join(':'),
// Prefer server cipher order
honorCipherOrder: true,
// Session resumption
sessionTimeout: 300
};
const server = https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('Secure connection\n');
});
server.listen(443, () => {
console.log('HTTPS server running on port 443');
});
Secure Client:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
class TLSAdapter(HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
ctx = create_urllib3_context()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
ctx.maximum_version = ssl.TLSVersion.TLSv1_3
ctx.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20')
kwargs['ssl_context'] = ctx
return super().init_poolmanager(*args, **kwargs)
session = requests.Session()
session.mount('https://', TLSAdapter())
# Make request
response = session.get('https://api.example.com')
print(f"TLS version: {response.raw.version}") # 771 = TLS 1.2, 772 = TLS 1.3
Secure Client:
package main
import (
"crypto/tls"
"fmt"
"net/http"
)
func main() {
tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS13,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
PreferServerCipherSuites: false,
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
resp, err := client.Get("https://api.example.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Printf("TLS version: %d\n", resp.TLS.Version)
fmt.Printf("Cipher suite: %x\n", resp.TLS.CipherSuite)
}
Secure Client:
use reqwest::ClientBuilder;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = ClientBuilder::new()
.min_tls_version(reqwest::tls::Version::TLS_1_2)
.max_tls_version(reqwest::tls::Version::TLS_1_3)
.timeout(Duration::from_secs(10))
.build()?;
let response = client
.get("https://api.example.com")
.send()
.await?;
println!("Status: {}", response.status());
Ok(())
}
Server Configuration (Nginx):
server {
listen 443 ssl;
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
# Client certificate verification
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
location / {
# Client cert subject available
proxy_set_header X-Client-Cert-DN $ssl_client_s_dn;
proxy_pass http://backend;
}
}
Go Client with mTLS:
package main
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net/http"
)
func main() {
// Load client cert
cert, err := tls.LoadX509KeyPair("client.crt", "client.key")
if err != nil {
panic(err)
}
// Load CA cert
caCert, err := ioutil.ReadFile("ca.crt")
if err != nil {
panic(err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
resp, err := client.Get("https://api.example.com")
if err != nil {
panic(err)
}
defer resp.Body.Close()
}
Session Tickets (Nginx):
# Disable session tickets (privacy)
ssl_session_tickets off;
# Or enable with rotation
ssl_session_tickets on;
ssl_session_ticket_key /etc/ssl/ticket_key1.key;
ssl_session_ticket_key /etc/ssl/ticket_key2.key;
Session Cache (better option):
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# ✅ Good: TLS 1.2 and 1.3 only
ssl_protocols TLSv1.2 TLSv1.3;
# ❌ Bad: Old protocols
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
# ✅ Good: Modern ciphers only
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305';
# ❌ Bad: Weak ciphers included
ssl_ciphers 'ALL:!aNULL:!eNULL';
# ✅ Good: ECDHE for forward secrecy
ssl_ciphers 'ECDHE-RSA-...';
ssl_prefer_server_ciphers on;
# ❌ Bad: Static RSA key exchange
ssl_ciphers 'RSA-AES128-SHA';
# ✅ Good: Include full chain
with open('fullchain.pem') as f: # cert + intermediate
cert = f.read()
# ❌ Bad: Only leaf certificate
with open('cert.pem') as f: # missing intermediate
cert = f.read()
# ✅ Good: Enable OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/certs/ca-chain.crt;
# ❌ Bad: No OCSP stapling (client must check)
# Force HTTPS for 1 year, including subdomains
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# Prevent MIME sniffing
add_header X-Content-Type-Options "nosniff" always;
# XSS protection
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
# CSP
add_header Content-Security-Policy "default-src 'self'" always;
Check with OpenSSL:
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
Common causes:
Symptoms: Slow HTTPS connections
Check:
# Measure handshake time
curl -w "time_appconnect: %{time_appconnect}s\n" -o /dev/null -s https://example.com
# Should be < 200ms for TLS 1.3, < 400ms for TLS 1.2
Solutions:
Check chain:
openssl s_client -connect example.com:443 -showcerts
Fix: Ensure full chain in certificate file
cat cert.pem intermediate.pem > fullchain.pem
# SSL Labs
https://www.ssllabs.com/ssltest/analyze.html?d=example.com
# Mozilla Observatory
https://observatory.mozilla.org/analyze/example.com
# Test specific TLS version
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3
# Test cipher suite
openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES128-GCM-SHA256'
# Test with curl
curl -v --tlsv1.2 --tls-max 1.2 https://example.com
curl -v --tlsv1.3 --tls-max 1.3 https://example.com
# Phase 1: Enable TLS 1.3 alongside 1.2
ssl_protocols TLSv1.2 TLSv1.3;
# Phase 2: Monitor adoption (check logs)
# Phase 3: Eventually deprecate TLS 1.2 (if possible)
TLS 1.3 Support (as of 2025):
✅ All modern browsers
✅ Modern programming language libraries
⚠️ Some legacy enterprise systems
❌ Very old clients (pre-2018)
This skill includes comprehensive resources for advanced TLS configuration, testing, and validation.
resources/
├── REFERENCE.md # Detailed TLS specifications, RFCs, cipher suites
├── scripts/ # Executable validation and testing scripts
│ ├── README.md
│ ├── validate_tls_config.sh # Validate nginx/apache TLS configs
│ ├── check_cipher_suites.py # List and verify cipher suites
│ └── test_tls_connection.py # Test TLS connectivity and timing
└── examples/ # Real-world configuration examples
├── nginx/
│ ├── modern-tls-config.conf
│ └── mtls-config.conf
└── python/
├── tls_server.py
└── tls_client.py
Validate TLS configuration:
cd resources/scripts
./validate_tls_config.sh --file /etc/nginx/nginx.conf --verbose
Check cipher suites on remote host:
python check_cipher_suites.py --host example.com --json
Test TLS connection with timing:
python test_tls_connection.py example.com --test-all-versions
Reference material:
cat resources/REFERENCE.md # TLS 1.2 vs 1.3, cipher suites, RFCs, OCSP
See resources/scripts/README.md for detailed usage examples and CI/CD integration patterns.
cryptography-pki-fundamentals - Certificate basicscryptography-certificate-management - Certificate lifecyclenetworking-mtls-implementation - Mutual TLS patternsprotocols-http2-multiplexing - HTTP/2 over TLSprotocols-http3-quic - QUIC with built-in TLS 1.3Last Updated: 2025-10-27