def hash(self, plaintext, salt, iterations=None): # Verify a MasterSalt has been set if not self.master_salt or len(self.master_salt) < 20: raise Exception( _("There is an error in the application configuration. The MasterSalt has not been set properly. Please see the instructions in the README for setting up a crypto keyring. Currently, Encryptor_MasterSalt=%(value)s" ) % {'value': self.master_salt}) if iterations is None: iterations = self.hash_iterations try: digest = hashlib.new(self.hash_algorithm) digest.update(self.master_salt) digest.update(salt) digest.update(plaintext) bytes = digest.digest() for i in range(self.hash_iterations): digest = hashlib.new(self.hash_algorithm) digest.update(bytes) bytes = digest.digest() encoded = ESAPI.encoder().encode_for_base64(bytes) return encoded except ValueError, e: raise EncryptionException( _("Problem hashing"), _("Internal Error - Can't find hash algorithm ") + self.hash_algorithm)
def test_encode_for_xml_attribute(self): instance = ESAPI.encoder() self.assertEquals(None, instance.encode_for_xml_attribute(None)) self.assertEquals(" ", instance.encode_for_xml_attribute(" ")) self.assertEquals("<script>", instance.encode_for_xml_attribute("<script>")) self.assertEquals(",.-_", instance.encode_for_xml_attribute(",.-_")) self.assertEquals(" !@$%()=+{}[]", instance.encode_for_xml_attribute(" !@$%()=+{}[]"))
def test_windows_codec(self): instance = ESAPI.encoder() win = WindowsCodec() immune = [] self.assertEquals(None, instance.encode_for_os(win, None)) npbs = PushbackString("n") self.assertEquals(None, win.decode_character(npbs)) epbs = PushbackString("") self.assertEquals(None, win.decode_character(epbs)) c = '<' cpbs = PushbackString(win.encode_character(immune, c)) decoded = win.decode_character(cpbs) self.assertEquals(c, decoded) orig = "c:\\jeff" enc = win.encode(Encoder.CHAR_ALPHANUMERICS, orig) self.assertEquals(orig, win.decode(enc)) self.assertEquals(orig, win.decode(orig)) self.assertEquals("c^:^\\jeff", instance.encode_for_os(win, "c:\\jeff")); self.assertEquals("c^:^\\jeff", win.encode(immune, "c:\\jeff")) self.assertEquals("dir^ ^&^ foo", instance.encode_for_os(win, "dir & foo")) self.assertEquals("dir^ ^&^ foo", win.encode(immune, "dir & foo"))
def validate_roles(self, roles): """ Checks that the roles passed in contain only letters, numbers, and underscores. Also checks that roles are no more than 10 characters long. If a role does not pass validation, it is not included in the list of roles returned by this method. A log warning is also generated for any invalid roles. @param roles: the list of roles to validate according to the criteria stated above. @return: a list of roles that are valid according to the criteria stated above. """ ret = [] for role in roles: canonical = '' try: stripped = role.strip() canonical = ESAPI.encoder().canonicalize(stripped) except EncodingException, extra: self.logger.warning( Logger.SECURITY_FAILURE, _("Failed to canonicalize role: %(role)s") % {'role' : stripped}, extra ) if not ESAPI.validator().is_valid_input( "Roles in FileBasedAccessController", canonical, "AccessControlRule", 200, False ): self.logger.warning( Logger.SECURITY_FAILURE, _("Role is invalid, and was not added to the list of roles for this rule: %(role)s") % {'role' : stripped} ) else:
def validate_roles(self, roles): """ Checks that the roles passed in contain only letters, numbers, and underscores. Also checks that roles are no more than 10 characters long. If a role does not pass validation, it is not included in the list of roles returned by this method. A log warning is also generated for any invalid roles. @param roles: the list of roles to validate according to the criteria stated above. @return: a list of roles that are valid according to the criteria stated above. """ ret = [] for role in roles: canonical = '' try: stripped = role.strip() canonical = ESAPI.encoder().canonicalize(stripped) except EncodingException, extra: self.logger.warning( Logger.SECURITY_FAILURE, _("Failed to canonicalize role: %(role)s") % {'role': stripped}, extra) if not ESAPI.validator().is_valid_input( "Roles in FileBasedAccessController", canonical, "AccessControlRule", 200, False): self.logger.warning( Logger.SECURITY_FAILURE, _("Role is invalid, and was not added to the list of roles for this rule: %(role)s" ) % {'role': stripped}) else: ret.append(stripped)
def hash(self, plaintext, salt, iterations=None): # Verify a MasterSalt has been set if not self.master_salt or len(self.master_salt) < 20: raise Exception( _("There is an error in the application configuration. The MasterSalt has not been set properly. Please see the instructions in the README for setting up a crypto keyring. Currently, Encryptor_MasterSalt=%(value)s") % {'value' : self.master_salt}) if iterations is None: iterations = self.hash_iterations try: digest = hashlib.new(self.hash_algorithm) digest.update(self.master_salt) digest.update(salt) digest.update(plaintext) bytes = digest.digest() for i in range(self.hash_iterations): digest = hashlib.new(self.hash_algorithm) digest.update(bytes) bytes = digest.digest() encoded = ESAPI.encoder().encode_for_base64(bytes) return encoded except ValueError, e: raise EncryptionException( _("Problem hashing"), _("Internal Error - Can't find hash algorithm ") + self.hash_algorithm )
def test_unix_codec(self): instance = ESAPI.encoder() unix = UnixCodec() immune = '' self.assertEquals(None, instance.encode_for_os(unix, None)) npbs = PushbackString("n") self.assertEquals(None, unix.decode_character(npbs)) c = '<' cpbs = PushbackString(unix.encode_character(immune, c)) decoded = unix.decode_character(cpbs) self.assertEquals(c, decoded) epbs = PushbackString("") self.assertEquals(None, unix.decode_character(epbs)) orig = "/etc/passwd" enc = unix.encode(immune, orig) self.assertEquals(orig, unix.decode(enc)) self.assertEquals(orig, unix.decode(orig)) self.assertEquals("c\\:\\\\jeff", instance.encode_for_os(unix, "c:\\jeff")) self.assertEquals("c\\:\\\\jeff", unix.encode(immune, "c:\\jeff")) self.assertEquals("dir\\ \\&\\ foo", instance.encode_for_os(unix, "dir & foo")) self.assertEquals("dir\\ \\&\\ foo", unix.encode(immune, "dir & foo")) # Unix paths (that must be encoded safely) self.assertEquals("\\/etc\\/hosts", instance.encode_for_os(unix, "/etc/hosts")) self.assertEquals("\\/etc\\/hosts\\;\\ ls\\ -l", instance.encode_for_os(unix, "/etc/hosts; ls -l")) self.assertEquals("\\", unix.decode('\\')) self.assertEquals(unichr(12345), unix.encode('', unichr(12345)))
def log(self, level, event_type, message, exception=None): """ Log the message after optionally encoding any special characters that might be dangerous when viewed by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging specific session ID, and the current date/time. It will only log the message if the current logging level is enabled, otherwise it will discard the message. @param level: the severity level of the security event @param event_type: the event_type of the event (SECURITY, FUNCTIONALITY, etc.) @param message: the message @param exception: an exception """ # Before we waste all kinds of time preparing this event for the # log, let check to see if its loggable if not self.pyLogger.isEnabledFor(level): return user = ESAPI.authenticator().current_user # create a random session number for the user to represent the # user's 'session', if it doesn't exist already sid = _("unknown") request = ESAPI.http_utilities().current_request if request is not None: session = request.session if session is not None: sid = session.get('ESAPI_SESSION', None) # if there is no session id for the user yet, create one # and store it in the user's session if sid is None: sid = str(ESAPI.randomizer().get_random_integer(0, 1000000)) session['ESAPI_SESSION'] = sid # ensure there's something to log if message is None: message = "" # ensure no CRLF injection into logs for forging records clean = message.replace('\n', '_').replace('\r', '_') if ESAPI.security_configuration().get_log_encoding_required(): clean = ESAPI.encoder().encode_for_html(message) if message != clean: clean += " (Encoded)" extra = { 'eventType' : str(event_type), 'eventSuccess' : [_("SUCCESS"),_("FAILURE")][event_type.is_success()], 'user' : user.account_name, 'hostname' : user.last_host_address, 'sessionID' : sid, } self.pyLogger.log(level, clean, extra=extra)
def __init__(self, encoder=None): Validator.__init__(self) if encoder: self.encoder = encoder else: self.encoder = ESAPI.encoder() self.make_file_validator()
def encrypt_state_in_cookie(self, cleartext_map, response=None): if response is None: response = self.current_response buf = '' for key, value in cleartext_map.items(): if buf != '': buf += '&' try: key = ESAPI.encoder().encode_for_url( key ) value = ESAPI.encoder().encode_for_url( value ) buf += "%s=%s" % (key, value) except EncodingException, extra: self.logger.error( Logger.SECURITY_FAILURE, _("Problem encrypting state in cookie - skipping entry"), extra=extra )
def __init__(self, encoder=None): Validator.__init__(self) if encoder: self.encoder = encoder else: self.encoder = ESAPI.encoder() self.make_file_validator()
def gen_keys(self): """ Create new keys. """ print( _("Creating new keys in %(location)s") % {'location': self.keys_location}) # Create symmetric key os.makedirs(self.keys_symmetric_location) keyczartool.main([ 'create', "--location=%s" % self.keys_symmetric_location, "--purpose=crypt" ]) keyczartool.main([ 'addkey', "--location=%s" % self.keys_symmetric_location, "--status=primary", "--size=%s" % self.encryption_key_length ]) # Create asymmetric private keys for signing os.makedirs(self.keys_asymmetric_private_location) keyczartool.main([ 'create', "--location=%s" % self.keys_asymmetric_private_location, "--purpose=sign", "--asymmetric=%s" % self.encrypt_algorithm ]) keyczartool.main([ 'addkey', "--location=%s" % self.keys_asymmetric_private_location, "--status=primary", "--size=%s" % self.signing_key_length ]) # Extract public keys for signing os.makedirs(self.keys_asymmetric_public_location) keyczartool.main([ 'create', "--location=%s" % self.keys_asymmetric_public_location, "--purpose=sign", "--asymmetric=%s" % self.encrypt_algorithm ]) keyczartool.main([ 'pubkey', "--location=%s" % self.keys_asymmetric_private_location, "--status=primary", "--destination=%s" % self.keys_asymmetric_public_location ]) # Gen a new master salt from os import urandom salt = urandom(20) print "Please modify this line in esapi/conf/settings.py:" print "Encryptor_MasterSalt = '" + ESAPI.encoder().encode_for_base64( salt) + "'" print "Done!"
def encrypt_state_in_cookie(self, cleartext_map, response=None): if response is None: response = self.current_response buf = '' for key, value in cleartext_map.items(): if buf != '': buf += '&' try: key = ESAPI.encoder().encode_for_url(key) value = ESAPI.encoder().encode_for_url(value) buf += "%s=%s" % (key, value) except EncodingException, extra: self.logger.error( Logger.SECURITY_FAILURE, _("Problem encrypting state in cookie - skipping entry"), extra=extra)
def test_encode_for_base64(self): instance = ESAPI.encoder() self.assertEquals(None, instance.encode_for_base64(None)) self.assertEquals(None, instance.decode_from_base64(None)) for i in range(100): random_string = ESAPI.randomizer().get_random_string( 20, Encoder.CHAR_SPECIALS ) encoded = instance.encode_for_base64( random_string ) decoded = instance.decode_from_base64( encoded ) self.assertEquals( random_string, decoded )
def test_encode_for_dn(self): instance = ESAPI.encoder() self.assertEquals(None, instance.encode_for_dn(None)) self.assertEquals("Hello�", instance.encode_for_dn("Hello�"), "No special characters to escape") self.assertEquals("\\# Hello�", instance.encode_for_dn("# Hello�"), "leading #") self.assertEquals("\\ Hello�", instance.encode_for_dn(" Hello�"), "leading space") self.assertEquals("Hello�\\ ", instance.encode_for_dn("Hello� "), "trailing space") self.assertEquals("Hello\\<\\>", instance.encode_for_dn("Hello<>"), "less than greater than") self.assertEquals("\\ \\ ", instance.encode_for_dn(" "), "only 3 spaces") self.assertEquals("\\ Hello\\\\ \\+ \\, \\\"World\\\" \\;\\ ", instance.encode_for_dn(" Hello\\ + , \"World\" ; "), "Christmas Tree DN")
def __init__(self, type_name, encoder=None): self.type_name = None self.allow_none = False self.encoder = None if encoder: self.set_encoder(encoder) else: self.set_encoder(ESAPI.encoder()) self.set_type_name(type_name)
def __init__(self, type_name, encoder=None): self.type_name = None self.allow_none = False self.encoder = None if encoder: self.set_encoder( encoder ) else: self.set_encoder( ESAPI.encoder() ) self.set_type_name(type_name)
def gen_keys(self): """ Create new keys. """ print (_("Creating new keys in %(location)s") % {'location' : self.keys_location} ) # Create symmetric key os.makedirs(self.keys_symmetric_location) keyczartool.main( ['create', "--location=%s" % self.keys_symmetric_location, "--purpose=crypt"] ) keyczartool.main( ['addkey', "--location=%s" % self.keys_symmetric_location, "--status=primary", "--size=%s" % self.encryption_key_length] ) # Create asymmetric private keys for signing os.makedirs(self.keys_asymmetric_private_location) keyczartool.main( ['create', "--location=%s" % self.keys_asymmetric_private_location, "--purpose=sign", "--asymmetric=%s" % self.encrypt_algorithm] ) keyczartool.main( ['addkey', "--location=%s" % self.keys_asymmetric_private_location, "--status=primary", "--size=%s" % self.signing_key_length] ) # Extract public keys for signing os.makedirs(self.keys_asymmetric_public_location) keyczartool.main( ['create', "--location=%s" % self.keys_asymmetric_public_location, "--purpose=sign", "--asymmetric=%s" % self.encrypt_algorithm] ) keyczartool.main( ['pubkey', "--location=%s" % self.keys_asymmetric_private_location, "--status=primary", "--destination=%s" % self.keys_asymmetric_public_location] ) # Gen a new master salt from os import urandom salt = urandom(20) print "Please modify this line in esapi/conf/settings.py:" print "Encryptor_MasterSalt = '" + ESAPI.encoder().encode_for_base64(salt) + "'" print "Done!"
def test_decode_from_base64(self): instance = ESAPI.encoder() for i in range(100): random_string = ESAPI.randomizer().get_random_string( 20, Encoder.CHAR_SPECIALS ) encoded = instance.encode_for_base64( random_string ) decoded = instance.decode_from_base64( encoded ) self.assertEqual( random_string, decoded ) for i in range(100): random_string = ESAPI.randomizer().get_random_string( 20, Encoder.CHAR_SPECIALS ) encoded = ESAPI.randomizer().get_random_string(1, Encoder.CHAR_ALPHANUMERICS) + instance.encode_for_base64( random_string ) decoded = instance.decode_from_base64( encoded ) self.assertFalse( random_string == decoded )
def test_percent_codec(self): instance = ESAPI.encoder() ### High level self.assertEquals(None, instance.encode_for_url(None)) self.assertEquals("%3Cscript%3E", instance.encode_for_url("<script>")) self.assertEquals("%03", instance.encode_for_url(unichr(3))) self.assertEquals("+", instance.encode_for_url(" ")) self.assertEquals(unichr(12345), instance.encode_for_url(unichr(12345))) self.assertEquals(None, instance.decode_from_url(None)) self.assertEquals("<script>", instance.decode_from_url("%3Cscript%3E")) self.assertEquals(unichr(3), instance.decode_from_url("%03")) self.assertEquals(" ", instance.decode_from_url("+++++") ) self.assertEquals(unichr(12345), instance.decode_from_url(unichr(12345))) # Wrap in assertRaises self.assertEquals("%3xridiculous", instance.decode_from_url( "%3xridiculous" ))
def test_codec_for_javascript(self): instance = ESAPI.encoder() ### High level self.assertEquals(None, instance.encode_for_javascript(None)) self.assertEquals("\\x3Cscript\\x3E", instance.encode_for_javascript("<script>")) self.assertEquals(",.\\x2D_\\x20", instance.encode_for_javascript(",.-_ ")) self.assertEquals("\\x21\\x40\\x24\\x25\\x28\\x29\\x3D\\x2B\\x7B\\x7D\\x5B\\x5D", instance.encode_for_javascript("!@$%()=+{}[]")) # Unicode self.assertEquals(unichr(12345), instance.encode_for_javascript(unichr(12345))) ### Low level codec = JavascriptCodec() # Bad hex format self.assertEquals("\\xAQ", codec.decode("\\xAQ")) self.assertEquals("\\uAAQ", codec.decode("\\uAAQ"))
def test_html_codec(self): instance = ESAPI.encoder() ### High level self.assertEquals(None, instance.encode_for_html(None)) # test invalid characters are replaced with spaces self.assertEquals("a b c d e f	g", instance.encode_for_html("a" + unichr(0) + "b" + unichr(4) + "c" + unichr(128) + "d" + unichr(150) + "e" +unichr(159) + "f" + unichr(9) + "g")) self.assertEquals("<script>", instance.encode_for_html("<script>")) self.assertEquals("&lt;script&gt;", instance.encode_for_html("<script>")) self.assertEquals("!@$%()=+{}[]", instance.encode_for_html("!@$%()=+{}[]")) self.assertEquals(",.-_ ", instance.encode_for_html(",.-_ ")) self.assertEquals("dir&", instance.encode_for_html("dir&")) self.assertEquals("one&two", instance.encode_for_html("one&two")) # Unicode self.assertEquals(unichr(12345), instance.encode_for_html(unichr(12345))) ### Low lovel codec = HTMLEntityCodec() cases = ( # PLAIN - ENCODED - ALT_ENCODINGS ('', '', ()), ('t','t', ()), ('test', 'test', ()), ('<script>', '<script>', ()), ('!@#$%^&*(){}[]?+/=|\\', '!@#$%^&*(){}[]?+/=|\', ()), ('"`~1234_-', '"`~1234_-', ()), (unichr(9), "	", ()), (unichr(12345), unichr(12345), ()), ('\\', '\', ()), ) for case in cases: self.assertEquals(case[ENCODED], codec.encode('', case[PLAIN])) self.assertEquals(case[PLAIN], codec.decode(case[ENCODED])) for encoding in case[ALT_ENCODINGS]: self.assertEquals(case[PLAIN], codec.decode(encoding)) # Bad entity name self.assertEquals("&ridiculous;", codec.decode("&ridiculous;"))
def test_codec_for_vbscript(self): instance = ESAPI.encoder() ### High level self.assertEquals(None, instance.encode_for_vbscript(None)) self.assertEquals( "chrw(60)&\"script\"&chrw(62)", instance.encode_for_vbscript("<script>")) self.assertEquals( "x\"&chrw(32)&chrw(33)&chrw(64)&chrw(36)&chrw(37)&chrw(40)&chrw(41)&chrw(61)&chrw(43)&chrw(123)&chrw(125)&chrw(91)&chrw(93)", instance.encode_for_vbscript("x !@$%()=+{}[]")) self.assertEquals( "alert\"&chrw(40)&chrw(39)&\"ESAPI\"&chrw(32)&\"test\"&chrw(33)&chrw(39)&chrw(41)", instance.encode_for_vbscript("alert('ESAPI test!')" )) self.assertEquals( "jeff.williams\"&chrw(64)&\"aspectsecurity.com", instance.encode_for_vbscript("*****@*****.**")) self.assertEquals( "test\"&chrw(32)&chrw(60)&chrw(62)&chrw(32)&\"test", instance.encode_for_vbscript("test <> test" )) ### Low level codec = VBScriptCodec() cases = ( # PLAIN - ENCODED - ALT_ENCODINGS ('', '', ()), # 0 length string ('t','t', ()), ('test', 'test', ()), (unichr(12345), unichr(12345), ()), ) for case in cases: if case[ENCODED] is not None: self.assertEquals(case[ENCODED], codec.encode('', case[PLAIN])) self.assertEquals(case[PLAIN], codec.decode(case[ENCODED])) for encoding in case[ALT_ENCODINGS]: print "encoding=",encoding self.assertEquals(case[PLAIN], codec.decode(encoding)) encode_only_cases = ( # PLAIN - ENCODED - ALT_ENCODINGS ('<script>', 'chrw(60)&"script"&chrw(62)', ()), ('!@#$%^&*(){}[]?+/=|\\', 'chrw(33)&chrw(64)&chrw(35)&chrw(36)&chrw(37)&chrw(94)&chrw(38)&chrw(42)&chrw(40)&chrw(41)&chrw(123)&chrw(125)&chrw(91)&chrw(93)&chrw(63)&chrw(43)&chrw(47)&chrw(61)&chrw(124)&chrw(92)', ()), ('"`~1234_-', 'chrw(34)&chrw(96)&chrw(126)&"1234"&chrw(95)&chrw(45)', ()), (unichr(9), "chrw(9)", ()), ('\\', 'chrw(92)', ('\\')), ) for case in encode_only_cases: if case[ENCODED] is not None: self.assertEquals(case[ENCODED], codec.encode('', case[PLAIN]))
def search_for_rule(self, dictionary, roles, key): """ Search for the rule. Four mapping rules are used in order: - Exact match, e.g. /access/login - Longest path prefix match, beginning / and ending /*, e.g. /access/* or /* - Extension matching, beginning *., e.g. *.css - Default rule, specified by the single character pattern @param dictionary: the map containing the access rules @param roles: a list of roles the user has @param key: the file, url, object, etc. being checked for access @return: the rule stating whether to allow or deny access """ canonical = None try: canonical = ESAPI.encoder().canonicalize(key) except EncodingException, extra: self.logger.warning( Logger.SECURITY_FAILURE, False, _("Failed to canonicalize input: %(key)s") % {'key': key})
def search_for_rule(self, dictionary, roles, key): """ Search for the rule. Four mapping rules are used in order: - Exact match, e.g. /access/login - Longest path prefix match, beginning / and ending /*, e.g. /access/* or /* - Extension matching, beginning *., e.g. *.css - Default rule, specified by the single character pattern @param dictionary: the map containing the access rules @param roles: a list of roles the user has @param key: the file, url, object, etc. being checked for access @return: the rule stating whether to allow or deny access """ canonical = None try: canonical = ESAPI.encoder().canonicalize(key) except EncodingException, extra: self.logger.warning( Logger.SECURITY_FAILURE, False, _("Failed to canonicalize input: %(key)s") %
def test_sql_codec(self): instance = ESAPI.encoder() ### High level mySQL1 = MySQLCodec( MySQLCodec.ANSI_MODE ) self.assertEquals(None, instance.encode_for_sql(mySQL1, None)) self.assertEquals("Jeff'' or ''1''=''1", instance.encode_for_sql(mySQL1, "Jeff' or '1'='1")) self.assertEquals("''", instance.encode_for_sql(mySQL1, "'")) self.assertEquals(unichr(12345), instance.encode_for_sql(mySQL1, unichr(12345))) mySQL2 = MySQLCodec( MySQLCodec.MYSQL_MODE ) self.assertEquals(None, instance.encode_for_sql(mySQL2, None)) self.assertEquals("Jeff\\' or \\'1\\'\\=\\'1", instance.encode_for_sql(mySQL2, "Jeff' or '1'='1")) self.assertEquals("\\t", instance.encode_for_sql(mySQL2, unichr(9))) self.assertEquals(unichr(12345), instance.encode_for_sql(mySQL2, unichr(12345))) ### Low level cases = ('test', '<script>', 'the answer', '!@#$%^&*(){}[]?+/=|\\', '"`~1234_-', unichr(9), unichr(12345)) for case in cases: self.assertEquals(case, mySQL1.decode(mySQL1.encode('', case))) self.assertEquals(case, mySQL2.decode(mySQL1.encode('', case))) self.assertEquals(unichr(9), mySQL2.decode("\\t")) self.assertEquals('m', mySQL2.decode("\\m")) self.assertEquals("'", mySQL1.decode("''")) self.assertEquals("'", mySQL1.decode("'")) self.assertEquals("'q", mySQL1.decode("'q")) # Bad mode self.assertRaises(BadModeError, MySQLCodec, -2 )
def test_double_encoding_canonicalization(self): instance = ESAPI.encoder() # note these examples use the strict=False flag on canonicalize to allow # full decoding without throwing an IntrusionException. Generally, you # should use strict mode as allowing double-encoding is an abomination. # double encoding examples self.assertEquals( "<", instance.canonicalize("&lt;", False )); #double entity self.assertEquals( "\\", instance.canonicalize("%255c", False)); #double percent self.assertEquals( "%", instance.canonicalize("%2525", False)); #double percent # double encoding with multiple schemes example self.assertEquals( "<", instance.canonicalize("%26lt%3b", False)); #first entity, then percent self.assertEquals( "&", instance.canonicalize("%26", False)); #first percent, then entity # nested encoding examples self.assertEquals( "<", instance.canonicalize("%253c", False)); #nested encode % with percent self.assertEquals( "<", instance.canonicalize("%%33%63", False)); #nested encode both nibbles with percent self.assertEquals( "<", instance.canonicalize("%%33c", False)); # nested encode first nibble with percent self.assertEquals( "<", instance.canonicalize("%3%63", False)); #nested encode second nibble with percent self.assertEquals( "<", instance.canonicalize("&lt;", False)); #nested encode l with entity self.assertEquals( "<", instance.canonicalize("%253c", False)); #triple percent, percent, 5 with entity # nested encoding with multiple schemes examples self.assertEquals( "<", instance.canonicalize("&%6ct;", False)); # nested encode l with percent self.assertEquals( "<", instance.canonicalize("%3c", False)); #nested encode 3 with entity # multiple encoding tests self.assertEquals( "% & <script> <script>", instance.canonicalize( "%25 %2526 %26#X3c;script> %3Cscript%25252525253e", False ) ) self.assertEquals( "< < < < < < <", instance.canonicalize( "%26lt; %26lt; %3c %3c %2526lt%253B %2526lt%253B %2526lt%253B", False ) ) # test strict mode with both mixed and multiple encoding self.assertRaises( IntrusionException, instance.canonicalize, "%26lt; %26lt; %3c %3c %2526lt%253B %2526lt%253B %2526lt%253B" ) self.assertRaises( IntrusionException, instance.canonicalize, "%253Cscript" ) self.assertRaises( IntrusionException, instance.canonicalize, "%3Cscript" )
def test_codec_for_css(self): instance = ESAPI.encoder() ### High level self.assertEquals(None, instance.encode_for_css(None)) self.assertEquals("\\3c script\\3e ", instance.encode_for_css("<script>")) self.assertEquals("\\21 \\40 \\24 \\25 \\28 \\29 \\3d \\2b \\7b \\7d \\5b \\5d ", instance.encode_for_css("!@$%()=+{}[]")) # Unicode self.assertEquals(unichr(12345), instance.encode_for_css(unichr(12345))) ### Low level codec = CSSCodec() cases = ( # PLAIN - ENCODED - ALT_ENCODINGS ('', '', ()), # 0 length string ('t','t', ()), ('test', 'test', ()), ('<script>', '\\3c script\\3e ', ()), ('!@#$%^&*(){}[]?+/=|\\', '\\21 \\40 \\23 \\24 \\25 \\5e \\26 \\2a \\28 \\29 \\7b \\7d \\5b \\5d \\3f \\2b \\2f \\3d \\7c \\5c ', ()), ('"`~1234_-', '\\22 \\60 \\7e 1234\\5f \\2d ', ()), (unichr(9), "\\9 ", ()), (unichr(12345), unichr(12345), ()), ('\\', '\\5c ', ('\\')), ('\\2aq', None, ("\\2aq",)), # Malformed hex ('\\2aq ', None, ("\\2aq ",)), # Malformed hex ('\\q ', None, ('\\q ',)), # Malformed hex ) for case in cases: if case[ENCODED] is not None: self.assertEquals(case[ENCODED], codec.encode('', case[PLAIN])) self.assertEquals(case[PLAIN], codec.decode(case[ENCODED])) for encoding in case[ALT_ENCODINGS]: print "encoding=",encoding self.assertEquals(case[PLAIN], codec.decode(encoding))
def test_oracle_codec(self): instance = ESAPI.encoder() ### High level oracle = OracleCodec() self.assertEquals(None, instance.encode_for_sql(oracle, None)) self.assertEquals("''", instance.encode_for_sql(oracle, "'")) ### Low level cases = ('t', 'test', '<script>', 'the answer', '!@#$%^&*(){}[]?+/=|\\', '"`~1234_-', unichr(9), unichr(12345), '\\') for case in cases: self.assertEquals(case, oracle.decode(oracle.encode('', case))) self.assertEquals("''", oracle.encode('', "'")) self.assertEquals("'", oracle.decode("''"))
def execute_system_command(self, executable, params, parent_dir, working_dir=None, codec=None, log_params=True): if codec is None: codec = self.codec if working_dir is None: working_dir = self.working_dir try: # Executable must exist if not os.path.exists(executable): raise ExecutorException( _("Execution failure"), _("No such executable: %(executable)s") % {'executable': executable}) directory, filename = os.path.split(executable) # executable must use canonical path if not ESAPI.validator().is_valid_directory_path( "Executor", directory, parent_dir, False): raise ExecutorException( _("Execution failure"), _("Directory did not pass validation: %(dir)s") % {'dir': directory}) # Must be in approved list approved = ESAPI.security_configuration().get_allowed_executables() if executable not in approved: raise ExecutorException( _("Execution failure"), _("Attempt to invoke executable that is not listed as an approved executable in configuration: %(executable)s" ) % {'executable': executable}) # Escape parameters params = [ ESAPI.encoder().encode_for_os(codec, param) for param in params ] # Working directory must exist if not os.path.exists(working_dir): raise ExecutorException( _("Execution failure"), _("No such working directory for running executable: %(dir)s" ) % {'dir': working_dir}) args = params args.insert(0, executable) start_time = datetime.now() proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=working_dir) if log_params: self.logger.warning( Logger.SECURITY_SUCCESS, _("Initiating executable %(args)s in %(dir)s") % { 'args': str(args), 'dir': working_dir }) else: self.logger.warning( Logger.SECURITY_SUCCESS, _("Initiating executable %(executable)s in %(dir)s") % { 'executable': args[0], 'dir': working_dir }) while (proc.poll() is None and datetime.now() - start_time < self.max_running_time): time.sleep(1) if proc.poll() is None: # Kill the process because it ran too long proc.terminate() time.sleep(1) if proc.poll() is None: proc.kill() raise ExecutorException( _("Execution failure"), _("Process exceeded maximum running time and was killed: %(executable)s" ) % {'executable': executable}) else: # Process terminated in allotted timeframe stdout_and_err = proc.communicate() return stdout_and_err except Exception, extra: raise ExecutorException( _("Execution failure"), _("Exception thrown during execution of system command"), extra)
def test_encode_for_xpath(self): instance = ESAPI.encoder() self.assertEquals(None, instance.encode_for_xpath(None)) self.assertEquals("'or 1=1", instance.encode_for_xpath("'or 1=1"))
def get_master_salt(self): return ESAPI.encoder().decode_from_base64(settings.Encryptor_MasterSalt)
def get_master_salt(self): return ESAPI.encoder().decode_from_base64( settings.Encryptor_MasterSalt)
def test_encode_for_ldap(self): instance = ESAPI.encoder() self.assertEquals(None, instance.encode_for_ldap(None)) self.assertEquals("Hi This is a test #��", instance.encode_for_ldap("Hi This is a test #��") ,"No special characters to escape") self.assertEquals("Hi \\00", instance.encode_for_ldap("Hi " + unichr(0)), "Zeros") self.assertEquals("Hi \\28This\\29 = is \\2a a \\5c test # � � �", instance.encode_for_ldap("Hi (This) = is * a \\ test # � � �"), "LDAP Christams Tree")
def execute_system_command(self, executable, params, parent_dir, working_dir=None, codec=None, log_params=True): if codec is None: codec = self.codec if working_dir is None: working_dir = self.working_dir try: # Executable must exist if not os.path.exists(executable): raise ExecutorException( _("Execution failure"), _("No such executable: %(executable)s") % {"executable": executable} ) directory, filename = os.path.split(executable) # executable must use canonical path if not ESAPI.validator().is_valid_directory_path("Executor", directory, parent_dir, False): raise ExecutorException( _("Execution failure"), _("Directory did not pass validation: %(dir)s") % {"dir": directory} ) # Must be in approved list approved = ESAPI.security_configuration().get_allowed_executables() if executable not in approved: raise ExecutorException( _("Execution failure"), _( "Attempt to invoke executable that is not listed as an approved executable in configuration: %(executable)s" ) % {"executable": executable}, ) # Escape parameters params = [ESAPI.encoder().encode_for_os(codec, param) for param in params] # Working directory must exist if not os.path.exists(working_dir): raise ExecutorException( _("Execution failure"), _("No such working directory for running executable: %(dir)s") % {"dir": working_dir}, ) args = params args.insert(0, executable) start_time = datetime.now() proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=working_dir) if log_params: self.logger.warning( Logger.SECURITY_SUCCESS, _("Initiating executable %(args)s in %(dir)s") % {"args": str(args), "dir": working_dir}, ) else: self.logger.warning( Logger.SECURITY_SUCCESS, _("Initiating executable %(executable)s in %(dir)s") % {"executable": args[0], "dir": working_dir}, ) while proc.poll() is None and datetime.now() - start_time < self.max_running_time: time.sleep(1) if proc.poll() is None: # Kill the process because it ran too long proc.terminate() time.sleep(1) if proc.poll() is None: proc.kill() raise ExecutorException( _("Execution failure"), _("Process exceeded maximum running time and was killed: %(executable)s") % {"executable": executable}, ) else: # Process terminated in allotted timeframe stdout_and_err = proc.communicate() return stdout_and_err except Exception, extra: raise ExecutorException( _("Execution failure"), _("Exception thrown during execution of system command"), extra )
def log(self, level, event_type, message, exception=None): """ Log the message after optionally encoding any special characters that might be dangerous when viewed by an HTML based log viewer. Also encode any carriage returns and line feeds to prevent log injection attacks. This logs all the supplied parameters plus the user ID, user's source IP, a logging specific session ID, and the current date/time. It will only log the message if the current logging level is enabled, otherwise it will discard the message. @param level: the severity level of the security event @param event_type: the event_type of the event (SECURITY, FUNCTIONALITY, etc.) @param message: the message @param exception: an exception """ # Before we waste all kinds of time preparing this event for the # log, let check to see if its loggable if not self.pyLogger.isEnabledFor(level): return user = ESAPI.authenticator().current_user # create a random session number for the user to represent the # user's 'session', if it doesn't exist already sid = _("unknown") request = ESAPI.http_utilities().current_request if request is not None: session = request.session if session is not None: sid = session.get('ESAPI_SESSION', None) # if there is no session id for the user yet, create one # and store it in the user's session if sid is None: sid = str(ESAPI.randomizer().get_random_integer( 0, 1000000)) session['ESAPI_SESSION'] = sid # ensure there's something to log if message is None: message = "" # ensure no CRLF injection into logs for forging records clean = message.replace('\n', '_').replace('\r', '_') if ESAPI.security_configuration().get_log_encoding_required(): clean = ESAPI.encoder().encode_for_html(message) if message != clean: clean += " (Encoded)" extra = { 'eventType': str(event_type), 'eventSuccess': [_("SUCCESS"), _("FAILURE")][event_type.is_success()], 'user': user.account_name, 'hostname': user.last_host_address, 'sessionID': sid, } self.pyLogger.log(level, clean, extra=extra)