def do_pre_handshake(self, network_timeout): """Open a socket to the server; setup HTTP tunneling if a proxy was configured. """ if self._tunnel_host: # Proxy configured; setup HTTP tunneling try: self._sock = socket.create_connection( (self._tunnel_host, self._tunnel_port), network_timeout) except socket.timeout as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(e[0])) except socket.error as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(e[1])) # Send a CONNECT request with the host we want to tunnel to if self._tunnel_basic_auth_token is None: self._sock.send( self.HTTP_CONNECT_REQ.format(self._host, self._port)) else: self._sock.send( self.HTTP_CONNECT_REQ_PROXY_AUTH_BASIC.format( self._host, self._port, self._tunnel_basic_auth_token)) http_response = HttpResponseParser.parse(self._sock) # Check if the proxy was able to connect to the host if http_response.status != 200: raise ProxyError(self.ERR_CONNECT_REJECTED) else: # No proxy; connect directly to the server self._sock = socket.create_connection((self._ip, self._port), network_timeout)
def _get_security_headers(cls, server_info): hpkp_report_only = False # Perform the SSL handshake ssl_connection = server_info.get_preconfigured_ssl_connection() ssl_connection.connect() certificate_chain = ssl_connection.get_peer_cert_chain() # Send an HTTP GET request to the server ssl_connection.write( HttpRequestGenerator.get_request(host=server_info.hostname)) http_resp = HttpResponseParser.parse(ssl_connection) ssl_connection.close() if http_resp.version == 9: # HTTP 0.9 => Probably not an HTTP response raise ValueError('Server did not return an HTTP response') else: hsts_header = http_resp.getheader('strict-transport-security', None) hpkp_header = http_resp.getheader('public-key-pins', None) if hpkp_header is None: hpkp_report_only = True hpkp_header = http_resp.getheader( 'public-key-pins-report-only', None) # 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)." return hsts_header, hpkp_header, hpkp_report_only, certificate_chain
def _get_security_headers(cls, server_info): hpkp_report_only = False # Perform the SSL handshake ssl_connection = server_info.get_preconfigured_ssl_connection() ssl_connection.connect() certificate_chain = ssl_connection.get_peer_cert_chain() # Send an HTTP GET request to the server ssl_connection.write(HttpRequestGenerator.get_request(host=server_info.hostname)) http_resp = HttpResponseParser.parse(ssl_connection) ssl_connection.close() if http_resp.version == 9: # HTTP 0.9 => Probably not an HTTP response raise ValueError("Server did not return an HTTP response") else: hsts_header = http_resp.getheader("strict-transport-security", None) hpkp_header = http_resp.getheader("public-key-pins", None) if hpkp_header is None: hpkp_report_only = True hpkp_header = http_resp.getheader("public-key-pins-report-only", None) # 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)." return hsts_header, hpkp_header, hpkp_report_only, certificate_chain
def create_connection(self, timeout: int) -> socket.SocketType: """Setup HTTP tunneling with the configured proxy. """ # Setup HTTP tunneling try: sock = socket.create_connection((self._tunnel_host, self._tunnel_port), timeout=timeout) except socket.timeout as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) except socket.error as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) # Send a CONNECT request with the host we want to tunnel to if self._tunnel_basic_auth_token is None: sock.send(self.HTTP_CONNECT_REQ.format(self._server_host, self._server_port).encode('utf-8')) else: sock.send(self.HTTP_CONNECT_REQ_PROXY_AUTH_BASIC.format( self._server_host, self._server_port, self._tunnel_basic_auth_token ).encode('utf-8')) http_response = HttpResponseParser.parse_from_socket(sock) # Check if the proxy was able to connect to the host if http_response.status != 200: raise ProxyError(self.ERR_CONNECT_REJECTED) return sock
def post_handshake_check(self): try: # TODO: This is code only used by OpenSSLCipherSuitesPlugin anf should be moved there # Send an HTTP GET to the server and store the HTTP Status Code self.write(HttpRequestGenerator.get_request(self._host)) # Parse the response and print the Location header http_response = HttpResponseParser.parse(self) if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response result = self.ERR_NOT_HTTP else: redirect = '' if 300 <= http_response.status < 400: if http_response.getheader('Location', None): # Add redirection URL to the result redirect = ' - ' + http_response.getheader( 'Location', None) result = self.GET_RESULT_FORMAT.format(http_response.status, http_response.reason, redirect) except socket.timeout: result = self.ERR_HTTP_TIMEOUT except IOError: result = self.ERR_GENERIC return result
def create_connection(self, timeout: int) -> socket.SocketType: """Setup HTTP tunneling with the configured proxy. """ # Setup HTTP tunneling try: sock = socket.create_connection( (self._tunnel_host, self._tunnel_port), timeout=timeout) except socket.timeout as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) except socket.error as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) # Send a CONNECT request with the host we want to tunnel to if self._tunnel_basic_auth_token is None: sock.send( self.HTTP_CONNECT_REQ.format( self._server_host, self._server_port).encode("utf-8")) else: sock.send( self.HTTP_CONNECT_REQ_PROXY_AUTH_BASIC.format( self._server_host, self._server_port, self._tunnel_basic_auth_token).encode("utf-8")) http_response = HttpResponseParser.parse_from_socket(sock) # Check if the proxy was able to connect to the host if http_response.status != 200: raise ProxyError(self.ERR_CONNECT_REJECTED) return sock
def send_request(self, ssl_client: SslClient) -> str: """Send an HTTP GET to the server and return the HTTP status code. """ try: ssl_client.write(HttpRequestGenerator.get_request(self._hostname)) # Parse the response and print the Location header http_response = HttpResponseParser.parse_from_ssl_connection(ssl_client) if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response result = self.ERR_NOT_HTTP else: redirect = '' if 300 <= http_response.status < 400: redirect_location = http_response.getheader('Location') if redirect_location: # Add redirection URL to the result redirect = f' - {redirect_location}' result = self.GET_RESULT_FORMAT.format(http_response.status, http_response.reason, redirect) except socket.timeout: result = self.ERR_HTTP_TIMEOUT except IOError: result = self.ERR_GENERIC return result
def do_pre_handshake(self, network_timeout): # type: (int) -> socket """Open a socket to the server; setup HTTP tunneling if a proxy was configured. """ if self._tunnel_host: # Proxy configured; setup HTTP tunneling try: sock = socket.create_connection((self._tunnel_host, self._tunnel_port), network_timeout) except socket.timeout as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) except socket.error as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) # Send a CONNECT request with the host we want to tunnel to if self._tunnel_basic_auth_token is None: sock.send(self.HTTP_CONNECT_REQ.format(self._hostname, self._port).encode('utf-8')) else: sock.send(self.HTTP_CONNECT_REQ_PROXY_AUTH_BASIC.format(self._hostname, self._port, self._tunnel_basic_auth_token).encode('utf-8')) http_response = HttpResponseParser.parse(sock) # Check if the proxy was able to connect to the host if http_response.status != 200: raise ProxyError(self.ERR_CONNECT_REJECTED) else: # No proxy; connect directly to the server sock = socket.create_connection(address=(self._ip_address, self._port), timeout=network_timeout) # Pass the connected socket to the SSL client self.ssl_client.set_underlying_socket(sock) return sock
def post_handshake_check(self): # type: () -> Text try: # Send an HTTP GET to the server and store the HTTP Status Code self.write(HttpRequestGenerator.get_request(self._hostname)) # Parse the response and print the Location header http_response = HttpResponseParser.parse(self) if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response result = self.ERR_NOT_HTTP else: redirect = '' if 300 <= http_response.status < 400: if http_response.getheader('Location', None): # Add redirection URL to the result redirect = ' - ' + http_response.getheader('Location', None) result = self.GET_RESULT_FORMAT.format(http_response.status, http_response.reason, redirect) except socket.timeout: result = self.ERR_HTTP_TIMEOUT except IOError: result = self.ERR_GENERIC return result
def post_handshake_check(self): try: # TODO: This is code only used by OpenSSLCipherSuitesPlugin anf should be moved there # Send an HTTP GET to the server and store the HTTP Status Code self.write(HttpRequestGenerator.get_request(self._host)) # Parse the response and print the Location header http_response = HttpResponseParser.parse(self) if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response result = self.ERR_NOT_HTTP else: redirect = "" if 300 <= http_response.status < 400: if http_response.getheader("Location", None): # Add redirection URL to the result redirect = " - " + http_response.getheader("Location", None) result = self.GET_RESULT_FORMAT.format(http_response.status, http_response.reason, redirect) except socket.timeout: result = self.ERR_HTTP_TIMEOUT except IOError: result = self.ERR_GENERIC return result
def do_pre_handshake(self, network_timeout): """Open a socket to the server; setup HTTP tunneling if a proxy was configured. """ if self._tunnel_host: # Proxy configured; setup HTTP tunneling try: self._sock = socket.create_connection((self._tunnel_host, self._tunnel_port), network_timeout) except socket.timeout as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(e[0])) except socket.error as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(e[1])) # Send a CONNECT request with the host we want to tunnel to if self._tunnel_basic_auth_token is None: self._sock.send(self.HTTP_CONNECT_REQ.format(self._host, self._port)) else: self._sock.send( self.HTTP_CONNECT_REQ_PROXY_AUTH_BASIC.format(self._host, self._port, self._tunnel_basic_auth_token) ) http_response = HttpResponseParser.parse(self._sock) # Check if the proxy was able to connect to the host if http_response.status != 200: raise ProxyError(self.ERR_CONNECT_REJECTED) else: # No proxy; connect directly to the server self._sock = socket.create_connection((self._ip, self._port), network_timeout)
def process_task( self, server_info: ServerConnectivityInfo, scan_command: PluginScanCommand ) -> 'HttpHeadersScanResult': if not isinstance(scan_command, HttpHeadersScanCommand): raise ValueError('Unexpected scan command') if server_info.tls_wrapped_protocol not in [TlsWrappedProtocolEnum.PLAIN_TLS, TlsWrappedProtocolEnum.HTTPS]: raise ValueError('Cannot test for HTTP headers on a StartTLS connection.') # Perform the SSL handshake mozilla_store = TrustStoresRepository.get_default().get_main_store() ssl_connection = server_info.get_preconfigured_ssl_connection(ssl_verify_locations=mozilla_store.path) try: ssl_connection.connect() try: verified_chain_as_pem = ssl_connection.ssl_client.get_verified_chain() except CouldNotBuildVerifiedChain: verified_chain_as_pem = None # Send an HTTP GET request to the server ssl_connection.ssl_client.write(HttpRequestGenerator.get_request(host=server_info.hostname)) # 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 the certificate chain verified_chain = [ load_pem_x509_certificate(cert_as_pem.encode('ascii'), backend=default_backend()) for cert_as_pem in verified_chain_as_pem ] if verified_chain_as_pem else None # Parse each header hsts_header = StrictTransportSecurityHeader.from_http_response(http_response) expect_ct_header = ExpectCtHeader.from_http_response(http_response) hpkp_header = PublicKeyPinsHeader.from_http_response(http_response) hpkp_report_only_header = PublicKeyPinsReportOnlyHeader.from_http_response(http_response) return HttpHeadersScanResult( server_info, scan_command, hsts_header, hpkp_header, hpkp_report_only_header, expect_ct_header, verified_chain )
def _get_security_headers( cls, server_info # type: ServerConnectivityInfo ): # type: (...) -> Tuple[Optional[Text], Optional[Text], Optional[Text], bool, List[Certificate]] hpkp_report_only = False # Perform the SSL handshake ssl_connection = server_info.get_preconfigured_ssl_connection() try: ssl_connection.connect() certificate_chain = [ load_pem_x509_certificate(x509_cert.as_pem().encode('ascii'), backend=default_backend()) for x509_cert in ssl_connection.ssl_client.get_peer_cert_chain() ] # Send an HTTP GET request to the server ssl_connection.write( HttpRequestGenerator.get_request(host=server_info.hostname)) http_resp = HttpResponseParser.parse_from_ssl_connection( ssl_connection) finally: ssl_connection.close() if http_resp.version == 9: # HTTP 0.9 => Probably not an HTTP response raise ValueError('Server did not return an HTTP response') else: hsts_header = http_resp.getheader('strict-transport-security', None) hpkp_header = http_resp.getheader('public-key-pins', None) expect_ct_header = http_resp.getheader('expect-ct', None) if hpkp_header is None: hpkp_report_only = True hpkp_header = http_resp.getheader( 'public-key-pins-report-only', None) # 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)." return hsts_header, hpkp_header, expect_ct_header, hpkp_report_only, certificate_chain
def _get_security_headers( cls, server_info: ServerConnectivityInfo ) -> Tuple[Optional[str], Optional[str], Optional[str], bool, List[Certificate]]: hpkp_report_only = False # Perform the SSL handshake ssl_connection = server_info.get_preconfigured_ssl_connection() try: ssl_connection.connect() certificate_chain = [ load_pem_x509_certificate(x509_cert.as_pem().encode('ascii'), backend=default_backend()) for x509_cert in ssl_connection.ssl_client.get_peer_cert_chain() ] # Send an HTTP GET request to the server ssl_connection.ssl_client.write(HttpRequestGenerator.get_request(host=server_info.hostname)) http_resp = HttpResponseParser.parse_from_ssl_connection(ssl_connection.ssl_client) finally: ssl_connection.close() if http_resp.version == 9: # HTTP 0.9 => Probably not an HTTP response raise ValueError('Server did not return an HTTP response') else: hsts_header = http_resp.getheader('strict-transport-security', None) hpkp_header = http_resp.getheader('public-key-pins', None) expect_ct_header = http_resp.getheader('expect-ct', None) if hpkp_header is None: hpkp_report_only = True hpkp_header = http_resp.getheader('public-key-pins-report-only', None) # 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)." return hsts_header, hpkp_header, expect_ct_header, hpkp_report_only, certificate_chain
def process_task( self, server_info: ServerConnectivityInfo, scan_command: PluginScanCommand) -> "HttpHeadersScanResult": if not isinstance(scan_command, HttpHeadersScanCommand): raise ValueError("Unexpected scan command") if server_info.tls_wrapped_protocol not in [ TlsWrappedProtocolEnum.PLAIN_TLS, TlsWrappedProtocolEnum.HTTPS ]: raise ValueError( "Cannot test for HTTP headers on a StartTLS connection.") # Perform the SSL handshake mozilla_store = TrustStoresRepository.get_default().get_main_store() ssl_connection = server_info.get_preconfigured_ssl_connection( ssl_verify_locations=mozilla_store.path) try: ssl_connection.connect() try: verified_chain_as_pem = ssl_connection.ssl_client.get_verified_chain( ) except CouldNotBuildVerifiedChain: verified_chain_as_pem = None # Send an HTTP GET request to the server ssl_connection.ssl_client.write( HttpRequestGenerator.get_request(host=server_info.hostname)) # 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 the certificate chain verified_chain = ([ load_pem_x509_certificate(cert_as_pem.encode("ascii"), backend=default_backend()) for cert_as_pem in verified_chain_as_pem ] if verified_chain_as_pem else None) # Parse each header hsts_header = StrictTransportSecurityHeader.from_http_response( http_response) expect_ct_header = ExpectCtHeader.from_http_response(http_response) hpkp_header = PublicKeyPinsHeader.from_http_response(http_response) hpkp_report_only_header = PublicKeyPinsReportOnlyHeader.from_http_response( http_response) return HttpHeadersScanResult( server_info, scan_command, hsts_header, hpkp_header, hpkp_report_only_header, expect_ct_header, verified_chain, )
def test_endpoint_for_secure_headers(httpEndpoint): # Setup the server to scan and ensure it is online/reachable hostname = httpEndpoint.get_endpoint_url() try: server_info = ServerConnectivityInfo(hostname=hostname) server_info.test_connectivity_to_server() except ServerConnectivityError as e: # Could not establish an SSL connection to the server raise RuntimeError('Error when connecting to {}: {}'.format( hostname, e.error_msg)) hpkp_report_only = False # Perform the SSL handshake ssl_connection = server_info.get_preconfigured_ssl_connection() ssl_connection.connect() certificate_chain = [ cryptography.x509.load_pem_x509_certificate( x509_cert.as_pem().encode('ascii'), backend=default_backend()) for x509_cert in ssl_connection.ssl_client.get_peer_cert_chain() ] # Send an HTTP GET request to the server ssl_connection.write( HttpRequestGenerator.get_request(host=server_info.hostname)) http_resp = HttpResponseParser.parse(ssl_connection) ssl_connection.close() if http_resp.version == 9: # HTTP 0.9 => Probably not an HTTP response raise ValueError('Server did not return an HTTP response') else: hsts_header = http_resp.getheader('strict-transport-security', None) x_frame_options_header = http_resp.getheader('x-frame-options', None) x_xss_protection_header = http_resp.getheader('x-xss-protection', None) content_security_policy_header = http_resp.getheader( 'Content-Security-Policy', None) content_security_policy_report_only_header = http_resp.getheader( 'content-security-policy-report-only', None) hpkp_header = http_resp.getheader('public-key-pins', None) expect_ct_header = http_resp.getheader('expect-ct', None) if hpkp_header is None: hpkp_report_only = True hpkp_header = http_resp.getheader('public-key-pins-report-only', None) # 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)." if hsts_header is not None: httpEndpoint.set_hsts_enabled(True) if "includeSubDomains".lower() not in hsts_header.lower(): httpEndpoint.set_hsts_issues("includeSubDomains is not set.") if "max-age".lower() not in hsts_header.lower(): issueValue = httpEndpoint.get_hsts_issues() issueValue += "max-age is not set." httpEndpoint.set_hsts_issues(issueValue) if x_xss_protection_header is not None: httpEndpoint.set_x_xss_protection_enabled(True) if "mode=block".lower() not in x_xss_protection_header.lower(): httpEndpoint.set_x_xss_protection_issues("mode=block is not set.") if x_frame_options_header is not None: httpEndpoint.set_x_frame_options_enabled(True) if "SAMEORIGIN".lower() not in x_frame_options_header.lower(): httpEndpoint.set_x_frame_options_issues( "SAMEORIGIN value is not set") if content_security_policy_header is not None: httpEndpoint.set_csp_enabled(True) #print(hsts_header) #print(x_frame_options_header) #print(x_xss_protection_header) print(content_security_policy_header) #print(content_security_policy_report_only_header) #print(http_resp.getheaders()) return httpEndpoint