Skip to content

Instantly share code, notes, and snippets.

@Rugby-Ball
Last active December 18, 2024 22:44
Show Gist options
  • Select an option

  • Save Rugby-Ball/ba23a5f53e30a984600561db8761c707 to your computer and use it in GitHub Desktop.

Select an option

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