def _open_socket_for_connection_via_http_proxy(
    server_location: ServerNetworkLocation, network_timeout: int
) -> socket.socket:
    assert server_location.http_proxy_settings
    try:
        sock = socket.create_connection(
            (server_location.http_proxy_settings.hostname, server_location.http_proxy_settings.port),
            timeout=network_timeout,
        )

        # Send a CONNECT request with the host we want to tunnel to
        proxy_authorization_header = server_location.http_proxy_settings.proxy_authorization_header
        if proxy_authorization_header is None:
            sock.send(f"CONNECT {server_location.hostname}:{server_location.port} HTTP/1.1\r\n\r\n".encode("utf-8"))
        else:
            sock.send(
                (
                    f"CONNECT {server_location.hostname}:{server_location.port} HTTP/1.1\r\n"
                    f"Proxy-Authorization: Basic {proxy_authorization_header}\r\n\r\n"
                ).encode("utf-8")
            )
        http_response = HttpResponseParser.parse_from_socket(sock)
    except socket.timeout:
        raise _ConnectionToHttpProxyTimedOut()
    except ConnectionError:
        raise _HttpProxyRejectedConnection("The HTTP proxy rejected the connection")
    except socket.error:
        raise _ConnectionToHttpProxyFailed()

    # Check if the proxy was able to connect to the host
    if http_response.status != 200:
        raise _HttpProxyRejectedConnection("The HTTP proxy rejected the CONNECT request")

    return sock
Beispiel #2
0
def _retrieve_and_analyze_http_response(
        server_info: ServerConnectivityInfo) -> HttpHeadersScanResult:
    # Send HTTP requests until we no longer received an HTTP redirection, but allow only 4 redirections max
    redirections_count = 0
    next_location_path: Optional[str] = "/"
    while next_location_path and redirections_count < 4:
        ssl_connection = server_info.get_preconfigured_tls_connection()
        try:
            # Perform the TLS handshake
            ssl_connection.connect()

            # Send an HTTP GET request to the server
            ssl_connection.ssl_client.write(
                HttpRequestGenerator.get_request(
                    host=server_info.network_configuration.
                    tls_server_name_indication,
                    path=next_location_path))
            http_response = HttpResponseParser.parse_from_ssl_connection(
                ssl_connection.ssl_client)
        finally:
            ssl_connection.close()

        if http_response.version == 9:
            # HTTP 0.9 => Probably not an HTTP response
            raise ValueError("Server did not return an HTTP response")

        # Handle redirection if there is one
        next_location_path = _detect_http_redirection(
            http_response=http_response,
            server_host_name=server_info.network_configuration.
            tls_server_name_indication,
            server_port=server_info.server_location.port,
        )
        redirections_count += 1

    # Parse and return each header
    return HttpHeadersScanResult(
        strict_transport_security_header=_parse_hsts_header_from_http_response(
            http_response),
        public_key_pins_header=_parse_hpkp_header_from_http_response(
            http_response),
        public_key_pins_report_only_header=
        _parse_hpkp_report_only_header_from_http_response(http_response),
        expect_ct_header=_parse_expect_ct_header_from_http_response(
            http_response),
    )
def _retrieve_and_analyze_http_response(
        server_info: ServerConnectivityInfo) -> HttpHeadersScanResult:
    # Perform the TLS handshake
    ssl_connection = server_info.get_preconfigured_tls_connection()
    try:
        ssl_connection.connect()

        # Send an HTTP GET request to the server
        ssl_connection.ssl_client.write(
            HttpRequestGenerator.get_request(
                host=server_info.network_configuration.
                tls_server_name_indication))

        # We do not follow redirections because the security headers must be set on the first page according to
        # https://hstspreload.appspot.com/:
        # "If you are serving an additional redirect from your HTTPS site, that redirect must still have the HSTS
        # header (rather than the page it redirects to)."
        http_response = HttpResponseParser.parse_from_ssl_connection(
            ssl_connection.ssl_client)
    finally:
        ssl_connection.close()

    if http_response.version == 9:
        # HTTP 0.9 => Probably not an HTTP response
        raise ValueError("Server did not return an HTTP response")

    # Parse and return each header
    return HttpHeadersScanResult(
        strict_transport_security_header=_parse_hsts_header_from_http_response(
            http_response),
        public_key_pins_header=_parse_hpkp_header_from_http_response(
            http_response),
        public_key_pins_report_only_header=
        _parse_hpkp_report_only_header_from_http_response(http_response),
        expect_ct_header=_parse_expect_ct_header_from_http_response(
            http_response),
    )
Beispiel #4
0
def _retrieve_and_analyze_http_response(server_info: ServerConnectivityInfo) -> HttpHeadersScanResult:
    # Send HTTP requests until we no longer received an HTTP redirection, but allow only 4 redirections max
    _logger.info(f"Retrieving HTTP headers from {server_info}")
    redirections_count = 0
    next_location_path: Optional[str] = "/"
    http_error_trace = None

    while next_location_path and redirections_count < 4:
        _logger.info(f"Sending HTTP request to {next_location_path}")
        http_path_redirected_to = next_location_path

        # Perform the TLS handshake
        ssl_connection = server_info.get_preconfigured_tls_connection()
        ssl_connection.connect()

        try:
            # Send an HTTP GET request to the server
            ssl_connection.ssl_client.write(
                HttpRequestGenerator.get_request(
                    host=server_info.network_configuration.tls_server_name_indication, path=next_location_path
                )
            )
            http_response = HttpResponseParser.parse_from_ssl_connection(ssl_connection.ssl_client)

        except (OSError, NotAValidHttpResponseError, SslError) as e:
            # The server closed/rejected the connection, or didn't return a valid HTTP response
            http_error_trace = TracebackException.from_exception(e)

        finally:
            ssl_connection.close()

        if http_error_trace:
            break

        # Handle redirection if there is one
        next_location_path = _detect_http_redirection(
            http_response=http_response,
            server_host_name=server_info.network_configuration.tls_server_name_indication,
            server_port=server_info.server_location.port,
        )
        redirections_count += 1

    # Prepare the results
    initial_http_request = HttpRequestGenerator.get_request(
        host=server_info.network_configuration.tls_server_name_indication, path="/"
    ).decode("ascii")

    if http_error_trace:
        # If the server errored when receiving an HTTP request, return the error as the result
        return HttpHeadersScanResult(
            http_request_sent=initial_http_request,
            http_error_trace=http_error_trace,
            http_path_redirected_to=None,
            strict_transport_security_header=None,
            expect_ct_header=None,
        )
    else:
        # If no HTTP error happened, parse and return each header
        return HttpHeadersScanResult(
            http_request_sent=initial_http_request,
            http_path_redirected_to=http_path_redirected_to,
            http_error_trace=None,
            strict_transport_security_header=_parse_hsts_header_from_http_response(http_response),
            expect_ct_header=_parse_expect_ct_header_from_http_response(http_response),
        )