A Guide to Checking Website Security Headers Using Python


 

Ensuring your website is secure is a top priority, especially in today's cyber environment. One way to achieve that is by configuring security headers that safeguard your web applications against common attacks. These headers act as directives from your server to the browser, telling it how to behave while interacting with your site.

In this blog post, we’ll walk through a simple Python script using the requests, colorama, and tabulate libraries to check the security headers of a website. This script will help you ensure your site follows best practices and identifies potential vulnerabilities.

Why Security Headers Matter

Security headers are critical in preventing attacks like cross-site scripting (XSS), clickjacking, MIME sniffing, and more. Here are some of the most important ones:

  • Content-Security-Policy (CSP): Restricts sources of content such as scripts and images.
  • Strict-Transport-Security (HSTS): Enforces HTTPS connections, preventing users from accidentally accessing the website through insecure HTTP.
  • X-Content-Type-Options: Stops browsers from trying to guess the MIME type of a file.
  • X-Frame-Options: Prevents your website from being embedded in iframes, protecting against clickjacking.
  • X-XSS-Protection: Enables the browser's XSS filter.
  • Referrer-Policy: Controls how much information about the previous page is shared when a user navigates away from your site.
  • Permissions-Policy: Manages access to browser features such as the camera or geolocation.


Setting Up the Environment

To get started, you'll need to install a few Python libraries. Open your terminal or command prompt and run:


pip install requests colorama tabulate

Overview of the Code

This Python script will check a target website’s security headers and flag them as either present, missing, or misconfigured. Here's a breakdown of how it works.

Importing the Required Libraries

We’re using three libraries:

  1. Requests: To fetch the HTTP response from the website.
  2. Colorama: To add colors to our terminal output, making it easier to spot issues.
  3. Tabulate: To display the results in a clean, table-like format.

import requests from requests.exceptions import RequestException from colorama import init, Fore, Style from tabulate import tabulate import re # Initialize colorama init(autoreset=True)

Defining Security Headers

We’ve pre-defined a dictionary of headers and their descriptions:


SECURITY_HEADERS = { "Content-Security-Policy": "Defines approved sources of content.", "Strict-Transport-Security": "Enforces secure (HTTP over SSL/TLS) connections to the server.", "X-Content-Type-Options": "Prevents MIME type sniffing.", "X-Frame-Options": "Controls whether the site can be framed.", "X-XSS-Protection": "Enables cross-site scripting filters.", "Referrer-Policy": "Controls how much referrer information is included with requests.", "Permissions-Policy": "Manages browser features and APIs permissions." }

Checking the HSTS Configuration

For Strict-Transport-Security, the script checks if the HSTS (HTTP Strict Transport Security) header is properly configured. This header helps protect against man-in-the-middle attacks by enforcing secure HTTPS connections.


def check_hsts(header_value): hsts_recommendations = { "max-age": "31536000", # 1 year in seconds "includeSubDomains": "present", "preload": "present" } # Parse the HSTS header directives = dict( item.strip().split('=') if '=' in item else (item.strip(), True) for item in header_value.split(';') )

Coloring the Output

Using colorama, we can color-code the results: green for headers that are correctly configured and red for those that are missing or misconfigured.


def color_text(text, condition): if condition: return Fore.GREEN + text + Style.RESET_ALL else: return Fore.RED + text + Style.RESET_ALL

Fetching the Website's Security Headers

The main function sends a GET request to the website and examines the response headers:


def check_security_headers(domain): if not re.match(r'^https?://', domain): domain = "https://" + domain try: response = requests.get(domain, timeout=10, verify=True) except RequestException as e: print(Fore.RED + f"Error accessing {domain}: {e}" + Style.RESET_ALL) return headers = response.headers table = [] for header, description in SECURITY_HEADERS.items(): header_value = headers.get(header)

Displaying Results in a Table

Finally, the results are displayed in a neat, tabulated format, making it easy to understand whether each security header is properly configured.



print(tabulate(table, headers=["Header", "Status", "Details"], tablefmt="fancy_grid"))

Running the Script

To run the script, simply provide the domain of the website you wish to analyze:


if __name__ == "__main__": domain = input("Enter the website domain (e.g., example.com): ").strip() check_security_headers(domain)

 

Full Script

#pip install requests colorama tabulate


import requests
from requests.exceptions import RequestException
from colorama import init, Fore, Style
from tabulate import tabulate
import re

# Initialize colorama
init(autoreset=True)



# Define security headers and their descriptions
SECURITY_HEADERS = {
    "Content-Security-Policy": "Defines approved sources of content.",
    "Strict-Transport-Security": "Enforces secure (HTTP over SSL/TLS) connections to the server.",
    "X-Content-Type-Options": "Prevents MIME type sniffing.",
    "X-Frame-Options": "Controls whether the site can be framed.",
    "X-XSS-Protection": "Enables cross-site scripting filters.",
    "Referrer-Policy": "Controls how much referrer information is included with requests.",
    "Permissions-Policy": "Manages browser features and APIs permissions."
}

def check_hsts(header_value):
    """
    Checks the HSTS header for recommended settings.
    """
    hsts_recommendations = {
        "max-age": "31536000",  # 1 year in seconds
        "includeSubDomains": "present",
        "preload": "present"
    }

    # Parse the HSTS header
    directives = dict(
        item.strip().split('=') if '=' in item else (item.strip(), True)
        for item in header_value.split(';')
    )

    results = {}
    # Check max-age
    max_age = directives.get("max-age")
    if max_age and int(max_age) >= int(hsts_recommendations["max-age"]):
        results["max-age"] = True
    else:
        results["max-age"] = False

    # Check includeSubDomains
    results["includeSubDomains"] = "includeSubDomains" in directives

    # Check preload
    results["preload"] = "preload" in directives

    return results

def color_text(text, condition):
    """
    Returns the text string wrapped in color codes based on the condition.
    """
    if condition:
        return Fore.GREEN + text + Style.RESET_ALL
    else:
        return Fore.RED + text + Style.RESET_ALL

def check_security_headers(domain):
    # Ensure the domain starts with http:// or https://
    if not re.match(r'^https?://', domain):
        domain = "https://" + domain

    try:
        # Send a GET request with a reasonable timeout
        response = requests.get(domain, timeout=10, verify=True)
    except RequestException as e:
        print(Fore.RED + f"Error accessing {domain}: {e}" + Style.RESET_ALL)
        return

    headers = response.headers

    table = []
    for header, description in SECURITY_HEADERS.items():
        header_value = headers.get(header)
        if header_value:
            if header == "Strict-Transport-Security":
                hsts_results = check_hsts(header_value)
                # Determine if all HSTS recommendations are met
                if all(hsts_results.values()):
                    status = color_text("Configured", True)
                else:
                    status = color_text("Misconfigured", False)
                details = f"max-age: {hsts_results['max-age']}, includeSubDomains: {hsts_results['includeSubDomains']}, preload: {hsts_results['preload']}"
            else:
                status = color_text("Present", True)
                details = header_value
        else:
            status = color_text("Missing", False)
            details = "N/A"

        table.append([header, status, details])

    # Display the results in a table
    print(f"\nSecurity Header Analysis for {domain}:\n")
    print(tabulate(table, headers=["Header", "Status", "Details"], tablefmt="fancy_grid"))

if __name__ == "__main__":
    domain = input("Enter the website domain (e.g., example.com): ").strip()
    check_security_headers(domain)

Conclusion

This Python script provides a quick and easy way to audit the security headers of any website. Properly configured security headers are essential in minimizing the risk of attacks and vulnerabilities. By using the requests, colorama, and tabulate libraries, you can automate this audit and ensure your website is aligned with security best practices.

Try running the script on your own website and see how secure your headers are!

Post a Comment

0 Comments