#!/usr/bin/env python3 """ Security Headers Analysis Tool for Project Starlight Analyzes HTTP security headers and provides recommendations """ import json import re import base64 import hashlib import datetime from typing import Dict, List, Optional, Any class SecurityHeadersAnalyzer: """Analyzes security headers for web applications.""" def __init__(self): self.security_headers = { 'strict-transport-security': { 'description': 'HTTP Strict Transport Security', 'recommended': 'max-age=31536000; includeSubDomains; preload', 'critical': True }, 'content-security-policy': { 'description': 'Content Security Policy', 'recommended': "default-src 'self'", 'critical': True }, 'x-frame-options': { 'description': 'X-Frame Options', 'recommended': 'DENY', 'critical': True }, 'x-content-type-options': { 'description': 'X-Content-Type-Options', 'recommended': 'nosniff', 'critical': True }, 'referrer-policy': { 'description': 'Referrer Policy', 'recommended': 'strict-origin-when-cross-origin', 'critical': False }, 'permissions-policy': { 'description': 'Permissions Policy', 'recommended': 'camera=(), microphone=(), geolocation=()', 'critical': False } } def analyze_headers(self, headers: Dict[str, str]) -> Dict[str, Any]: """Analyze security headers and return assessment.""" results = { 'timestamp': datetime.datetime.now().isoformat(), 'analysis': {}, 'score': 0, 'critical_missing': [], 'recommendations': [] } total_headers = len(self.security_headers) present_headers = 0 critical_headers_present = 0 for header_name, header_info in self.security_headers.items(): header_lower = header_name.lower() found_header = None # Find header case-insensitively for key in headers: if key.lower() == header_lower: found_header = headers[key] break if found_header: present_headers += 1 analysis = self._analyze_header_value(header_name, found_header) results['analysis'][header_name] = analysis if header_info['critical'] and analysis.get('secure', False): critical_headers_present += 1 else: analysis = { 'present': False, 'secure': False, 'value': None, 'issues': [f"Missing {header_info['description']} header"] } results['analysis'][header_name] = analysis if header_info['critical']: results['critical_missing'].append(header_name) results['recommendations'].append({ 'header': header_name, 'severity': 'high' if header_info['critical'] else 'medium', 'message': f"Add {header_info['description']}: {header_info['recommended']}" }) # Calculate security score results['score'] = int((present_headers / total_headers) * 100) # Add overall recommendations if results['critical_missing']: results['recommendations'].insert(0, { 'header': 'overall', 'severity': 'critical', 'message': f"Critical headers missing: {', '.join(results['critical_missing'])}" }) return results def _analyze_header_value(self, header_name: str, value: str) -> Dict[str, Any]: """Analyze individual header value.""" analysis = { 'present': True, 'value': value, 'secure': False, 'issues': [] } header_info = self.security_headers.get(header_name.lower(), {}) if header_name.lower() == 'strict-transport-security': analysis.update(self._analyze_hsts(value)) elif header_name.lower() == 'content-security-policy': analysis.update(self._analyze_csp(value)) elif header_name.lower() == 'x-frame-options': analysis.update(self._analyze_x_frame_options(value)) elif header_name.lower() == 'x-content-type-options': analysis.update(self._analyze_x_content_type_options(value)) elif header_name.lower() == 'referrer-policy': analysis.update(self._analyze_referrer_policy(value)) return analysis def _analyze_hsts(self, value: str) -> Dict[str, Any]: """Analyze HSTS header.""" result = {'secure': False, 'issues': []} if 'max-age=' not in value: result['issues'].append("HSTS missing max-age directive") else: max_age_match = re.search(r'max-age=(\d+)', value) if max_age_match: max_age = int(max_age_match.group(1)) if max_age < 31536000: # 1 year result['issues'].append("HSTS max-age should be at least 1 year (31536000 seconds)") else: result['secure'] = True if 'includeSubDomains' not in value: result['issues'].append("Consider adding includeSubDomains to HSTS") if 'preload' not in value: result['issues'].append("Consider adding preload to HSTS for browser inclusion") return result def _analyze_csp(self, value: str) -> Dict[str, Any]: """Analyze Content Security Policy.""" result = {'secure': False, 'issues': []} dangerous_sources = ["'unsafe-inline'", "'unsafe-eval'", "*"] for dangerous in dangerous_sources: if dangerous in value: result['issues'].append(f"CSP contains dangerous directive: {dangerous}") if "default-src 'self'" in value or "default-src 'none'" in value: result['secure'] = True elif 'default-src' not in value: result['issues'].append("CSP should include default-src directive") return result def _analyze_x_frame_options(self, value: str) -> Dict[str, Any]: """Analyze X-Frame-Options header.""" result = {'secure': False, 'issues': []} if value.upper() in ['DENY', 'SAMEORIGIN']: result['secure'] = True else: result['issues'].append("X-Frame-Options should be 'DENY' or 'SAMEORIGIN'") return result def _analyze_x_content_type_options(self, value: str) -> Dict[str, Any]: """Analyze X-Content-Type-Options header.""" result = {'secure': False, 'issues': []} if value.lower() == 'nosniff': result['secure'] = True else: result['issues'].append("X-Content-Type-Options should be 'nosniff'") return result def _analyze_referrer_policy(self, value: str) -> Dict[str, Any]: """Analyze Referrer-Policy header.""" result = {'secure': False, 'issues': []} secure_policies = [ 'no-referrer', 'no-referrer-when-downgrade', 'strict-origin', 'strict-origin-when-cross-origin' ] if value in secure_policies: result['secure'] = True else: result['issues'].append("Consider using a stricter referrer policy") return result def generate_test_headers(self) -> Dict[str, str]: """Generate sample headers for testing.""" return { 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload', 'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'", 'X-Frame-Options': 'DENY', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'strict-origin-when-cross-origin', 'Permissions-Policy': 'camera=(), microphone=(), geolocation=()' } def main(): """Test the security headers analyzer.""" analyzer = SecurityHeadersAnalyzer() # Test with sample headers test_headers = analyzer.generate_test_headers() results = analyzer.analyze_headers(test_headers) print("Security Headers Analysis Results:") print(json.dumps(results, indent=2)) return results def test_comprehensive(): """Comprehensive test of security headers analysis""" analyzer = SecurityHeadersAnalyzer() # Test cases test_cases = [ { 'name': 'Secure Headers', 'headers': analyzer.generate_test_headers() }, { 'name': 'Missing Critical Headers', 'headers': {'X-Frame-Options': 'DENY'} }, { 'name': 'Insecure Configuration', 'headers': { 'Content-Security-Policy': "default-src * 'unsafe-inline'", 'X-Frame-Options': 'ALLOWALL' } } ] results = [] for test_case in test_cases: analysis = analyzer.analyze_headers(test_case['headers']) results.append({ 'test_name': test_case['name'], 'score': analysis['score'], 'critical_missing': analysis['critical_missing'], 'recommendations_count': len(analysis['recommendations']) }) return { 'test_completed': True, 'total_tests': len(test_cases), 'results': results } if __name__ == "__main__": main() test_result = test_comprehensive() print("\nComprehensive Test Results:") print(json.dumps(test_result, indent=2))