Forward Secrecy
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5), since detection_hints lists semgrep, ssllabs, and testssl.sh — specialist scanners that detect static RSA cipher suites or legacy TLS via code_pattern matching, not a default linter or compiler.
Closest to 'simple parameterised fix' (e3), since quick_fix is to switch to TLS 1.3 or restrict cipher suite configuration to ECDHE/DHE families — a config-level pattern replacement rather than a single-line swap, but localized to TLS setup.
Closest to 'localised tax' (b3), since the choice applies to TLS/key-exchange configuration in web/cli/library contexts; it affects the connection layer but does not gravitationally reshape every change across the codebase.
Closest to 'serious trap' (t7), since the misconception is that forward secrecy is a property of the cipher (AES) when it is actually a property of the key exchange — a developer reasonably assumes AES-256-GCM gives forward secrecy, contradicting how the property actually works, with multiple reinforcing common_mistakes.
Also Known As
TL;DR
Explanation
Forward secrecy (often called perfect forward secrecy, PFS) guarantees that recording today's encrypted traffic does not let an attacker decrypt it later even if they eventually steal the server's long-term private key. The mechanism is ephemeral key exchange: instead of using the server's static RSA key to transport the session secret, both parties run an ephemeral Diffie-Hellman exchange (ECDHE or DHE) that generates a fresh key pair per connection. The shared session key is derived from those ephemeral values and then thrown away when the session ends - it is never written to disk and never encrypted under the long-term key. Because the ephemeral private keys exist only briefly in memory and are discarded, there is nothing left to recover later. Without forward secrecy (classic RSA key transport in TLS), the server's private key can decrypt the premaster secret of every session ever recorded; one stolen key compromises years of captured traffic retroactively. With forward secrecy, an attacker who steals the long-term key can impersonate the server going forward but cannot read the archive of past conversations. TLS 1.3 enforces forward secrecy by removing static RSA and static DH key exchange entirely - every handshake uses ephemeral (EC)DHE. In TLS 1.2 you must explicitly prefer ECDHE/DHE cipher suites and avoid plain RSA suites. Messaging protocols like Signal go further with double-ratchet schemes that rotate keys per message, giving per-message forward secrecy. Forward secrecy is a property of the key agreement, not of the symmetric cipher: AES-GCM with RSA key transport has no forward secrecy, while AES-GCM with ECDHE does. Operationally, the cost is a slightly heavier handshake (an extra ephemeral computation), which modern hardware handles trivially. The benefit is that the value of a stolen private key collapses from 'decrypt everything ever sent' to 'impersonate from now on', dramatically shrinking the blast radius of key compromise.
Common Misconception
Why It Matters
Common Mistakes
- Leaving static RSA key-transport cipher suites enabled in TLS 1.2 so connections fall back to no forward secrecy.
- Believing AES-256 alone provides forward secrecy when the session key came from non-ephemeral key transport.
- Logging or persisting derived session keys, which defeats the whole point of discarding ephemeral material.
- Reusing a single ephemeral DH key pair across many connections instead of generating a fresh one per handshake.
- Assuming forward secrecy protects future sessions after key theft - it only protects already-recorded past sessions.
Avoid When
- An internal protocol where traffic is never recorded and key compromise risk is negligible - though ephemeral exchange is cheap enough that there is rarely a reason to skip it.
- Legacy embedded clients that physically cannot perform ephemeral (EC)DHE and where upgrading transport is impossible.
- Situations requiring passive decryption for compliance inspection, where deliberate key escrow conflicts with forward secrecy goals.
When To Use
- Any TLS endpoint serving sensitive data - prefer TLS 1.3 or ECDHE/DHE-only suites on TLS 1.2.
- Long-lived encrypted channels where recorded traffic could be a future target if the server key leaks.
- Messaging or session protocols where you want per-session or per-message key rotation to limit breach blast radius.
- Compliance regimes that mandate cryptographic best practice for data in transit.
Code Examples
<?php
// TLS 1.2 config that allows non-forward-secret RSA key transport.
// These cipher suites use the server's static RSA key to encrypt
// the premaster secret - one stolen key decrypts all past traffic.
$context = stream_context_create([
'ssl' => [
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
// Includes plain RSA suites (no ECDHE/DHE) -> no PFS:
'ciphers' => 'AES256-GCM-SHA384:AES128-GCM-SHA256',
],
]);
$fp = stream_socket_client(
'ssl://api.example.com:443', $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, $context
);
<?php
// Prefer TLS 1.3 (forward secrecy is built in, every handshake is ephemeral).
// If TLS 1.2 is required, restrict to ECDHE/DHE suites only.
$context = stream_context_create([
'ssl' => [
'crypto_method' =>
STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT |
STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
// Only ephemeral key-exchange suites -> forward secrecy:
'ciphers' => 'ECDHE-ECDSA-AES256-GCM-SHA384:'
. 'ECDHE-RSA-AES256-GCM-SHA384:'
. 'ECDHE-RSA-AES128-GCM-SHA256',
'verify_peer' => true,
'verify_peer_name' => true,
],
]);
$fp = stream_socket_client(
'ssl://api.example.com:443', $errno, $errstr, 30,
STREAM_CLIENT_CONNECT, $context
);
// Session keys are derived from ephemeral DH and discarded after use.