SSL/TLS Best Practices

Secure SSL/TLS implementation is essential to protect data in transit against interception, modification and man-in-the-middle attacks that can compromise the confidentiality and integrity of sensitive communications. Although SSL/TLS is widely adopted (virtually every modern site uses HTTPS), many implementations still contain vulnerabilities stemming from inadequate configurations - the use of obsolete protocol versions (SSL 2.0, SSL 3.0, TLS 1.0, TLS 1.1) that have been officially deprecated by bodies such as the IETF and PCI-DSS due to known vulnerabilities like POODLE, BEAST and CRIME, weak or broken cipher suites such as RC4, DES, 3DES and MD5 that can be attacked with modern computing resources, certificates with SHA-1 signature algorithms now considered insecure, lack of Perfect Forward Secrecy (PFS) that allows captured traffic to be decrypted retroactively if the server's private key is compromised in the future, absence of HSTS (HTTP Strict Transport Security) enabling downgrade attacks where an attacker forces an unencrypted HTTP connection, and inadequate or missing certificate pinning in critical mobile applications allowing interception via malicious certificates installed on the device. Optimized SSL/TLS configuration not only improves security posture but also impacts performance (TLS 1.3 handshakes are faster than TLS 1.2), compatibility (very old clients may not support modern configurations) and compliance (LGPD, GDPR, PCI-DSS require strong encryption for data in transit). This article provides detailed technical guidelines for implementing SSL/TLS following industry best practices, recommendations from NIST, the Mozilla SSL Configuration Generator and OWASP.

Protocol Versions: TLS 1.2 and TLS 1.3

Completely disable SSL 2.0, SSL 3.0, TLS 1.0 and TLS 1.1 on all servers - these versions have documented vulnerabilities and should not be used even for compatibility with legacy clients. Configure your web server (nginx, Apache, IIS) or load balancer (AWS ALB, Azure Application Gateway) to accept only TLS 1.2 and TLS 1.3. TLS 1.3, ratified in 2018 as RFC 8446, offers significant improvements over previous versions: a faster handshake (1-RTT instead of 2-RTT, with 0-RTT support for resumed sessions), removal of insecure cipher suites (it no longer supports RSA key exchange, CBC mode ciphers, RC4, 3DES), mandatory forward secrecy in all cipher suites, and an encrypted handshake that hides protocol negotiation metadata. In nginx, configure with "ssl_protocols TLSv1.2 TLSv1.3;". In Apache, "SSLProtocol -all +TLSv1.2 +TLSv1.3". AWS ALB allows selection of a security policy that defines protocols and ciphers - choose "ELBSecurityPolicy-TLS13-1-2-2021-06" for modern environments or "ELBSecurityPolicy-2016-08" if you need to support some older clients. Monitor your access logs to identify how many clients still use TLS 1.0/1.1 - if the number is insignificant (less than 0.1% of traffic), you can disable it without material impact. For B2B APIs where you control both endpoints, require TLS 1.3 exclusively. Remember that the protocol version is negotiated during the handshake - the server should announce the supported versions and the client selects the highest one it also supports.

Cipher Suites: ECDHE and AES-GCM

Cipher suites define the algorithms used for key exchange, authentication, symmetric encryption and HMAC during a TLS connection - an inadequate choice can compromise the entire security of the communication even when using TLS 1.2+. Prioritize cipher suites with ECDHE (Elliptic Curve Diffie-Hellman Ephemeral) for key exchange because it offers Perfect Forward Secrecy, and AES-GCM (Galois/Counter Mode) or ChaCha20-Poly1305 for symmetric encryption because they are AEAD (Authenticated Encryption with Associated Data) ciphers that guarantee both confidentiality and integrity. Example of a secure cipher suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 or TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256. Completely avoid: cipher suites with RC4 (vulnerable to statistical biases), export ciphers (limited to 40 or 56 bits by old export regulations), NULL ciphers (no encryption), DES and 3DES (small block size vulnerable to Sweet32), CBC mode in TLS 1.0-1.1 (vulnerable to BEAST and Lucky13), and RSA key exchange without DHE/ECDHE (no forward secrecy). In nginx, configure "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:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';" and "ssl_prefer_server_ciphers on;" to force the server's order. Use tools such as ssllabs.com/ssltest to validate your configuration and obtain an A+ rating. For TLS 1.3, cipher suites are simplified and more secure by default (TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256).

HSTS: HTTP Strict Transport Security

HSTS is a mechanism that instructs browsers to always use HTTPS for a specific domain, preventing SSL stripping attacks where a man-in-the-middle forces an unencrypted HTTP connection by intercepting the first request before the 301 redirect to HTTPS happens. Implement HSTS by adding the "Strict-Transport-Security" header to all HTTPS responses from your server: "Strict-Transport-Security: max-age=31536000; includeSubDomains; preload". The max-age parameter (in seconds) defines how long the browser should remember to use only HTTPS - 31536000 seconds = 1 year is the recommended value. "includeSubDomains" applies the policy to all subdomains (use with care because you need to ensure that ALL subdomains support HTTPS). "preload" indicates that you want your domain included in the HSTS preload list maintained by browsers - a hardcoded list of domains that must always use HTTPS even on the first visit before any HSTS header is received. To be included in the preload list, submit your domain at hstspreload.org after confirming that: the entire site works perfectly over HTTPS, you have a valid certificate covering the domain and subdomains, you redirect all HTTP traffic to HTTPS, you serve the HSTS header on the base domain with the includeSubDomains and preload directives, and max-age is greater than or equal to 31536000. IMPORTANT: preload is a one-way decision - removing a domain from the list can take months and will break access for users during that period if you can no longer support HTTPS. In nginx: "add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;". In Apache: "Header always set Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload'".

Certificate Pinning for Critical Applications

Certificate pinning is a technique where your application (especially mobile apps and critical APIs) hardcodes or embeds the hash/fingerprint of the server's expected certificate, validating during the TLS handshake that the presented certificate matches the stored pin - this prevents attacks where an attacker installs a malicious certificate on the victim's device (via malware or physical access) and intercepts HTTPS traffic that would normally be trusted by the operating system's default CA chain. There are two types of pinning: pinning the leaf certificate (specific to that certificate, must be updated when the certificate expires/renews), or pinning the CA's public key/public key (more flexible, allows certificate rotation as long as they are signed by the same CA). Implement pinning in mobile apps using native frameworks: the iOS Network framework allows pinning via URLSessionDelegate callbacks, Android uses Network Security Configuration XML with the pin-set element, React Native can use the react-native-ssl-pinning library. For APIs, consider mutual TLS (mTLS) where the client also presents a certificate to the server, validated via pinning. CAUTION: incorrect pinning can break your application if you lose access to the pinned certificate and have no fallback mechanism - always implement: a backup pin (a second valid certificate/CA), over-the-air pin updates, and a grace period before enforcing pins to allow rollback in emergencies. Use Public Key Pinning instead of Certificate Pinning when possible (more resilient to renewals). Example of a configuration on Android with an SHA-256 digest and base64 hash of the public key. Tools such as openssl can extract the public key hash from certificates to use in the pins.

Perfect Forward Secrecy (PFS)

Perfect Forward Secrecy is a cryptographic property where compromise of the server's long-term private key (the SSL certificate's private key) does not allow decryption of past sessions that were captured - this is achieved by using dynamically generated ephemeral keys for each session that are destroyed after use. Without PFS, if an attacker captures all encrypted traffic (something trivial with network taps) and years later manages to steal your private key (via a breach, insider threat, court order), they can go back to the stored pcaps and decrypt ALL historical sessions - a scenario known as "retrospective decryption". PFS prevents this by generating unique session keys through Diffie-Hellman algorithms that do not depend on the certificate's private key for derivation - even if the private key is compromised, previous session keys remain secure because they were generated with ephemeral components that no longer exist. To ensure PFS, use only cipher suites with DHE (Diffie-Hellman Ephemeral) or ECDHE (Elliptic Curve DHE) in the key exchange - avoid RSA key exchange, which does not offer PFS because the session key is encrypted with the server's public key and decrypted with the private key. In TLS 1.3, PFS is mandatory (all cipher suites use ECDHE). In TLS 1.2, configure the server to prefer ECDHE ciphers over RSA. Validate with: openssl s_client -connect yoursite.com:443 -cipher 'ECDHE' - if it connects successfully, PFS is working. An additional benefit of PFS is compliance - many frameworks (PCI-DSS, NIST) recommend or require PFS for the protection of sensitive data. Disadvantage: slight performance overhead because DHE operations are computationally more expensive than RSA, but with modern hardware (AES-NI, CPU crypto acceleration) the impact is negligible.