Udostępnij za pośrednictwem


Dostosowywanie SNI w żądaniach HTTP

Gdy klient i serwer negocjują połączenie HTTPS, najpierw należy ustanowić połączenie TLS. W ramach uzgadniania TLS klient wysyła nazwę domeny serwera, do którego się łączy, w jednym z rozszerzeń TLS. Jeśli na tej samej maszynie jest hostowanych wiele (wirtualnych) serwerów, ta funkcja protokołu TLS umożliwia klientom odróżnienie serwerów, z którymi się łączą, i skonfigurowanie ustawień protokołu TLS, takich jak certyfikat serwera, odpowiednio.

Po utworzeniu żądania HTTP przy użyciu HttpClient implementacja automatycznie wybiera wartość rozszerzenia oznaczania nazwy serwera (SNI) na podstawie adresu URL, z którym nawiązuje połączenie klient. W przypadku scenariuszy wymagających większej ręcznej kontroli nad rozszerzeniem można użyć jednego z następujących podejść.

Nagłówek hosta

Nagłówek HTTP hosta wykonuje podobną funkcję jak rozszerzenie SNI w protokole TLS. Dzięki temu serwer docelowy może rozróżniać żądania dla wielu nazw hostów na jednym adresie IP. HttpClient automatycznie wypełnia nagłówek Host przy użyciu identyfikatora URI żądania. Można jednak również ręcznie ustawić jej wartość, a HttpClient także użyć nowej wartości w rozszerzeniu SNI. Możesz użyć metody HttpRequestMessage.Headers.Host lub HttpClient.DefaultRequestHeaders.Host , aby osiągnąć ten efekt.

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);

Uwaga / Notatka

Ta metoda nie umożliwia całkowitego uniknięcia wysyłania SNI podczas nawiązywania połączenia z adresem URL zawierającym nazwę hosta. Jeśli nagłówek jest ustawiony na pusty ciąg, HttpClient zamiast tego użyje nazwy hosta z adresu URL.

Uwaga / Notatka

Dostosowywanie nagłówka hosta wpływa na walidację certyfikatu serwera. Domyślnie klient będzie oczekiwać, że certyfikat serwera będzie zgodny z nazwą hosta w nagłówku Host.

Ręczna autoryzacja SslStream za pomocą metody ConnectCallback

Bardziej skomplikowaną, ale również bardziej zaawansowaną opcją jest użycie elementu SocketsHttpHandler.ConnectCallback. Od wersji .NET 7, możliwe jest zwrócenie uwierzytelnionego elementu SslStream, co umożliwia dostosowanie sposobu nawiązywania połączenia TLS. Wewnątrz wywołania zwrotnego można użyć dowolnych SslClientAuthenticationOptions opcji do przeprowadzenia uwierzytelniania po stronie klienta.

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);