Last active
December 18, 2024 22:44
-
-
Save Rugby-Ball/ba23a5f53e30a984600561db8761c707 to your computer and use it in GitHub Desktop.
Function, enter in urls to check what TLS version is in use. Output to screen, and offer user to save as a text file. #Public #Audit #Utility #SSL_Certificate #Python #Security #TLS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import socket | |
| import ssl | |
| from datetime import datetime | |
| import sys | |
| import os | |
| from typing import Dict, Any | |
| from io import StringIO | |
| def check_ssl_tls_versions(url: str, port: int = 443) -> Dict[str, Any]: | |
| """ | |
| Function to check SSL/TLS versions supported by a given URL | |
| Args: | |
| url (str): Target URL to test | |
| port (int): Port number to connect to (default 443 for HTTPS) | |
| Returns: | |
| Dict containing test results and formatted output | |
| """ | |
| # Initialize string buffer to capture all output for potential file saving | |
| output_buffer = StringIO() | |
| try: | |
| # Clean and validate the URL | |
| hostname = ( | |
| url.replace("https://", "").replace("http://", "").split("/")[0].strip() | |
| ) | |
| if not hostname or "." not in hostname: | |
| raise ValueError(f"Invalid hostname format: {url}") | |
| # Initialize results dictionary with metadata and protocol structure | |
| results = { | |
| "hostname": hostname, | |
| "scan_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
| "protocols": { | |
| "TLS 1.0": {"enabled": False, "cipher": None}, | |
| "TLS 1.1": {"enabled": False, "cipher": None}, | |
| "TLS 1.2": {"enabled": False, "cipher": None}, | |
| "TLS 1.3": {"enabled": False, "cipher": None}, | |
| }, | |
| } | |
| # Define TLS versions to test with corresponding SSL library constants | |
| tls_versions = { | |
| "TLS 1.0": ssl.TLSVersion.TLSv1, | |
| "TLS 1.1": ssl.TLSVersion.TLSv1_1, | |
| "TLS 1.2": ssl.TLSVersion.TLSv1_2, | |
| "TLS 1.3": ssl.TLSVersion.TLSv1_3, | |
| } | |
| # Print and store header information | |
| output = f"\nTesting SSL/TLS versions for {hostname}\n" | |
| output += "-" * 50 + "\n" | |
| print(output) | |
| output_buffer.write(output) | |
| # Test each TLS version | |
| for tls_name, tls_version in tls_versions.items(): | |
| try: | |
| # Create SSL context with specific version constraints | |
| context = ssl.create_default_context() | |
| context.minimum_version = tls_version | |
| context.maximum_version = tls_version | |
| context.check_hostname = True | |
| # Attempt connection with timeout | |
| with socket.create_connection((hostname, port), timeout=10) as sock: | |
| with context.wrap_socket(sock, server_hostname=hostname) as ssock: | |
| cipher = ssock.cipher() | |
| # Store successful connection details | |
| results["protocols"][tls_name]["enabled"] = True | |
| results["protocols"][tls_name]["cipher"] = cipher[0] | |
| output = f"{tls_name}: Enabled (Cipher: {cipher[0]})\n" | |
| print(output, end="") | |
| output_buffer.write(output) | |
| except (ssl.SSLError, socket.error, Exception) as e: | |
| # Handle failed connection attempts | |
| output = f"{tls_name}: Disabled\n" | |
| print(output, end="") | |
| output_buffer.write(output) | |
| # Print and store security assessment header | |
| output = "\nSecurity Assessment:\n" | |
| output += "-" * 50 + "\n" | |
| print(output) | |
| output_buffer.write(output) | |
| # Perform security assessment checks and generate warnings | |
| if ( | |
| results["protocols"]["TLS 1.0"]["enabled"] | |
| or results["protocols"]["TLS 1.1"]["enabled"] | |
| ): | |
| output = "⚠️ WARNING: Older TLS versions (1.0/1.1) are enabled - this is a security risk\n" | |
| print(output, end="") | |
| output_buffer.write(output) | |
| if not results["protocols"]["TLS 1.2"]["enabled"]: | |
| output = "❌ CRITICAL: TLS 1.2 is not enabled - this is required for modern security\n" | |
| print(output, end="") | |
| output_buffer.write(output) | |
| if not results["protocols"]["TLS 1.3"]["enabled"]: | |
| output = "ℹ️ NOTICE: TLS 1.3 is not enabled - consider enabling for best security\n" | |
| print(output, end="") | |
| output_buffer.write(output) | |
| if results["protocols"]["TLS 1.2"]["enabled"] and not ( | |
| results["protocols"]["TLS 1.0"]["enabled"] | |
| or results["protocols"]["TLS 1.1"]["enabled"] | |
| ): | |
| output = "✅ GOOD: Only secure TLS versions are enabled\n" | |
| print(output, end="") | |
| output_buffer.write(output) | |
| # Store complete output text in results | |
| results["output_text"] = output_buffer.getvalue() | |
| return results | |
| except Exception as e: | |
| # Handle any unexpected errors | |
| output = f"Error: {str(e)}\n" | |
| print(output) | |
| output_buffer.write(output) | |
| results = {"error": str(e), "output_text": output_buffer.getvalue()} | |
| return results | |
| finally: | |
| # Ensure buffer is closed | |
| output_buffer.close() | |
| def save_to_file(content: str, directory: str, filename: str) -> bool: | |
| """ | |
| Save the test results to a file | |
| Args: | |
| content (str): Content to write to file | |
| directory (str): Directory path for file | |
| filename (str): Name of the file | |
| Returns: | |
| bool: True if save successful, False otherwise | |
| """ | |
| try: | |
| # Create directory structure if it doesn't exist | |
| os.makedirs(directory, exist_ok=True) | |
| # Ensure filename has .txt extension | |
| if not filename.endswith(".txt"): | |
| filename += ".txt" | |
| # Create full file path | |
| filepath = os.path.join(directory, filename) | |
| # Write content to file with UTF-8 encoding | |
| with open(filepath, "w", encoding="utf-8") as f: | |
| f.write(content) | |
| print(f"\nResults successfully saved to: {filepath}") | |
| return True | |
| except Exception as e: | |
| print(f"\nError saving file: {str(e)}") | |
| return False | |
| def main(): | |
| """ | |
| Main function to coordinate the SSL/TLS testing process | |
| Handles user input, runs tests, and manages file output | |
| """ | |
| # Initialize storage for all test results | |
| all_results = [] | |
| urls = [] | |
| try: | |
| # Get URLs either from command line arguments or user input | |
| if len(sys.argv) > 1: | |
| urls = sys.argv[1:] | |
| else: | |
| print("Enter URLs to check (separate multiple URLs with commas):") | |
| user_input = input().strip() | |
| if not user_input: | |
| raise ValueError("No URLs provided") | |
| urls = [url.strip() for url in user_input.split(",")] | |
| # Validate URLs | |
| urls = [url for url in urls if url.strip()] | |
| if not urls: | |
| raise ValueError("No valid URLs provided") | |
| # Process each URL and store results | |
| for url in urls: | |
| try: | |
| print(f"\nProcessing: {url}") | |
| print("=" * 60) | |
| result = check_ssl_tls_versions(url) | |
| all_results.append(result) | |
| except Exception as e: | |
| print(f"Error checking {url}: {str(e)}") | |
| finally: | |
| print("=" * 60) | |
| # Ask user about saving results to file | |
| save_prompt = input( | |
| "\nWould you like to save these results to a file? (yes/no): " | |
| ).lower() | |
| if save_prompt in ["yes", "y"]: | |
| # Get and validate directory path | |
| while True: | |
| directory = input( | |
| "Enter directory path to save the file (press Enter for current directory): " | |
| ).strip() | |
| if not directory: | |
| directory = os.getcwd() | |
| if os.path.exists(directory) or directory == os.getcwd(): | |
| break | |
| print("Directory does not exist. Please enter a valid path.") | |
| # Get and validate filename | |
| while True: | |
| filename = input("Enter filename (without extension): ").strip() | |
| if filename: | |
| break | |
| print("Please enter a valid filename.") | |
| # Prepare content for file | |
| content = f"SSL/TLS Test Results - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n" | |
| content += "=" * 60 + "\n\n" | |
| # Combine all results into single content string | |
| for result in all_results: | |
| if "output_text" in result: | |
| content += result["output_text"] | |
| content += "\n" + "=" * 60 + "\n" | |
| # Save results to file | |
| save_to_file(content, directory, filename) | |
| except KeyboardInterrupt: | |
| # Handle user interruption | |
| print("\nOperation cancelled by user") | |
| sys.exit(1) | |
| except Exception as e: | |
| # Handle unexpected errors | |
| print(f"Program error: {str(e)}") | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment