def load_configuration(self): """Load configuration""" # Get the correct set of modules global conf, resources, settings try: # If esapi.test.conf was already imported, we know we are running a unit # test, so we should load the testing configuration if 'esapi.test.conf' in sys.modules: self.log_special( _("WARNING - LOADING UNIT TEST CONFIGURATION! IF YOU ARE NOT RUNNING UNIT TESTS, SOMETHING IS VERY WRONG AND YOUR APP IS NOT SECURE!" )) import esapi.test.conf as conf import esapi.test.conf.resources as resources import esapi.test.conf.settings as settings else: import esapi.conf as conf import esapi.conf.resources as resources import esapi.conf.settings as settings except ImportError: raise ImportError, _( "Unable to import settings file - Check settings.py") self.log_special(_("Loaded ESAPI properties")) self.log_special(_(" ======Master Configuration======")) for option in dir(settings): if "Master" not in option and option[0] != "_": self.log_special(" | %(key)s = %(value)s" % { "key": option, "value": str(settings.__dict__[option]) })
def add_event(self, event_name, log_message): self.logger.warning( Logger.SECURITY_FAILURE, _("Security event %(name)s received: %(message)s") % { 'name': event_name, 'message': log_message, }) # Add the event to the current user, which may trigger a detector user = ESAPI.authenticator().current_user try: self.add_security_event(user, "event_" + event_name) except IntrusionException, extra: quota = ESAPI.security_configuration().get_quota("event_" + event_name) for action in quota.actions: message = (_( "User exceeded quota of %(count)s per %(interval)s seconds for event %(event_name)s. Taking actions %(actions)s" ) % { 'count': quota.count, 'interval': quota.interval, 'event_name': event_name, 'actions': quota.actions, }) self.take_security_action(action, message)
def assert_authorized_for_url(self, url): if len(self.url_map) == 0: self.url_map = self.load_rules(self.URLACFILE) if not self.match_rule(self.url_map, url): raise AccessControlException( _("Not authorized for URL"), _("Not authorized for URL: %(url)s") % {'url': url})
def assert_authorized_for_file(self, filepath): if len(self.file_map) == 0: self.file_map = self.load_rules(self.FILEACFILE) if not self.match_rule(self.file_map, filepath.replace("\\\\", "/")): raise AccessControlException( _("Not authorized for file"), _("Not authorized for file: %(file)s") % {'file': filepath})
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 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 __init__(self): Encryptor.__init__(self) self.logger = ESAPI.logger("DefaultEncryptor") # Hashing self.hash_algorithm = ESAPI.security_configuration().get_hash_algorithm() self.hash_iterations = ESAPI.security_configuration().get_hash_iterations() # Encryption self.encrypt_algorithm = ESAPI.security_configuration().get_encryption_algorithm() if self.encrypt_algorithm not in self.VALID_ENCRYPTION_ALGOS: raise EncryptionException( _("Encryption Failure - Unknown algorithm for encryption: %(algorithm)s") % {'algorithm' : self.encrypt_algorithm} ) self.encryption_key_length = ESAPI.security_configuration().get_encryption_key_length() self.master_salt = ESAPI.security_configuration().get_master_salt() # Public key crypto self.signing_algorithm = ESAPI.security_configuration().get_digital_signature_algorithm() if self.signing_algorithm not in self.VALID_SIGNING_ALGOS: raise EncryptionException( _("Failure to encrypt"), _("Encryption Failure - Unknown algorithm for signing: %(algorithm)s") % {'algorithm' : self.signing_algorithm} ) self.signing_key_length = ESAPI.security_configuration().get_digital_signature_key_length() # Key locations self.keys_location = os.path.realpath(ESAPI.security_configuration().get_encryption_keys_location()) + '/' self.keys_symmetric_location = self.keys_location + "symmetric" self.keys_asymmetric_private_location = self.keys_location + "asymmetric-private" self.keys_asymmetric_public_location = self.keys_location + "asymmetric-public"
def test_change_password(self): instance = ESAPI.authenticator() old_password = '******' user = self.create_test_user(password=old_password) print (_("Hash of %(old_password)s = %(hash)s") % {'old_password' : old_password, 'hash' : instance.get_hashed_password(user)}) password1 = "SomethingElse34#$" user.change_password(old_password, password1, password1) print (_("Hash of %(password)s = %(hash)s") % {'password' : password1, 'hash' : instance.get_hashed_password(user)}) self.assertTrue(user.verify_password(password1)) self.assertFalse(user.verify_password(old_password)) password2 = "YetAnother56%^" user.change_password(password1, password2, password2) print (_("Hash of %(password)s = %(hash)s") % {'password' : password2, 'hash' : instance.get_hashed_password(user)}) self.assertTrue(user.verify_password(password2)) self.assertFalse(user.verify_password(password1)) try: user.change_password(password2, password1, password1) # Should not be able to re-use a password self.fail() except AuthenticationException: pass self.assertFalse(user.verify_password("badpass"))
def get_direct_reference(self, indirect): if self.itod.has_key(indirect): return self.itod[indirect] raise AccessControlException( _("Access denied"), _("Request for invalid indirect reference: %(ref)s") % {"ref": indirect} )
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 set_remember_token(self, password, max_age, domain, path, request=None, response=None): if request is None: request = self.current_request if response is None: response = self.current_response user = ESAPI.authenticator().current_user try: self.kill_cookie(self.REMEMBER_TOKEN_COOKIE_NAME, request, response) # Seal already contains random data clear_token = user.account_name + "|" + password expiry = datetime.now() + timedelta(seconds=max_age) crypt_token = ESAPI.encryptor().seal(clear_token, expiry) morsel = Cookie.Morsel() morsel.value = crypt_token morsel['max-age'] = max_age morsel['domain'] = domain morsel['path'] = path response.cookies[self.REMEMBER_TOKEN_COOKIE_NAME] = morsel self.logger.info( Logger.SECURITY_SUCCESS, _("Enabled remember me token for %(user)s") % {'user' : user.account_name} ) return crypt_token except IntegrityException, extra: self.logger.warning( Logger.SECURITY_FAILURE, _("Attempt to set remember me token failed for %(user)s") % {'user' : user.account_name}, extra )
def assert_authorized_for_data(self, action, key): if len(self.data_map) == 0: self.data_map = self.load_rules(self.DATAACFILE) if not self.match_rule(self.data_map, key, action): raise AccessControlException( _("Not authorized for data"), _("Not authorized for data: %(data)s") % {'data': key})
def load_configuration(self): """Load configuration""" # Get the correct set of modules global conf, resources, settings try: # If esapi.test.conf was already imported, we know we are running a unit # test, so we should load the testing configuration if 'esapi.test.conf' in sys.modules: self.log_special(_("WARNING - LOADING UNIT TEST CONFIGURATION! IF YOU ARE NOT RUNNING UNIT TESTS, SOMETHING IS VERY WRONG AND YOUR APP IS NOT SECURE!")) import esapi.test.conf as conf import esapi.test.conf.resources as resources import esapi.test.conf.settings as settings else: import esapi.conf as conf import esapi.conf.resources as resources import esapi.conf.settings as settings except ImportError: raise ImportError, _("Unable to import settings file - Check settings.py") self.log_special(_("Loaded ESAPI properties")) self.log_special(_(" ======Master Configuration======")) for option in dir(settings): if "Master" not in option and option[0] != "_": self.log_special(" | %(key)s = %(value)s" % {"key": option, "value": str(settings.__dict__[option])})
def decrypt_hidden_field(self, encrypted): try: return ESAPI.encryptor().decrypt(encrypted) except EncryptionException, extra: raise IntrusionException( _("Invalid request"), _("Tampering detected. Hidden field data did not decrypt properly."), extra )
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 add_role(self, role): """ If role is a string, it will be lower()'d. """ if isinstance(role, str): role = role.lower() if ESAPI.validator().is_valid_input("addRole", role, "RoleName", DefaultUser.MAX_ROLE_LENGTH, False): if role not in self._roles: self._roles.append(role) self.logger.info( Logger.SECURITY_SUCCESS, _("Role %(role_name)s added to %(account_name)s") % { 'role_name': role, 'account_name': self.account_name }) else: # Role already assigned pass else: raise AuthenticationAccountsException( _("Add role failed"), _("Attempt to add invalid role %(role_name)s to %(account_name)s" ) % { 'role_name': role, 'account_name': self.account_name })
def assert_authorized_for_function(self, function_name): if len(self.function_map) == 0: self.function_map = self.load_rules(self.FUNCTIONACFILE) if not self.match_rule(self.function_map, function_name): raise AccessControlException( _("Not authorized for function"), _("Not authorized for function: %(function)s") %
def add_role(self, role): """ If role is a string, it will be lower()'d. """ if isinstance(role, str): role = role.lower() if ESAPI.validator().is_valid_input("addRole", role, "RoleName", DefaultUser.MAX_ROLE_LENGTH, False): if role not in self._roles: self._roles.append(role) self.logger.info(Logger.SECURITY_SUCCESS, _("Role %(role_name)s added to %(account_name)s") % {'role_name' : role, 'account_name' : self.account_name}) else: # Role already assigned pass else: raise AuthenticationAccountsException( _("Add role failed"), _("Attempt to add invalid role %(role_name)s to %(account_name)s") % {'role_name' : role, 'account_name' : self.account_name})
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 get_direct_reference(self, indirect): if self.itod.has_key(indirect): return self.itod[indirect] raise AccessControlException( _("Access denied"), _("Request for invalid indirect reference: %(ref)s") % {'ref': indirect})
def assert_authorized_for_file(self, filepath): if len(self.file_map) == 0: self.file_map = self.load_rules(self.FILEACFILE) if not self.match_rule(self.file_map, filepath.replace("\\\\","/")): raise AccessControlException( _("Not authorized for file"), _("Not authorized for file: %(file)s") %
def assert_authorized_for_service(self, service_name): if len(self.service_map) == 0: self.service_map = self.load_rules(self.SERVICEACFILE) if not self.match_rule(self.service_map, service_name): raise AccessControlException( _("Not authorized for service"), _("Not authorized for service: %(service)s") %
def assert_authorized_for_service(self, service_name): if len(self.service_map) == 0: self.service_map = self.load_rules(self.SERVICEACFILE) if not self.match_rule(self.service_map, service_name): raise AccessControlException( _("Not authorized for service"), _("Not authorized for service: %(service)s") % {'service': service_name})
def assert_authorized_for_data(self, action, key): if len(self.data_map) == 0: self.data_map = self.load_rules(self.DATAACFILE) if not self.match_rule(self.data_map, key, action): raise AccessControlException( _("Not authorized for data"), _("Not authorized for data: %(data)s") %
def assert_authorized_for_url(self, url): if len(self.url_map) == 0: self.url_map = self.load_rules(self.URLACFILE) if not self.match_rule(self.url_map, url): raise AccessControlException( _("Not authorized for URL"), _("Not authorized for URL: %(url)s") % { 'url' : url } )
def decrypt_hidden_field(self, encrypted): try: return ESAPI.encryptor().decrypt(encrypted) except EncryptionException, extra: raise IntrusionException( _("Invalid request"), _("Tampering detected. Hidden field data did not decrypt properly." ), extra)
def sign(self, data): try: signer = keyczar.Signer.Read(self.keys_asymmetric_private_location) signature = signer.Sign(data) return signature except KeyczarError, err: raise EncryptionException(_("Problem signing"), _("Keyczar raised an error"), err)
def assert_authorized_for_function(self, function_name): if len(self.function_map) == 0: self.function_map = self.load_rules(self.FUNCTIONACFILE) if not self.match_rule(self.function_map, function_name): raise AccessControlException( _("Not authorized for function"), _("Not authorized for function: %(function)s") % {'function': function_name})
def decrypt(self, ciphertext): try: crypter = keyczar.Crypter.Read(self.keys_symmetric_location) plaintext = crypter.Decrypt(ciphertext) return plaintext except KeyczarError, err: raise EncryptionException(_("Problem decrypting"), _("Keyczar raised an error"), err)
def _set_last_host_address(self, address): if (self._last_host_address is not None and self._last_host_address != address): raise AuthenticationHostException( _("Host change"), _("User sessions just jumped from %(old)s to %(new)s") % {'old' : self._last_host_address, 'new' : address}) self._last_host_address = address
def decrypt(self, ciphertext): try: crypter = keyczar.Crypter.Read(self.keys_symmetric_location) plaintext = crypter.Decrypt(ciphertext) return plaintext except KeyczarError, err: raise EncryptionException( _("Problem decrypting"), _("Keyczar raised an error"), err )
def sign(self, data): try: signer = keyczar.Signer.Read(self.keys_asymmetric_private_location) signature = signer.Sign(data) return signature except KeyczarError, err: raise EncryptionException( _("Problem signing"), _("Keyczar raised an error"), err )
def __setitem__(self, context, validation_exception): if context is None: raise RuntimeError(_("context parameter cannot be None")) if validation_exception is None: raise RuntimeError(_("validation_exception parameter cannot be None")) if self.has_key(context): raise RuntimeError(_("Context %(context)s already exists, must be unique") % {'context' : context}) dict.__setitem__(self, context, validation_exception)
def _set_last_host_address(self, address): if (self._last_host_address is not None and self._last_host_address != address): raise AuthenticationHostException( _("Host change"), _("User sessions just jumped from %(old)s to %(new)s") % { 'old': self._last_host_address, 'new': address }) self._last_host_address = address
def __setitem__(self, context, validation_exception): if context is None: raise RuntimeError(_("context parameter cannot be None")) if validation_exception is None: raise RuntimeError( _("validation_exception parameter cannot be None")) if self.has_key(context): raise RuntimeError( _("Context %(context)s already exists, must be unique") % {'context': context}) dict.__setitem__(self, context, validation_exception)
def get_class_for_interface(self, interface): interface = interface.lower() prop = "ESAPI_%s" % (interface,) try: fqn = getattr(settings, prop) except AttributeError, extra: raise ConfigurationException( _('There is an error in the application configuration. See the security log for more details.'), _("Class for this interface not specified in settings: %(interface)s") % {'interface' : interface}, extra )
def get_class_for_interface(self, interface): interface = interface.lower() prop = "ESAPI_%s" % (interface, ) try: fqn = getattr(settings, prop) except AttributeError, extra: raise ConfigurationException( _('There is an error in the application configuration. See the security log for more details.' ), _("Class for this interface not specified in settings: %(interface)s" ) % {'interface': interface}, extra)
def get_validation_pattern(self, key): value = getattr(settings, "Validator_" + key, None) if value is None: self.log_special(_("Trying to get validation pattern Validator_%(key)s failed because it doesn't exist") % {'key' : key}) return None try: return re.compile(value) except Exception, extra: self.log_special(_("SecurityConfiguration for Validator_%(key)s is not a valid regex in settings. Returning None.") % {'key' : key}) return None
def increment(self, count, interval): now = datetime.now() self.times.append(now) if len(self.times) > count: num_to_remove = len(self.times) - count self.times = self.times[num_to_remove:] if len(self.times) == count: first_event_time = self.times[0] if now - first_event_time < timedelta(seconds=interval): raise IntrusionException( _("Threshold exceeded"), _("Exceeded threshold for %(key)s") % {'key' : self.key} )
def get_valid_filename(self, context, input_, allow_none, error_list=None, allowed_extensions=None): try: if self.is_empty(input_): if allow_none: return None raise ValidationException( _("%(context)s: Input file name required") % {'context': context}, _("Input required: context=%(context)s, input=%(input)s") % { 'context': context, 'input': input_ }, context) # Do basic validation self.get_valid_input(context, input_, "Filename", 255, True) # Verify extensions if not allowed_extensions: allowed_extensions = ESAPI.security_configuration( ).get_allowed_file_extensions() file_ext = os.path.splitext(input_)[1] file_ext = file_ext.lower() if file_ext in allowed_extensions: return input_ else: raise ValidationException( _("%(context)s: Invalid file name does not have valid extension ( %(allowed_extensions)s )" ) % { 'context': context, 'allowed_extensions': allowed_extensions }, _("Invalid file name does not have valid extension ( %(allowed_extensions)s ): context=%(context)s, input=%(input)s" ) % { 'allowed_extensions': allowed_extensions, 'context': context, 'input': input_ }, context) except ValidationException, extra: if error_list is not None: error_list[context] = extra else: raise
def increment(self, count, interval): now = datetime.now() self.times.append(now) if len(self.times) > count: num_to_remove = len(self.times) - count self.times = self.times[num_to_remove:] if len(self.times) == count: first_event_time = self.times[0] if now - first_event_time < timedelta(seconds=interval): raise IntrusionException( _("Threshold exceeded"), _("Exceeded threshold for %(key)s") % {'key': self.key})
def unseal(self, seal): try: plaintext = self.decrypt(seal) parts = plaintext.split(":") if len(parts) != 4: raise EncryptionException(_("Invalid seal"), _("Seal was not formatted properly")) timestring = parts[0] expiration = datetime.fromtimestamp(float(timestring)) if datetime.now() > expiration: raise EncryptionException(_("Invalid seal"), _("Seal has expired")) random, data, sig = parts[1:4] sig_data = timestring + ":" + random + ":" + data if not self.verify_signature(sig, sig_data): raise EncryptionException(_("Invalid seal"), _("Seal integrity check failed")) return data except EncryptionException: raise except Exception, err: raise EncryptionException(_("Invalid seal"), _("Invalid seal"), err)
def unseal(self, seal): try: plaintext = self.decrypt(seal) parts = plaintext.split(":") if len(parts) != 4: raise EncryptionException( _("Invalid seal"), _("Seal was not formatted properly") ) timestring = parts[0] expiration = datetime.fromtimestamp(float(timestring)) if datetime.now() > expiration: raise EncryptionException( _("Invalid seal"), _("Seal has expired") ) random, data, sig = parts[1:4] sig_data = timestring + ":" + random + ":" + data if not self.verify_signature(sig, sig_data): raise EncryptionException( _("Invalid seal"), _("Seal integrity check failed") ) return data except EncryptionException: raise except Exception, err: raise EncryptionException( _("Invalid seal"), _("Invalid seal"), err )
def verify_csrf_token(self, request=None): if request is None: request = self.current_request user = ESAPI.authenticator().current_user # check if user authenticated with this request - no CSRF protection required if request.headers.has_key(user.csrf_token): return token = request.GET.get(self.CSRF_TOKEN_NAME) if user.csrf_token != token: raise IntrusionException( _("Authentication failed"), _("Possibly forged HTTP request without proper CSRF token detected") )
def get_validation_pattern(self, key): value = getattr(settings, "Validator_" + key, None) if value is None: self.log_special( _("Trying to get validation pattern Validator_%(key)s failed because it doesn't exist" ) % {'key': key}) return None try: return re.compile(value) except Exception, extra: self.log_special( _("SecurityConfiguration for Validator_%(key)s is not a valid regex in settings. Returning None." ) % {'key': key}) return None
def add_exception(self, exception): # Log the exception if hasattr(exception, 'get_log_message'): self.logger.warning( Logger.SECURITY_FAILURE, exception.get_log_message(), exception ) else: self.logger.warning( Logger.SECURITY_FAILURE, exception.message, exception ) if isinstance(exception, IntrusionException): return # Add the exception to the current user, which may trigger a # dector user = ESAPI.authenticator().current_user event_name = exception.__class__.__name__ try: self.add_security_event(user, event_name) except IntrusionException, extra: quota = ESAPI.security_configuration().get_quota(event_name) for action in quota.actions: message = (_("User exceeded quota of %(count)s per %(interval)s seconds for event %(event_name)s. Taking actions %(actions)s") % {'count' : quota.count, 'interval' : quota.interval, 'event_name' : event_name, 'actions' : quota.actions,}) self.take_security_action(action, message)
def unlock(self): self._locked = False self._failed_login_count = 0 self.logger.info( Logger.SECURITY_SUCCESS, _("Account unlocked: %(account_name)s") % {'account_name': self.account_name})
def assert_secure_request(self, request=None): if request is None: request = self.current_request if not request.is_secure(): raise AccessControlException( _("Insecure request received"), _("Receieved non-SSL request: %(url)s") % {'url' : request.url}) req_method = "POST" if request.method != req_method: raise AccessControlException( _("Insecure request received"), _("Receieved request using %(method)s when only %(req_method)s is allowed") % {'method' : request.method, 'req_method' : req_method} )
def create_test_user(self, username=None, password=None): """ Creates a test user. @return: the test user @raises AuthenticationException: """ if username is None: username = ESAPI.randomizer().get_random_string(8, DefaultEncoder.CHAR_ALPHANUMERICS) if password is None: password = ESAPI.randomizer().get_random_string(8, DefaultEncoder.CHAR_ALPHANUMERICS) while True: try: ESAPI.authenticator().verify_password_strength(password) except: password = ESAPI.randomizer().get_random_string(8, DefaultEncoder.CHAR_ALPHANUMERICS) else: break caller = inspect.stack()[2][3] print (_("Creating user %(username)s for %(caller)s") % {'username' : username, 'caller' : caller}) # Not sure if User tests should be coupled with Authenticator... user = ESAPI.authenticator().create_user(username, password, password) return user
def __init__(self): self.logger = ESAPI.logger("Executor") self.working_dir = ESAPI.security_configuration().get_working_directory() self.max_running_time = ESAPI.security_configuration().get_max_running_time() if os.name == "nt": self.logger.warning( Logger.SECURITY_SUCCESS, _("Using WindowsCodec for Executor. If this is not running on Windows, this could allow for injection"), ) self.codec = WindowsCodec() else: self.logger.warning( Logger.SECURITY_SUCCESS, _("Using UnixCodec for Executor. If this is not running on Unix, this could allow injection"), ) self.codec = UnixCodec()
def __init__(self, codecs=None): """ Instantiates a new DefaultEncoder. @param codecs: : a list of codec instances to use for canonicalization """ Encoder.__init__(self) self.html_codec = HTMLEntityCodec() self.percent_codec = PercentCodec() self.javascript_codec = JavascriptCodec() self.vbscript_codec = VBScriptCodec() self.css_codec = CSSCodec() self.ldap_codec = LDAPCodec() self.ldap_dn_codec = LDAPDNCodec() self.logger = ESAPI.logger("Encoder") # Used for canonicalization self.codecs = [] if codecs is None: self.codecs.append(self.html_codec) self.codecs.append(self.percent_codec) self.codecs.append(self.javascript_codec) # Leaving out css_codec because it eats / characters # Leaving out vbscript_codec because it eats " characters else: for codec in codecs: if not isinstance(codec, Codec): raise TypeError( _("Codecs in list must be instances of children of Codec" )) self.codecs.append(codec)
def add_exception(self, exception): # Log the exception if hasattr(exception, 'get_log_message'): self.logger.warning(Logger.SECURITY_FAILURE, exception.get_log_message(), exception) else: self.logger.warning(Logger.SECURITY_FAILURE, exception.message, exception) if isinstance(exception, IntrusionException): return # Add the exception to the current user, which may trigger a # dector user = ESAPI.authenticator().current_user event_name = exception.__class__.__name__ try: self.add_security_event(user, event_name) except IntrusionException, extra: quota = ESAPI.security_configuration().get_quota(event_name) for action in quota.actions: message = (_( "User exceeded quota of %(count)s per %(interval)s seconds for event %(event_name)s. Taking actions %(actions)s" ) % { 'count': quota.count, 'interval': quota.interval, 'event_name': event_name, 'actions': quota.actions, }) self.take_security_action(action, message)
def add_cookie(self, response=None, **kwargs): if response is None: response = self.current_response if not kwargs.has_key('secure'): if ESAPI.security_configuration().get_force_secure_cookies(): kwargs['secure'] = True if not kwargs.has_key('httponly'): if ESAPI.security_configuration().get_force_http_only_cookies(): kwargs['httponly'] = True # Validate the key and value errors = ValidationErrorList() safe_key = ESAPI.validator().get_valid_input("cookie name", kwargs['key'], "HTTPCookieName", 50, False, errors) safe_value = ESAPI.validator().get_valid_input("cookie value", kwargs['value'], "HTTPCookieValue", 5000, False, errors) kwargs['key'] = safe_key kwargs['value'] = safe_value # If no errors, set the cookie if len(errors) == 0: response.set_cookie(**kwargs) return # Error! self.logger.warning( Logger.SECURITY_FAILURE, _("Attempt to add unsafe data to cookie (skip mode). Skipping cookie and continuing.") )
def verify_csrf_token(self, request=None): if request is None: request = self.current_request user = ESAPI.authenticator().current_user # check if user authenticated with this request - no CSRF protection required if request.headers.has_key(user.csrf_token): return token = request.GET.get(self.CSRF_TOKEN_NAME) if user.csrf_token != token: raise IntrusionException( _("Authentication failed"), _("Possibly forged HTTP request without proper CSRF token detected" ))
def add_blacklist_pattern(self, pattern_string): try: pattern = re.compile(pattern_string) self.blacklist_patterns.append(pattern) except Exception, extra: raise RuntimeError( _("Validation misconfiguration, problem with specified pattern: %(pattern)s" ) % {'pattern': pattern_string}, extra)
def get_valid(self, context, input_, error_list=None): try: # check null if input_ is None or len(input_) == 0: if self.allow_none: return None raise ValidationException( _("%(context)s: Input credit card required") % {'context': context}, _("Input credit card required: context=%(context)s, input=%(input)s" ) % { 'context': context, 'input': input_ }, context) # canonicalize canonical = self.ccrule.get_valid(context, input_) digits_only = ''.join( [char for char in canonical if char.isdigit()]) # Luhn alogrithm checking sum_ = 0 times_two = False for digit in reversed(digits_only): digit = int(digit) assert 0 <= digit <= 9 if times_two: digit *= 2 if digit > 9: digit -= 9 sum_ += digit times_two = not times_two if (sum_ % 10) != 0: raise ValidationException( _("%(context)s: Invalid credit card input") % {'context': context}, _("Invalid credit card input. Credit card number did not pass Luhn test: context=%(context)s" ) % {'context': context}, context) return digits_only except ValidationException, extra: if error_list is not None: error_list[context] = extra else: raise
def get_header(self, name, request=None): if request is None: request = self.current_request value = request[name] return ESAPI.validator().get_valid_input( _("HTTP header value: %(value)s") % {'value': value}, value, "HTTPHeaderValue", 150, False)
def get_valid(self, context, input_, error_list=None): try: # check null if input_ is None or len(input_) == 0: if self.allow_none: return None raise ValidationException( _("%(context)s: Input credit card required") % {'context' : context}, _("Input credit card required: context=%(context)s, input=%(input)s") % {'context' : context, 'input' : input_}, context ) # canonicalize canonical = self.ccrule.get_valid(context, input_) digits_only = ''.join([char for char in canonical if char.isdigit()]) # Luhn alogrithm checking sum_ = 0 times_two = False for digit in reversed(digits_only): digit = int(digit) assert 0 <= digit <= 9 if times_two: digit *= 2 if digit > 9: digit -= 9 sum_ += digit times_two = not times_two if (sum_ % 10) != 0: raise ValidationException( _("%(context)s: Invalid credit card input") % {'context' : context}, _("Invalid credit card input. Credit card number did not pass Luhn test: context=%(context)s") % {'context' : context}, context ) return digits_only except ValidationException, extra: if error_list is not None: error_list[context] = extra else: raise