""" Name: Security Headers Nmap Parser Version: 0.20 Date: 5/30/2014 Author: Smeege Contact: SmeegeSec@gmail.com Description: Security-Headers-Nmap-Parser.py is a python script written to parse and display the results of nmap .xml output files. If the '--script=http-headers' argument is specifed an html report will be generated with each row being an individual ip:port and which security headers it responded with. The motivation behind this script was to provide a clean report and clear look at which assets in an environment respond with which security headers. Logic: access-control-allow-origin: * #bad, allows cross-site requests from any domain. null or specify domain is good. content-security-policy #good, define scripts,media,stylesheets,etc. that can run. whitelist resources. x-permitted-cross-domain-policies #good, specify which policy files to follow (http://www.adobe.com/devnet-docs/acrobatetk/tools/AppSec/CrossDomain_PolicyFile_Specification.pdf) x-content-type-options #good, reject responses with incorrect MIME types. MIME types must match script and stylesheet resources. server #bad, dont need to expose server information strict-transport-security #good, specify browsers should request https version of content x-frame-options #good, disallow framing by other sites. three options: deny, sameorigin, allow-from x-powered-by #bad, dont need to expose software information x-xss-protection: 0 #bad, protection disabled x-xss-protection: 1 #good, modifies response to break up potential script attacks x-xss-protection: 1; mode=block #good, prevents whole page from rendering if potential attack is detected """ import argparse import os import sys import re from xml.dom import minidom parser = argparse.ArgumentParser(prog='Security-Headers-Nmap-Parser.py', usage='%(prog)s {-f file} [-o output_file]') parser.add_argument("-f", "--file", type=str, required=True, help="Parse a single Nmap .xml output file") parser.add_argument("-o", "--output", type=str, help="Filename of output file for HTML report") args = parser.parse_args() #Check and create input and output files if not os.path.isfile(args.file): print '\nThere was an error opening file: %s' % args.file sys.exit() if args.output: if args.output.endswith('.html'): outFile = open(args.output, 'w') else: outFile = open(args.output + '.html', 'w') else: outFile = open('Security-Headers-Report.html', 'w') xmlDoc = minidom.parse(args.file) hostList = xmlDoc.getElementsByTagName('host') #List of security headers which are checked for and reported on headerList = ['access-control-allow-origin', 'content-security-policy', 'server', 'strict-transport-security', 'x-content-type-options', 'x-frame-options', 'x-permitted-cross-domain-policies', 'x-powered-by', 'x-xss-protection' ] assetDict = dict() print '\nInput File: %s' % args.file print 'Output File: %s' % outFile.name outFile.write('\n
\naccess-control-allow-origin | content-security-policy | server | strict-transport-security | x-content-type-options | x-frame-options | x-permitted-cross-domain-policies | x-powered-by | x-xss-protection | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
%s | ' % asset) if 'access-control-allow-origin: *' in securityHeadersString: outFile.write('Defined, Allows All (*) | ') elif 'access-control-allow-origin' not in securityHeadersString: outFile.write('Undefined | ') else: outFile.write('Defined, %s | ' % re.search('access-control-allow-origin: (.*)', securityHeadersString).group(1)) if 'content-security-policy' in securityHeadersString: outFile.write('%s | ' % re.search('content-security-policy: (.*)', securityHeadersString).group(1)) else: outFile.write('Undefined | ') if 'server' in securityHeadersString: outFile.write('Defined, %s | ' % re.search('server: (.*)', securityHeadersString).group(1)) else: outFile.write('Undefined | ') if 'strict-transport-security' in securityHeadersString: outFile.write('%s | ' % re.search('strict-transport-security: (.*)', securityHeadersString).group(1)) else: outFile.write('Undefined | ') if 'x-content-type-options' in securityHeadersString: outFile.write('%s | ' % re.search('x-content-type-options: (.*)', securityHeadersString).group(1)) else: outFile.write('Undefined | ') if 'x-frame-options' in securityHeadersString: outFile.write('%s | ' % re.search('x-frame-options: (.*)', securityHeadersString).group(1)) else: outFile.write('Undefined | ') if 'x-permitted-cross-domain-policies' in securityHeadersString: outFile.write('%s | ' % re.search('x-permitted-cross-domain-policies: (.*)', securityHeadersString).group(1)) else: outFile.write('Undefined | ') if 'x-powered-by' in securityHeadersString: outFile.write('Defined, %s | ' % re.search('x-powered-by: (.*)', securityHeadersString).group(1)) else: outFile.write('Undefined | ') if 'x-xss-protection: 0' in securityHeadersString: outFile.write('Protection Disabled, x-xss-protection: 0 | ') elif 'x-xss-protection' not in securityHeadersString: outFile.write('Undefined | ') else: outFile.write('%s | ' % re.search('x-xss-protection: (.*)', securityHeadersString).group(1)) headersDescription = """