def check(self, opt_options=dict()): csp = self.csp if not csp: return [] findings = [] angular = [] jsonp = [] jsonpeval = [] if 'bypasses' not in opt_options.keys(): bypasses = [] else: bypasses = opt_options['bypasses'] directive = csp.getEffectiveDirective(csp.directive.OBJECT_SRC) objectSrcValues = [] try: objectSrcValues = csp[directive] except KeyError: objectSrcValues = [] if csp.directive.PLUGIN_TYPES in csp.parsedstring: pluginTypes = csp[csp.directive.PLUGIN_TYPES] else: pluginTypes = None if pluginTypes and not 'application/x-shockwave-flash' in pluginTypes: return [] for value in objectSrcValues: if value == csp.keyword.NONE: return [] url = '//' + Util.getSchemeFreeUrl(value) flashBypass = Util.matchWildcardUrls(url, bypasses) if (flashBypass): findings.append( Finding( csp.headerkey, FindingType.OBJECT_WHITELIST_BYPASS, flashBypass.netloc + ' is known to host Flash files which allow to bypass this CSP.', FindingSeverity.HIGH, directive, value)) elif (directive == csp.directive.OBJECT_SRC): findings.append( Finding(csp.headerkey, FindingType.OBJECT_WHITELIST_BYPASS, 'Can you restrict object-src to \'none\' only?', FindingSeverity.MEDIUM_MAYBE, directive, value)) return findings
def check(self, headers, opt_options=dict()): policy = self.getfeaturepolicy(headers) if not policy or not policy.parsedstring: return [] findings = [] directivesToCheck = policy.getEffectiveDirectives() for directive in directivesToCheck: values = policy.getEffectiveValues(directive) for value in values: url = Util.getSchemeFreeUrl(value) if url and str(FeaturePolicyKeyword.STAR) in url and len( url ) == 1 and directive != FeaturePolicyDirective.PICTURE_IN_PICTURE: findings.append( Finding( policy.headerkey, FindingType.PLAIN_WILDCARD, directive.value + ' should not allow \'*\' as source. It enables the current page and nesting contexts, such as iframes, to use the feature. It may be better to disable and explicitly tell the iframe which feature is allowed.', FindingSeverity.LOW, directive, value)) return findings
def check(self): csp = self.csp if not csp or not csp.parsedstring: return [] findings = [] directivesToCheck = csp.getEffectiveDirectives( csp.DIRECTIVES_CAUSING_XSS) for directive in directivesToCheck: values = [] if directive in csp.parsedstring: values = csp[directive] for value in values: url = Util.getSchemeFreeUrl(value) if '*' in url and len(url) == 1: findings.append( Finding( csp.headerkey, FindingType.PLAIN_WILDCARD, directive.value + ' should not allow \'*\' as source. This may enable execution of malicious JavaScript.', FindingSeverity.HIGH, directive, value)) return findings
def checkIP(self, directive, directiveValues, findings): csp = self.csp for value in directiveValues: url = '//' + Util.getSchemeFreeUrl(value) host = urlparse(url).netloc ip = None validip = True try: ip = ipaddress.ip_address(u'' + host) except ValueError: validip = False if validip: ipString = str(ip) + '' if '127.0.0.1' in ipString: findings.append( Finding( csp.headerkey, FindingType.IP_SOURCE, directive.value + ' directive allows localhost as source. Please make sure to remove this in production environments.', FindingSeverity.INFO, directive, value)) else: findings.append( Finding( csp.headerkey, FindingType.IP_SOURCE, directive.value + ' directive has an IP-Address as source: ' + ipString + ' (will be ignored by browsers!). ', FindingSeverity.INFO, directive, value))
def populate(self): # path = securityheaders.models.__path__[0] # with warnings.catch_warnings(): # warnings.simplefilter("ignore") # Util.load_all_modules_from_dir(path) clazzes = list(Util.inheritors(Header)) clazzes.extend(Util.inheritors(Directive)) clazzes.extend(Util.inheritors(Keyword)) for header in list(Util.inheritors(Header)): if hasattr(header,'headerkey'): self.headers[header.headerkey] = header all_my_base_classes = {cls: cls for cls in clazzes} for clazz in all_my_base_classes: self.clazzes[clazz.__name__] = clazz
def test_matchWildcardUrls(self): urls = [ '//vk.com/swf/video.swf', '//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf' ] self.assertEqual(Util.matchWildcardUrls('https://*.vk.com', urls), None) self.assertEqual( Util.matchWildcardUrls('https://ajax.googleapis.com', urls), urlparse( '//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf' )) self.assertEqual( Util.matchWildcardUrls('https://*.googleapis.com', urls), urlparse( '//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf' )) self.assertEqual(Util.matchWildcardUrls(None, urls), None) self.assertEqual( Util.matchWildcardUrls('https://*.googleapis.com', None), None) self.assertEqual( Util.matchWildcardUrls('https://*.googleapis.com', []), None) self.assertEqual( Util.matchWildcardUrls('*.googleapis.com', urls), urlparse( '//ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/charts/assets/charts.swf' ))
def populate(self): #path = securityheaders.formatters.__path__[0] #with warnings.catch_warnings(): # warnings.simplefilter("ignore") # Util.load_all_modules_from_dir(path) clazzes = list(Util.inheritors(FindingFormatter)) all_my_base_classes = {cls: cls for cls in clazzes} for clazz in all_my_base_classes: self.clazzes[clazz.__name__] = clazz
def normalizeDirectiveValue(self, directiveValue): directiveValue = directiveValue.strip() directiveValueLower = directiveValue.lower() if self.directivekeyword: if self.directivekeyword.isKeyword(directiveValueLower): return self.directivekeyword[directiveValueLower] elif self.directivekeyword.isValue(directiveValueLower): return self.directivekeyword(directiveValueLower) elif Util.isUrlScheme(directiveValue): return directiveValueLower return directiveValue
def check(self, opt_options=dict()): csp = self.csp if not csp: return [] findings = [] cdn = [] if 'cdn' not in opt_options.keys(): cdn = [] elif 'cdn' in opt_options.keys(): cdn = opt_options['cdn'] effectiveScriptSrcDirective = csp.getEffectiveDirective( csp.directive.SCRIPT_SRC) scriptSrcValues = [] try: scriptSrcValues = csp[effectiveScriptSrcDirective] except KeyError: scriptSrcValues = [] for value in scriptSrcValues: if value.startswith('\''): continue if Util.isUrlScheme(value) or value.find('.') == -1: continue url = '//' + Util.getSchemeFreeUrl(value) cdnbypass = Util.matchWildcardUrls(url, cdn) if cdnbypass: bypassDomain = '' bypassUrl = '//' + cdnbypass.netloc + cdnbypass.path bypassDomain = cdnbypass.netloc findings.append( Finding( csp.headerkey, FindingType.SCRIPT_WHITELIST_BYPASS, bypassDomain + ' is known to host many libraries which allow to bypass this CSP. Do not whitelist all scripts of a CDN.', FindingSeverity.HIGH, effectiveScriptSrcDirective, value)) return findings
def test_getSchemeFreeUrlValid(self): self.assertEqual(Util.getSchemeFreeUrl('http://www.synopsys.com'), 'www.synopsys.com')
def check(self, opt_options=dict()): csp = self.csp if not csp: return [] findings = [] angular = [] jsonp = [] jsonpeval = [] if 'angular' not in opt_options.keys(): angular = [] elif 'angular' in opt_options.keys(): angular = opt_options['angular'] if 'jsonp' not in opt_options.keys(): jsonp = [] if 'jsonp' in opt_options.keys(): jsonp = opt_options['jsonp'] if 'jsonpeval' not in opt_options.keys(): jsonpeval = [] if 'jsonpeval' in opt_options.keys(): jsonpeval = opt_options['jsonpeval'] effectiveScriptSrcDirective = csp.getEffectiveDirective( csp.directive.SCRIPT_SRC) scriptSrcValues = [] try: scriptSrcValues = csp[effectiveScriptSrcDirective] except KeyError: scriptSrcValues = [] for value in scriptSrcValues: if value == csp.keyword.SELF or value == str(csp.keyword.SELF): findings.append( Finding( csp.headerkey, FindingType.SCRIPT_WHITELIST_BYPASS, '\'self\' can be problematic if you host JSONP, Angular or user uploaded files.', FindingSeverity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)) continue if value.startswith('\''): continue if Util.isUrlScheme(value) or value.find('.') == -1: continue url = '//' + Util.getSchemeFreeUrl(value) angularBypass = Util.matchWildcardUrls(url, angular) jsonpBypass = Util.matchWildcardUrls(url, jsonp) if jsonpBypass: bypassUrl = '//' + jsonpBypass.netloc + jsonpBypass.path evalRequired = jsonpBypass.netloc in jsonpeval evalPresent = csp.keyword.UNSAFE_EVAL in scriptSrcValues if evalRequired and not evalPresent: jsonpBypass = None if jsonpBypass or angularBypass: bypassDomain = '' bypassTxt = '' if jsonpBypass: bypassDomain = jsonpBypass.netloc bypassTxt = ' JSONP endpoints' if angularBypass: bypassDomain = angularBypass.netloc if not bypassTxt: bypassTxt += '' else: bypassTxt += ' and' bypassTxt += ' Angular libraries' findings.append( Finding( csp.headerkey, FindingType.SCRIPT_WHITELIST_BYPASS, bypassDomain + ' is known to host' + bypassTxt + ' which allow to bypass this CSP.', FindingSeverity.HIGH, effectiveScriptSrcDirective, value)) else: findings.append( Finding( csp.headerkey, FindingType.SCRIPT_WHITELIST_BYPASS, 'No bypass found; make sure that this URL doesn\'t serve JSONP replies or Angular libraries.', FindingSeverity.MEDIUM_MAYBE, effectiveScriptSrcDirective, value)) return findings
from .xcontenttypeoptions import * from .xframeoptions import * from .xxssprotection import * from .referrerpolicy import * from .featurepolicy import * from .server import * from .xpoweredby import * from .xwebkitcsp import * from .xcsp import * from .xdownloadoptions import * from .expectct import * from .xaspnetversion import * from .xaspnetmvcversion import * from .hpkp import * from .xpcdp import * from .setcookie import * __all__ = [ 'annotations', 'csp', 'cors', 'clearsitedata', 'hsts', 'xcontenttypeoptions', 'xframeoptions', 'xxssprotection', 'featurepolicy', 'referrerpolicy', 'server', 'xpoweredby', 'expectct', 'xcsp', 'xwebkitcsp', 'xpcdp', 'xaspnetversion', 'xaspnetmvcversion', 'hpkp', 'xdownloadoptions' ] clazzes = list(Util.inheritors(Header)) clazzes.extend(Util.inheritors(Directive)) clazzes.extend(Util.inheritors(Keyword)) all_my_base_classes = {cls.__name__: cls for cls in clazzes} __all__.extend(all_my_base_classes) __all__.append('ModelFactory') __all__.append('CSPVersion')
def test_isUrlSchemeNone(self): self.assertFalse(Util.isUrlScheme(None))
def test_isUrlSchemeHttps(self): self.assertTrue(Util.isUrlScheme('https:'))
def test_isUrlSchemeInvalid(self): self.assertFalse(Util.isUrlScheme('noscheme'))
def test_getSchemeFreeUrlNone(self): self.assertEqual(Util.getSchemeFreeUrl(None), None)
def test_getSchemeFreeUrlCapitals(self): self.assertEqual(Util.getSchemeFreeUrl('HTTPS://www.synopsys.com'), 'www.synopsys.com')