Here we give instructions and references how to implement strict verification of ALPN and SNI in common TLS libraries. We thank the maintainers of these libraries for their help in assembling these instructions, and apologize for any errors we made in editing. Please let us know if you find inaccuracies or areas for improvement.
Implementing these countermeasures is effective in preventing cross-protocol attacks irregardless of hostnames and ports used for application servers. The most important recommendations are highlighted below.
We recommend that the server aborts the handshake if the client sends the ALPN extension, but the list of protocols does not include a protocol supported by the server.
We also recommend that the server acknowledge the ALPN extension, so the client can verify that the server supports ALPN and selected a valid protocol.
We do not recommend that the server aborts the handshake if the client does not send the ALPN extension, unless backwards compatibility wth legacy clients that do not support ALPN is not a concern.
We recommend that the client sends the ALPN extension with a list of the intended protocols, and the SNI extension with the intended hostname.
We also recommend that the client aborts the handshake if the server acknowledges the ALPN extension, but does not select a protocol from the client list.
We do not recommend that the client aborts the handshake if the server does not acknowledge the ALPN extension, unless backwards compatibility with legacy servers that do not support or acknowledge the ALPN extension is not a concern.
Implementing these countermeasures is effective in preventing same-protocol attacks on servers with different hostnames, as well as cross-protocol attacks on servers with different hostnames even if the ALPN countermeasures can not be implemented.
Compared to ALPN, the SNI extension is more difficult to configure, because the application server needs to be aware of its configuration in the network. While the supported protocol list for ALPN is usually determined statically at time of development of the application server, the list of acceptable hostnames for SNI is dependent on the location of the server in the network, and how it is accessed by clients, for example through the use of gateway and proxy servers. We recognize that in complicated deployment scenarios, implementing the following recommendations can be challenging. In particular, implementing these SNI-based countermeasures can potentially break legitimate connections that depend on inappropriately configured legacy environments, such as SNI fallback for unknown hosts.
We recommend that the server aborts the handshake if the client sends an SNI hostname that the server does not recognize.
We also recommend that the server acknowledges the SNI hostname to the client so the client can verify that the server supports SNI and recognized the hostname.
We do not recommend that the server aborts the handshake if the client does not send an SNI hostname, unless backwards compatibility wth legacy clients that do not support SNI is not a concern.
We recommend that all clients are configured to send the SNI extension with the hostname of the intended server.
We also recommend that clients abort the handshake if the server acknowledges the SNI hostname with a different hostname than the one sent by the client.
We do not recommend that clients abort the handshake if the server does not acknowledge the SNI hostname, unless backwards compatibility with legacy servers that do not support or acknowledge the SNI hostname is not a concern.
ALPN selection in the client is supported by SSL_CTX_set_alpn_protos(), which takes as input a list of supported protocols for negotiation.
ALPN selection in the server is supported by an application
callback set
with
SSL_CTX_set_alpn_select_cb()
. The
callback should return one of three values:
SSL_TLSEXT_ERR_OK
: ALPN protocol
selected. The connection continues.
SSL_TLSEXT_ERR_ALERT_FATAL
: There was no
overlap between the client's supplied list and the
server configuration. The connection will be aborted.
SSL_TLSEXT_ERR_NOACK
: ALPN protocol not
selected, e.g., because no ALPN protocols are
configured for this connection. The connection
continues.
Note that if there is no ALPN proposed in the ClientHello, the callback is not invoked.
To protect against ALPACA attacks with OpenSSL and ALPN,
the client should configure the supported protocols
with SSL_CTX_set_alpn_protos()
, and the
server should configure
the SSL_CTX_set_alpn_select_cb
callback. If
an expected protocol identifier is provided, the server
should return SSL_TLSEXT_ERR_OK
, otherwise
it should return SSL_TLSEXT_ERR_ALERT_FATAL
.
The SNI hostname is set in the client by calling SSL_set_tlsext_host_name().
In the server, SNI selection is supported by an application
callback set
with
SSL_CTX_set_tlsext_servername_callback()
. The
callback should return one of four values:
SSL_TLSEXT_ERR_OK
: SNI hostname is
accepted. The connection continues.
SSL_TLSEXT_ERR_ALERT_FATAL
: SNI hostname
is not accepted. The connection is aborted.
SSL_TLSEXT_ERR_ALERT_WARNING
: SNI hostname
is not accepted, warning alert sent (not in
TLSv1.3). The connection continues.
SSL_TLSEXT_ERR_NOACK
: SNI hostname is not
accepted and not acknowledged, e.g. if SNI has not been
configured. The connection continues.
To protect against ALPACA attacks with OpenSSL and SNI, the
client should configure the SNI hostname
with SSL_set_tlsext_host_name()
, and the
server should
configure SSL_CTX_set_tlsext_servername_callback()
. If
a matching SNI hostname is sent by the client, the server
should return
SSL_TLSEXT_ERR_OK
, otherwise it should
return SSL_TLSEXT_ERR_ALERT_FATAL
.
The TLS stack in Java implements strict ALPN verification by
default. The server and the client can configure ALPN support by passing
acceptable values to the SSLParameters
configuration object using
the javax.net.ssl.SSLParameters.getSSLParameters
method,
which is then passed to the SSLEngine/SSLSocket to be
configured. Overlapping values will be selected.
Alternatively, the server can examine the values of the client
ALPN extension and its own SSLSocket/SSLEngine before a
protocol is negotiated, using
the javax.net.ssl.SSLSocket.setHandshakeApplicationProtocolSelector
callback function. The function must return a String value
that is in the client's list, and takes priority over
the SSLParameters
list. The function can also
return the empty string if ALPN should not be acknowledged by
the server, and null (or a String value not in the client's
list) to abort the connection with
a no_application_protocol
alert.
To protect against ALPACA attacks with Java and ALPN, the
client should set the list of supported protocols with
the getSSLParameters
method, and the server
should either do the same or implement
the setHandshakeApplicationProtocolSelect
and
return either a recognized protocol or null to abort the
handshake.
The server can use SSLParameters.setSNIMatchers
to enable strict hostname verification.
The client can set the SNI hostname
with SSLParameters.setServerNames
.
To protect against ALPACA attacks with Java and SNI, the
client should set the SNI hostname
with SSLParameters.setServerNames
, and the server
should set a SNI matcher
with SSLParameters.setSNIMatchers
that returns
true if the hostname is recognized and false to abort the
handshake.
The client and server can be configured to negotiate a
protocol using the tls.Config.NextProtos
field. At the time of writing (2021-02-16), the handshake
will not be aborted on the client side if the server ignores
the clients ALPN extension, or on the server side if the
server does not support any of the clients requested
protocols. However, application developers can still
implement stricter enforcement by setting
the tls.Config.VerifyConnection
callback,
although this is considered an advanced pattern.
Note: The default behavior might change in the future to implement strict checking according to RFC 7301.
To protect against ALPACA attacks with GoLang and ALPN, the
client and server should configure the supported protocols
in tls.Config.NextProtos
, and the server
should abort the handshake if no protocol can be
negotiated, using manual verification in the
tls.Config.VerifyConnection
callback. If and
when the default behavior changes to enforce strict ALPN
verification, this custom callback can be removed.
The server does not strictly enforce the SNI
hostname. Instead, the SNI hostname is used to select the
right certificate if multiple certificates are
configured. However, application developers can still
implement stricter enforcement by setting the
tls.Config.VerifyConnection
, although this is
considered an advanced pattern.
On the client side, setting
the tls.Config.ServerName
is required. This is
sent in the ClientHello and used to verify the certificate
provided by the server, unless certificate verification is
disabled.
To protect against ALPACA attacks with GoLang and SNI, the
client should configure the SNI hostname
in tls.Config.ServerName
, and the server
should abort the handshake if the client provided an SNI
hostname that is not recognized, using manual verification
in the
tls.Config.VerifyConnection
callback.
The Windows TLS stack implements RFC 7301 faithfully and always terminates if the client and the server ALPN extensions are configured and the server does not recognize a supported protocol.
To protect against ALPACA attacks with the Windows TLS stack and ALPN, the client and server should configure the supported ALPN protocols.
The Windows TLS stack provides SNI information to the TLS server application. It is up to the server application to handle SNI in an application-specific manner. The Windows TLS stack has no way to enforce disconnects on SNI mismatch: it does not own the socket, and does not have any information about the network. On the TLS client side, any caller-provided SNI information is used for server certificate validation.
To protect against ALPACA attacks with the Windows TLS stack and SNI, the client should configure the SNI hostname, and the server should abort the handshake if the client provided an SNI hostname that is not recognized, using manual verification.
Mbed TLS implements RFC 7301 faithfully and always terminates if the client and the server ALPN extensions are configured and the server does not recognize a supported protocol.
The server and the client can enable ALPN by
defining MBEDTLS_SSL_ALPN
in the configuration
script, and set the list of supported protocols
with mbedtls_ssl_conf_alpn_protocols
(see the
example in programs/ssl/ssl_client2.c
).
To protect against ALPACA attacks with Mbed TLS and ALPN, the client and server should configure the supported ALPN protocols.
Mbed TLS supports SNI with
the MBEDTLS_SSL_SERVER_NAME_INDICATION
setting in
the configuration script, which is enabled by default.
The client can enable SNI by setting the hostname of the
server with mbedtls_ssl_set_hostname()
.
The server can set a callback function
with mbedtls_ssl_conf_sni()
and compare the
hostname in the third argument with the list of supported
hostnames. In case no match is found, the server can terminate
the connection by returning a non-zero value from the callback
(see the example sni_callback()
in ssl_server2.c
).
To protect against ALPACA attacks with Mbed TLS and SNI, the client should configure the SNI hostname, and the server should abort the handshake if the client provided an SNI hostname that is not recognized, using manual verification.
BoringSSL leaves server ALPN selection to an
application-supplied callback. In response to our findings,
BoringSSL has
made changes to allow
strict ALPN verification by returning
SSL_TLSEXT_ERR_ALERT_FATAL
from this callback (same as
OpenSSL).
To protect against ALPACA attacks with BoringSSL and ALPN, the
server should return SSL_TLSEXT_ERR_ALERT_FATAL
in the ALPN callback to abort the connection if no matching
protocol is found.
BoringSSL implements SNI support with the application-supplied
servername
callback. The application can terminate the connection by
returning SSL_TLSEXT_ERR_ALERT_FATAL
.
Beside the servername callback, there are two more SNI-related
callbacks that offer more flexibility, and have other return
values to indicate an
error. The cert
callback can return zero on error, and
the select_certificate
callback can return ssl_select_cert_error
.
To protect against ALPACA attacks with BoringSSL and SNI, the
server should return SSL_TLSEXT_ERR_ALERT_FATAL
from the servername callback if the clients provides an SNI
hostname that is not recognized, or equivalently return an
appropriate error from the cert
or select_certificate
callback functions in this case.
The server implements a
callback tls_server_choose_app_protocol
which
takes a list of the protocols offered by the client, and
returns a string which is either empty (meaning to ignore the
ALPN) or some protocol, or it may choose to throw an exception
at that point which will cause the connection to be closed
with an alert. The default implementation returns an empty
string, thus ignoring ALPN.
On the client side, the ALPN protocol list can be set in the
next_protocols
argument when creating
the TLS::Client
instance.
To protect against ALPACA attacks with Botan and ALPN, the
server should implement
the tls_server_choose_app_protocol
callback and
throw an exception if none of the protocols provided by the
client are recognized. The client should set the list of
protocols in the next_protocols
argument
of TLS::Client
.
The server invokes a callback
on Credentials_Manager
, cert_chain_single_type
,
which asks to return a certificate chain usable for the named
host. If the callback throws an exception if the named host
does not match an expected hostname, then the connection will be
closed.
Clients can send the SNI hostname by passing an
initialized Server_Information
struct to
the TLS::Client
constructor.
To protect against ALPACA attacks with Botan and SNI, the
server should implement
the cert_chain_single_type
callback and throw an
exception if the SNI hostname provided by the client is not
recognized. The client should set the hostname in
the Server_Information
struct of
the TLS::Client
constructor.
The client and server can be configured to negotiate a
protocol using
the br_ssl_engine_set_protocol_names()
function. A mismatch can be detected by either the server
or the client, if the ALPN extension was used, and the list
of protocols sent by the client or resp. the protocol
chosen by the server is not part of the configured list.
In this case, the behavior depends on
the BR_OPT_FAIL_ON_ALPN_MISMATCH
flag: If the flag was set
with br_ssl_engine_add_flag()
, a mismatch
implies a handshake failure with fatal alert 120. If the
flag was not set, then the handshake continues.
The selected protocol can be obtained with br_ssl_engine_get_selected_protocol(). The result is NULL if no protocol was selected, which can happen on mismatch (with the BR_OPT_FAIL_ON_ALPN_MISMATCH not set), or if the peer did not send an ALPN extension.
To protect against ALPACA attacks with BearSSL and ALPN,
the client and server should configure the list of
supported protocols
with br_ssl_engine_set_protocol_names()
and
the server (and preferably the client, too) should set
the BR_OPT_FAIL_ON_ALPN_MISMATCH
flag.
The client initializes its context
with br_ssl_client_reset()
,
which also takes an SNI hostname parameter.
The server can retrieve the SNI hostname
with br_ssl_engine_get_server_name()
and implement strict checking manually in the callback
configured to select the certificate chain to be sent to
the client.
To protect against ALPACA attacks with BearSSL and SNI, the
client should set the SNI hostname with
the server_name
parameter
in br_ssl_client_reset()
. The server should
retrieve that hostname
with br_ssl_engine_get_server_name()
in the
certificate chain selection callback, verify that the SNI
hostname provided by the client is recognized, and
otherwise abort the handshake.
To support ALPN, WolfSSL should be configured
with --enable-alpn
or by
defining HAVE_ALPN
and HAVE_TLS_EXTENSIONS
in a settings header. The
default behavior is configurable and must be set when ALPN is
initialized,
using wolfSSL_UseALPN()
with the opion WOLFSSL_ALPN_CONTINUE_ON_MISMATCH
or WOLFSSL_ALPN_FAILED_ON_MISMATCH
.
To protect against ALPACA attacks with WolfSSL and ALPN, the
client and server should enable ALPN by setting the appropriate
compilation flags, and initialize it by
calling wolfSSL_UseALPN
. The server (and preferably
the client, too) should use
the WOLFSSL_ALPN_FAILED_ON_MISMATCH
option to enforce
strict ALPN verification.
To support SNI, WolfSSL should be configured
with --enable-sni
or by
defining HAVE_SNI
and HAVE_TLS_EXTENSIONS
in a settings header. The
client
can set
the SNI hostname with wolfSSL_CTX_UseSNI
or woflSSL_UseSNI
, and the server can implement
custom verification using the callback
function wolfSSL_CTX_set_servername_callback()
.
To protect against ALPACA attacks with WolfSSL and SNI, the client
and server should enable SNI by setting the appropriate
compilation flags. The client should initialize it by
calling wolfSSL_UseSNI
. The server should implement a
custom verification for the SNI hostname using
the wolfSSL_CTX_set_servername_callback
.
To support ALPN in GnuTLS, the client or server can call gnutls_alpn_set_protocols
before the handshake to configure the list of supported protocols. The function takes an array of
gnutls_datum_t
elements with protocol names. If the flag GNUTLS_ALPN_MANDATORY
is set when calling that
function, the connection
is terminated when no matching protocol is found. The seelcted protocol can be obtained through
gnutls_alpn_get_selected_protocol
.
To protect against ALPACA attacks with GnuTLS and ALPN, the
client and server should call gnutls_alpn_set_protocol
with the
GNUTLS_ALPN_MANDATORY
flag set to enforce strict ALPN verification.
To support SNI in GnuTLS, the client should call gnutls_server_name_set
before the handshake
starts to set the intended server name.
The server can retrieve the name sent by the client with gnutls_server_name_get
.
To protect against ALPACA attacks with GnuTLS and SNI, the client should initialize it by
calling gnutls_server_name_set
, and the server should set a hook function on
the ClientHello, call gnutls_server_name_get
and manually verify the SNI hostname.