Personalización de SNI en solicitudes HTTP

Cuando un cliente y un servidor negocian una conexión HTTPS, primero debe establecerse una conexión TLS. Como parte del protocolo de enlace TLS, el cliente envía el nombre de dominio del servidor al que se conecta en una de las extensiones TLS. Cuando se hospedan varios servidores (virtuales) en la misma máquina, esta característica del protocolo TLS permite a los clientes distinguir a cuál de estos servidores se conectan y configurar las opciones de TLS, como el certificado de servidor, en consecuencia.

Cuando se realiza una solicitud HTTP mediante HttpClient , la implementación selecciona automáticamente un valor para la extensión de indicación de nombre de servidor (SNI) en función de la dirección URL a la que se conecta el cliente. Para escenarios que requieren un control más manual de la extensión, puede usar uno de los métodos siguientes.

Encabezado del host

El encabezado HTTP de host realiza una función similar a la extensión SNI en TLS. Permite que el servidor de destino distinga entre las solicitudes de varios nombres de host en una sola dirección IP. HttpClient rellena automáticamente el encabezado Host mediante el URI de solicitud. Sin embargo, también puede establecer su valor manualmente y HttpClient también usará el nuevo valor en la extensión SNI. Puede usar tanto HttpRequestMessage.Headers.Host como HttpClient.DefaultRequestHeaders.Host para lograr este efecto.

using HttpClient client = new();

client.DefaultRequestHeaders.Host = "www.microsoft.com";

using var response = await client.GetAsync("https://127.0.0.1:5001/");

System.Console.WriteLine(response);

Nota:

Este método no permite evitar el envío de SNI por completo al conectarse a una dirección URL con un nombre de host. Si el encabezado se establece en una cadena vacía, HttpClient usa el nombre de host de la dirección URL en su lugar.

Nota:

La personalización del encabezado Host afecta a la validación del certificado de servidor. De forma predeterminada, el cliente esperará que el certificado de servidor coincida con el nombre de host en el encabezado Host.

Autenticación manual de SslStream mediante ConnectCallback

Una opción más complicada, pero también más eficaz, es usar .SocketsHttpHandler.ConnectCallback Desde .NET 7, es posible devolver un elemento autenticado SslStream y, de esta forma, personalizar cómo se establece la conexión TLS. Dentro de la devolución de llamada, se pueden usar opciones arbitrarias SslClientAuthenticationOptions para realizar la autenticación del lado cliente.

var handler = new SocketsHttpHandler
{
    ConnectCallback = async (context, cancellationToken) =>
    {
        var socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
        try
        {
            await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);

            var sslStream = new SslStream(new NetworkStream(socket, ownsSocket: true));

            // When using HTTP/2, you must also keep in mind to set options like ApplicationProtocols
            await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
            {
                TargetHost = context.DnsEndPoint.Host,

            }, cancellationToken);

            return sslStream;
        }
        catch
        {
            socket.Dispose();
            throw;
        }
    }
};

using HttpClient client = new(handler);

using var response = await client.GetAsync("https://www.microsoft.com");

System.Console.WriteLine(response);