# How testssl.sh Tests for Forward Secrecy (FS): Configuration and Implementation

> Discover how testssl.sh tests Forward Secrecy by filtering cipher suites and attempting handshakes. Learn its configuration options for comprehensive TLS security checks.

- Repository: [Dirk Wetter/testssl.sh](https://github.com/drwetter/testssl.sh)
- Tags: deep-dive
- Published: 2026-03-01

---

**testssl.sh tests for Forward Secrecy by filtering cipher suites to isolate only DHE, ECDHE, and TLS 1.3 offerings, then attempting handshakes via bash sockets or the native OpenSSL client to verify server support.** The test is triggered with the `-f` flag and controlled by environment variables including `WIDE`, `SSL_NATIVE`, and `CLIENT_MIN_FS`.

When auditing TLS configurations with drwetter/testssl.sh, verifying Forward Secrecy ensures that session keys remain secure even if the server's private key is compromised later. The script implements this check in the `run_fs` function, which negotiates ciphers using ephemeral Diffie-Hellman or Elliptic Curve Diffie-Hellman exchanges according to the source code at [`testssl.sh`](https://github.com/drwetter/testssl.sh/blob/main/testssl.sh) line 11068.

## Enabling Forward Secrecy Testing

To activate the FS audit, pass `-f`, `--fs`, or `--forward-secrecy` when invoking the script. The option table at line 21557 defines these flags, and the case handling at line 24811 dispatches execution to the dedicated `run_fs` function. When enabled, the script creates a JSON identifier `FS` at line 11002 to track results for machine-readable output.

## The FS Testing Architecture

The `run_fs` function (starting at line 11068) orchestrates the Forward Secrecy validation through a multi-stage pipeline that selects appropriate ciphers and chooses between socket-based or OpenSSL-based execution.

### Cipher Collection and Filtering

The script iterates over the complete cipher list (`TLS_CIPHER_RFC_NAME`, `TLS_CIPHER_HEXCODE`) and applies strict filtering logic at lines 11024-11025. A cipher qualifies as FS-capable only if it meets these criteria:

- Uses **DHE**, **ECDHE**, or **TLS 1.3** (identified by hex code `"${hexc:2:2}" == "13"`)
- Is not classified as **NULL**, **DES**, **RC4**, or **PSK**
- Is supported by the local OpenSSL installation or available through bash sockets

Before testing begins, the script verifies the client can offer at least `CLIENT_MIN_FS` ciphers (default value **5**, declared at line 225). If the available FS ciphers fall below this threshold, the test aborts with a warning at lines 11071-11075.

### Socket vs. OpenSSL Execution Modes

The script selects its testing backend based on the `SSL_NATIVE`, `FAST`, and `TLS_NR_CIPHERS` flags (lines 11004-11006):

- **Socket mode** (`using_sockets=true`): Sends TLS 1.2 and TLS 1.3 ClientHello packets containing the collected FS cipher hex codes via `tls_sockets "04"` and `tls_sockets "03"`, checking return codes and implementing fallback logic at lines 11058-11067.
- **OpenSSL mode**: Constructs an `openssl s_client` command with the filtered cipher list (`-cipher $fs_cipher_list`) and appends a `-curves` option to exhaustively test all supported elliptic curves (lines 11090-11108). When more than 28 curves are present, the script automatically splits them into two batches (lines 11177-11188).

## Result Interpretation and Grading

After handshake completion, the script extracts the negotiated cipher using `fs_cipher=$(get_cipher $TMPFILE)`. If no handshake succeeds, testssl.sh reports *"No ciphers supporting Forward Secrecy (FS) offered"* at lines 11114-11116. Successful negotiation marks FS as offered with JSON status `OK` (line 11122).

When `WIDE` mode is enabled, the output expands to include specific cipher names, hex codes, key exchange types, and curve information (lines 11123-11127). If the runtime detection `HAS_DH_BITS` returns false, the script cannot display DH/ECDH key-size information and emits a warning at lines 11012-11015.

According to the grading logic at line 11116, missing Forward Secrecy capability caps the overall security grade at **"B"** regardless of other strengths.

## Configuration Options and Environment Variables

The behavior of the FS test is controlled through several environment variables and command-line options:

- **`CLIENT_MIN_FS`** (default: 5): Sets the minimum number of FS ciphers required on the client side to run the test. Declared at line 225.
- **`WIDE`**: When set to `true`, prints the complete list of offered FS ciphers/curves with additional columns for hex codes and key exchange details.
- **`SSL_NATIVE`**: Forces the use of the native OpenSSL client instead of bash sockets, useful on platforms where socket reliability is uncertain.
- **`FAST`**: Shortcut that disables socket usage, running a reduced FS test for quicker results.
- **`HAS_DH_BITS`**: Runtime-detected boolean indicating whether the OpenSSL build supports displaying DH/ECDH key-size information.
- **`OPENSSL`**: Specifies the path to the OpenSSL binary used for `s_client` commands.
- **`CURVES` handling**: Automatically derived from `OSSL_SUPPORTED_CURVES` with internal batching logic when curve count exceeds 28; no user-visible switch controls this.

## Practical Usage Examples

Run a basic Forward Secrecy test against a target host:

```bash
./testssl.sh -f example.com:443

```

Enable the wide view to see curve information and hex codes:

```bash
WIDE=true ./testssl.sh -f example.com:443

```

Force OpenSSL-native mode when socket implementations are unreliable:

```bash
SSL_NATIVE=true ./testssl.sh -f example.com:443

```

Adjust the minimum required client FS ciphers (for example, requiring at least 10):

```bash
CLIENT_MIN_FS=10 ./testssl.sh -f example.com:443

```

## Summary

- **Invocation**: Use `-f`, `--fs`, or `--forward-secrecy` to enable Forward Secrecy testing.
- **Implementation**: The `run_fs` function filters for DHE/ECDHE/TLS 1.3 ciphers and attempts handshakes via sockets or OpenSSL.
- **Minimum Threshold**: The `CLIENT_MIN_FS` variable (default 5) determines the minimum client-side cipher support required to run the test.
- **Output Control**: Set `WIDE=true` to display detailed cipher and curve information; missing FS caps the grade at "B".
- **Backend Selection**: Use `SSL_NATIVE=true` to bypass bash sockets and use the system OpenSSL client directly.

## Frequently Asked Questions

### What ciphers qualify as Forward Secrecy in testssl.sh?

According to the filtering logic at lines 11024-11025, testssl.sh classifies ciphers as FS-capable if they use DHE, ECDHE, or TLS 1.3 key exchange (identified by hex code `13`), and explicitly excludes NULL authentication, DES, RC4, and PSK variants.

### Why does testssl.sh cap the grade at B when FS is missing?

The grading logic at line 11116 enforces a maximum grade of "B" when the server fails to offer Forward Secrecy, regardless of other security strengths. This reflects the cryptographic principle that without FS, compromised long-term private keys could decrypt past sessions.

### How do I see the specific elliptic curves offered by the server?

Set the environment variable `WIDE=true` when running the test. This activates the detailed output block at lines 11123-11127, which displays the negotiated curves alongside cipher names, hex codes, and key exchange mechanisms. If `HAS_DH_BITS` is false, curve size information will be unavailable.

### What happens if my OpenSSL client supports fewer than 5 FS ciphers?

If the available client ciphers fall below the `CLIENT_MIN_FS` threshold (default 5, defined at line 225), the script skips the test and emits a warning at lines 11071-11075. You can override this by setting `CLIENT_MIN_FS` to a lower value or ensuring your OpenSSL binary supports a broader cipher suite.