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.") )
예제 #2
0
    def test_is_valid_dir_path(self):
        encoder_class = ESAPI.security_configuration().get_class_for_interface('encoder')
        validator_class = ESAPI.security_configuration().get_class_for_interface('validator')
        encoder = encoder_class([HTMLEntityCodec()])
        instance = validator_class(encoder)
        
        if os.name == 'nt': # Windows
            # Windows paths that don't exist and thus should fail
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\ridiculous", "c:\\", False))
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\jeff", "c:\\", False))
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\temp\\..\\etc", "c:\\", False))
            
            # When the parent directory doesn't exist, these should fail
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\", "c:\\ridiculous", False))
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\", None, False))
            
            # Windows paths that should pass
            self.assertTrue(instance.is_valid_directory_path("test", "C:\\", "C:\\", False)) # Windows root directory
            self.assertTrue(instance.is_valid_directory_path("test", "C:\\Windows", "C:\\", False)) # Windows always exist directory
            
            # Should fail for files
            self.assertFalse(instance.is_valid_directory_path("test", "C:\\Windows\\System32\\cmd.exe", "C:\\", False)) # Windows command shell	
            
            # Testing case insensitivity between input and parent_dir
            self.assertTrue(instance.is_valid_directory_path("test", "C:\\", "c:\\", False)) # Windows root directory
            self.assertTrue(instance.is_valid_directory_path("test", "c:\\Windows", "C:\\", False)) # Windows always exist directory
            
            # Testing the verification of the parent directory
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\", "C:\\windows", False)) # Windows always exist directory
            self.assertFalse(instance.is_valid_directory_path("test", "C:\\", "C:\\windows", False)) # Windows always exist directory
            
            # Unix specific paths should not pass
            self.assertFalse(instance.is_valid_directory_path("test", "/tmp", "/", False))	# Unix Temporary directory
            self.assertFalse(instance.is_valid_directory_path("test", "/bin/sh", "/", False))	# Unix Standard shell	
            self.assertFalse(instance.is_valid_directory_path("test", "/etc/config", "/", False))
            
            # Unix specific paths that should not exist or work
            self.assertFalse(instance.is_valid_directory_path("test", "/etc/ridiculous", "/", False))
            self.assertFalse(instance.is_valid_directory_path("test", "/tmp/../etc", "/", False))
        else:
            # Windows paths should fail
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\ridiculous", "c:\\", False))
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\temp\\..\\etc", "c:\\", False))

            # Standard Windows locations should fail
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\", "c:\\", False))
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\Windows\\temp", "c:\\", False))
            self.assertFalse(instance.is_valid_directory_path("test", "c:\\Windows\\System32\\cmd.exe", "c:\\", False))
            
            # Unix specific paths should pass
            # Root
            self.assertTrue(instance.is_valid_directory_path("test", "/", "/", False))
            # /bin
            self.assertTrue(instance.is_valid_directory_path("test", "/bin", "/", False))
            
            # Unix specific paths that should not exist or work
            self.assertFalse(instance.is_valid_directory_path("test", "/etc/ridiculous", "/", False))
            self.assertFalse(instance.is_valid_directory_path("test", "/tmp/../etc", "/", False))
예제 #3
0
 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 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)
예제 #5
0
 def get_cc_rule(self, encoder):
     pattern = ESAPI.security_configuration().get_validation_pattern(
         "CreditCard")
     ccr = StringValidationRule("ccrule", encoder, pattern)
     ccr.set_maximum_length(CC_MAX_LENGTH)
     ccr.set_allow_none(False)
     return ccr
    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 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)
예제 #8
0
    def login_with_password(self, password):
        if password is None:
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Missing password: %(account_name)s") %
                {'account_name': self.account_name})

        # Don't let disabled users log in
        if not self.is_enabled():
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Disabled user attempt to login: %(account_name)s") %
                {'account_name': self.account_name})

        # Don't let locked users log in
        if self.is_locked():
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Locked user attempt to login: %(account_name)s") %
                {'account_name': self.account_name})

        # Don't let expired users log in
        if self.is_expired():
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Expired user attempt to login: %(account_name)s") %
                {'account_name': self.account_name})

        self.logout()
        if self.verify_password(password):
            self._logged_in = True
            ESAPI.http_utilities().change_session_identifier(
                ESAPI.current_request())
            ESAPI.authenticator().current_user = self
            self.last_login_time = datetime.now()
            self.last_host_address = ESAPI.http_utilities(
            ).get_current_request().remote_host
            self.logger.trace(
                Logger.SECURITY_SUCCESS,
                _("User logged in: %(account_name)s") %
                {'account_name': self.account_name})
        else:
            self._logged_in = False
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            if self.get_failed_login_count() >= ESAPI.security_configuration(
            ).get_allowed_login_attempts():
                self.lock()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Incorrect password provided for %(account_name)s") %
                {'account_name': self.account_name})
 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()
예제 #10
0
 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) 
예제 #11
0
 def make_file_validator(self):
     if DefaultValidator.file_validator is not None:
         return
     DefaultValidator.file_validator = 'fail'
     file_codecs = [HTMLEntityCodec(), PercentCodec()]
     encoder_class = ESAPI.security_configuration().get_class_for_interface('encoder')
     file_encoder = encoder_class(file_codecs)
     DefaultValidator.file_validator = DefaultValidator( file_encoder )
 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()
예제 #13
0
    def get_valid_file_content(self,
                               context,
                               input_,
                               max_bytes,
                               allow_none,
                               errors=None):
        """
        This implementation only verifies that the file size is less than or
        equal to the max_bytes specified in the parameter and less than the
        max bytes specified in ESAPI.conf.settings.
        """
        try:
            if self.is_empty(input_):
                if allow_none:
                    return None
                raise ValidationException(
                    _("%(context)s: Input required") % {'context': context},
                    _("Input required: context=%(context)s, input=%(input)s") %
                    {
                        'context': context,
                        'input': input_
                    }, context)

            esapi_max_bytes = ESAPI.security_configuration(
            ).get_allowed_file_upload_size()
            if len(input_) > esapi_max_bytes:
                raise ValidationException(
                    _("%(context)s: Invalid file content can not exceed %(max_bytes)s bytes"
                      ) % {
                          'context': context,
                          'max_bytes': esapi_max_bytes
                      },
                    _("Exceeded ESAPI max length of %(max_bytes) bytes by %(exceeded)s bytes"
                      ) % {
                          'max_bytes': esapi_max_bytes,
                          'exceeded': len(input_) - esapi_max_bytes
                      }, context)
            if len(input_) > max_bytes:
                raise ValidationException(
                    _("%(context)s: Invalid file content can not exceed %(max_bytes)s bytes"
                      ) % {
                          'context': context,
                          'max_bytes': max_bytes
                      },
                    _("Exceeded max_bytes of %(max_bytes)s bytes by %(exceeded)s bytes"
                      ) % {
                          'max_bytes': max_bytes,
                          'exceeded': len(input_) - max_bytes
                      }, context)

            return input_

        except ValidationException, extra:
            if errors is not None:
                errors[context] = extra
            else:
                raise
예제 #14
0
 def make_file_validator(self):
     if DefaultValidator.file_validator is not None:
         return
     DefaultValidator.file_validator = 'fail'
     file_codecs = [HTMLEntityCodec(), PercentCodec()]
     encoder_class = ESAPI.security_configuration().get_class_for_interface(
         'encoder')
     file_encoder = encoder_class(file_codecs)
     DefaultValidator.file_validator = DefaultValidator(file_encoder)
예제 #15
0
 def get_valid_input(self, context, input_, type_, max_length, allow_none, error_list=None):
     rvr = StringValidationRule( type_, self.encoder )
     pattern = ESAPI.security_configuration().get_validation_pattern(type_)
     if pattern is not None:
         rvr.add_whitelist_pattern(pattern)
     else:
         rvr.add_whitelist_pattern(type_)
         
     rvr.set_maximum_length(max_length)
     rvr.set_allow_none(allow_none)
     return rvr.get_valid(context, input_, error_list)
예제 #16
0
 def login_with_password(self, password):
     if password is None:
         self.last_failed_login_time = datetime.now()
         self.increment_failed_login_count()
         raise AuthenticationLoginException( _("Login failed"),
             _("Missing password: %(account_name)s") %
             {'account_name' : self.account_name})
          
     # Don't let disabled users log in
     if not self.is_enabled():
         self.last_failed_login_time = datetime.now()
         self.increment_failed_login_count()
         raise AuthenticationLoginException( _("Login failed"),
             _("Disabled user attempt to login: %(account_name)s") %
             {'account_name' : self.account_name})
             
     # Don't let locked users log in
     if self.is_locked():
         self.last_failed_login_time = datetime.now()
         self.increment_failed_login_count()
         raise AuthenticationLoginException( _("Login failed"),
             _("Locked user attempt to login: %(account_name)s") %
             {'account_name' : self.account_name})
             
     # Don't let expired users log in
     if self.is_expired():
         self.last_failed_login_time = datetime.now()
         self.increment_failed_login_count()
         raise AuthenticationLoginException( _("Login failed"),
             _("Expired user attempt to login: %(account_name)s") %
             {'account_name' : self.account_name})
             
     self.logout()
     if self.verify_password( password ):
         self._logged_in = True
         ESAPI.http_utilities().change_session_identifier( ESAPI.current_request() )
         ESAPI.authenticator().current_user = self
         self.last_login_time = datetime.now()
         self.last_host_address = ESAPI.http_utilities().get_current_request().remote_host
         self.logger.trace(Logger.SECURITY_SUCCESS, 
             _("User logged in: %(account_name)s") %
             {'account_name' : self.account_name})
     else:
         self._logged_in = False
         self.last_failed_login_time = datetime.now()
         self.increment_failed_login_count()
         if self.get_failed_login_count() >= ESAPI.security_configuration().get_allowed_login_attempts():
             self.lock()
         raise AuthenticationLoginException( _("Login failed"),
             _("Incorrect password provided for %(account_name)s") %
             {'account_name' : self.account_name})
예제 #17
0
    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
예제 #18
0
    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."
              ))
예제 #19
0
    def get_valid_input(self,
                        context,
                        input_,
                        type_,
                        max_length,
                        allow_none,
                        error_list=None):
        rvr = StringValidationRule(type_, self.encoder)
        pattern = ESAPI.security_configuration().get_validation_pattern(type_)
        if pattern is not None:
            rvr.add_whitelist_pattern(pattern)
        else:
            rvr.add_whitelist_pattern(type_)

        rvr.set_maximum_length(max_length)
        rvr.set_allow_none(allow_none)
        return rvr.get_valid(context, input_, error_list)
예제 #20
0
 def get_valid_file_content(self, context, input_, max_bytes, allow_none, errors=None ):
     """
     This implementation only verifies that the file size is less than or
     equal to the max_bytes specified in the parameter and less than the
     max bytes specified in ESAPI.conf.settings.
     """
     try:
         if self.is_empty(input_):
             if allow_none:
                 return None
             raise ValidationException( _("%(context)s: Input required") % 
                {'context' : context}, 
                _("Input required: context=%(context)s, input=%(input)s") % 
                {'context' : context,
                 'input' : input_}, 
                context )
     
         esapi_max_bytes = ESAPI.security_configuration().get_allowed_file_upload_size()
         if len(input_) > esapi_max_bytes:
             raise ValidationException( 
                 _("%(context)s: Invalid file content can not exceed %(max_bytes)s bytes") % 
                {'context' : context,
                 'max_bytes' : esapi_max_bytes}, 
                _("Exceeded ESAPI max length of %(max_bytes) bytes by %(exceeded)s bytes") % 
                {'max_bytes' : esapi_max_bytes,
                 'exceeded' : len(input_) - esapi_max_bytes}, 
               context )
         if len(input_) > max_bytes:
             raise ValidationException( 
                 _("%(context)s: Invalid file content can not exceed %(max_bytes)s bytes") % 
                {'context' : context,
                 'max_bytes' : max_bytes}, 
                _("Exceeded max_bytes of %(max_bytes)s bytes by %(exceeded)s bytes") % 
                {'max_bytes' : max_bytes,
                 'exceeded' : len(input_) - max_bytes}, 
                context )
         
         return input_
     
     except ValidationException, extra:
         if errors is not None:
             errors[context] = extra
         else:
             raise
 def add_security_event(self, user, event_name):
     """
     Adds a security event to the user. These events are used to check
     that the user has not reached the security thresholds set in the
     SecurityConfiguration.
     
     @param user: the user tha caused the event
     @param event_name: the name of the event that occurred.
     """
     if user.is_anonymous():
         return
         
     threshold = ESAPI.security_configuration().get_quota(event_name)
     if threshold is not None:
         event = user.event_map.get(event_name)
         if event is None:
             event = self.Event(event_name)
             user.event_map[event_name] = event
             
         event.increment(threshold.count, threshold.interval)
    def add_security_event(self, user, event_name):
        """
        Adds a security event to the user. These events are used to check
        that the user has not reached the security thresholds set in the
        SecurityConfiguration.
        
        @param user: the user tha caused the event
        @param event_name: the name of the event that occurred.
        """
        if user.is_anonymous():
            return

        threshold = ESAPI.security_configuration().get_quota(event_name)
        if threshold is not None:
            event = user.event_map.get(event_name)
            if event is None:
                event = self.Event(event_name)
                user.event_map[event_name] = event

            event.increment(threshold.count, threshold.interval)
 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 __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"
예제 #25
0
 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
예제 #26
0
class DefaultUser(User):
    """
    The reference implementation of the User interface. This implementation
    is pickled/shelved into a flat file.
    """

    IDLE_TIMEOUT_LENGTH = ESAPI.security_configuration(
    ).get_session_idle_timeout_length()

    ABSOLUTE_TIMEOUT_LENGTH = ESAPI.security_configuration(
    ).get_session_absolute_timeout_length()

    MAX_ROLE_LENGTH = 250

    def __init__(self, account_name):
        """
        Instantiates a new user.
        
        @param account_name: The name of this user's account.
        """
        User.__init__(self)

        self._account_name = None
        self._set_account_name(account_name)

        # Get random numbers until we find an unused account number
        # WARNING: This could cause in infinite loop if the number of users equals the keyspace of uids.
        while True:
            id = ESAPI.randomizer().get_random_integer(1)
            if id != 0 and not ESAPI.authenticator().exists(account_id=id):
                self._account_id = id
                break

        self.logger = ESAPI.logger("DefaultUser")
        self._screen_name = None
        self._csrf_token = self.reset_csrf_token()
        self._roles = []
        self._locked = False
        self._logged_in = False
        self._enabled = False
        self._last_host_address = None
        self._last_password_change_time = None
        self._last_login_time = datetime.min
        self._last_failed_login_time = datetime.min
        self._expiration_time = datetime.max
        self._sessions = []

        # Security event dictionary, used by the IntrusionDetector
        self.event_map = {}

        self._failed_login_count = 0
        self._locale = None

    # Login
    def login_with_password(self, password):
        if password is None:
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Missing password: %(account_name)s") %
                {'account_name': self.account_name})

        # Don't let disabled users log in
        if not self.is_enabled():
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Disabled user attempt to login: %(account_name)s") %
                {'account_name': self.account_name})

        # Don't let locked users log in
        if self.is_locked():
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Locked user attempt to login: %(account_name)s") %
                {'account_name': self.account_name})

        # Don't let expired users log in
        if self.is_expired():
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Expired user attempt to login: %(account_name)s") %
                {'account_name': self.account_name})

        self.logout()
        if self.verify_password(password):
            self._logged_in = True
            ESAPI.http_utilities().change_session_identifier(
                ESAPI.current_request())
            ESAPI.authenticator().current_user = self
            self.last_login_time = datetime.now()
            self.last_host_address = ESAPI.http_utilities(
            ).get_current_request().remote_host
            self.logger.trace(
                Logger.SECURITY_SUCCESS,
                _("User logged in: %(account_name)s") %
                {'account_name': self.account_name})
        else:
            self._logged_in = False
            self.last_failed_login_time = datetime.now()
            self.increment_failed_login_count()
            if self.get_failed_login_count() >= ESAPI.security_configuration(
            ).get_allowed_login_attempts():
                self.lock()
            raise AuthenticationLoginException(
                _("Login failed"),
                _("Incorrect password provided for %(account_name)s") %
                {'account_name': self.account_name})

    def logout(self):
        return ESAPI.authenticator().logout(self)

    def is_logged_in(self):
        return self._logged_in

    # Locale
    def _get_locale(self):
        return self._locale

    def _set_locale(self, locale):
        self._locale = locale

    locale = property(_get_locale, _set_locale)

    # Roles
    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 remove_role(self, role):
        if isinstance(role, str):
            role = role.lower()

        try:
            self._roles.remove(role)
            self.logger.trace(
                Logger.SECURITY_SUCCESS,
                _("Role %(role_name)s removed from %(account_name)") % {
                    'role_name': role,
                    'account_name': self.account_name
                })
        except ValueError:
            # Raise an exception?
            pass

    def is_in_role(self, role):
        if isinstance(role, str):
            role = role.lower()

        return role in self._roles

    def _get_roles(self):
        return tuple(self._roles)

    def _set_roles(self, roles):
        self._roles = list(roles)[:]

    roles = property(_get_roles,
                     _set_roles,
                     doc="The roles assigned to a particular user")

    def add_roles(self, roles):
        for role in roles:
            self.add_role(role)

    # Passwords
    def verify_password(self, password):
        return ESAPI.authenticator().verify_password(self, password)

    def change_password(self, old_password, new_password1, new_password2):
        ESAPI.authenticator().change_password(self, old_password,
                                              new_password1, new_password2)

    def _get_last_password_change_time(self):
        return self._last_password_change_time

    def _set_last_password_change_time(self, time):
        self._last_password_change_time = time
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("Set last password change time to %(time)s for %(account_name)s")
            % {
                'time': time,
                'account_name': self.account_name
            })

    last_password_change_time = property(
        _get_last_password_change_time,
        _set_last_password_change_time,
        doc="The time of the last password change for this user.")

    # Enable/Disable
    def disable(self):
        self._enabled = False
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("Account disabled: %(account_name)s") %
            {'account_name': self.account_name})

    def enable(self):
        self._enabled = True
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("Account enabled: %(account_name)s") %
            {'account_name': self.account_name})

    def is_enabled(self):
        return self._enabled

    # Account id
    def _get_account_id(self):
        return self._account_id

    account_id = property(_get_account_id, doc="The User's account ID")

    # Account name
    def _get_account_name(self):
        return self._account_name

    def _set_account_name(self, name):
        old = self.account_name
        self._account_name = name.lower()
        if old is not None:
            if old == "":
                old = "[nothing]"
            self.logger.info(
                Logger.SECURITY_SUCCESS,
                _("Account name changed from %(old)s to %(new)s") % {
                    'old': old,
                    'new': self.account_name
                })

    account_name = property(_get_account_name,
                            _set_account_name,
                            doc="The User's account name")

    # CSRF tokens
    def _get_csrf_token(self):
        return self._csrf_token

    csrf_token = property(_get_csrf_token, doc="The User's CSRF token")

    def reset_csrf_token(self):
        self._csrf_token = ESAPI.randomizer().get_random_string(
            8, Encoder.CHAR_ALPHANUMERICS)
        return self.csrf_token

    # Expiration
    def _get_expiration_time(self):
        return self._expiration_time

    def _set_expiration_time(self, expiration_time):
        self._expiration_time = expiration_time
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("Account expiration time was set to %(time)s for %(account_name)s"
              ) % {
                  'time': expiration_time,
                  'account_name': self.account_name
              })

    expiration_time = property(
        _get_expiration_time,
        _set_expiration_time,
        doc="The date and time that this User's account will expire")

    def is_expired(self):
        return self.expiration_time < datetime.now()

    # Failed logins
    def get_failed_login_count(self):
        return self._failed_login_count

    def increment_failed_login_count(self):
        self._failed_login_count += 1

    def _get_last_failed_login_time(self):
        return self._last_failed_login_time

    def _set_last_failed_login_time(self, time):
        self._last_failed_login_time = time
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("Set last failed login time to %(time)s for %(user)s") % {
                'time': time,
                'user': self.account_name
            })

    last_failed_login_time = property(
        _get_last_failed_login_time,
        _set_last_failed_login_time,
        doc="The date and time of the last failed login for the user.")

    # Host address
    def _get_last_host_address(self):
        if self._last_host_address is None:
            return "unknown"
        return self._last_host_address

    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

    last_host_address = property(_get_last_host_address,
                                 _set_last_host_address,
                                 doc="The last host address used by this user")

    # Login times
    def _get_last_login_time(self):
        return self._last_login_time

    def _set_last_login_time(self, time):
        self._last_login_time = time
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("Set last successful login time to %(time)s for %(account_name)s"
              ) % {
                  'time': time,
                  'account_name': self.account_name
              })

    last_login_time = property(
        _get_last_login_time,
        _set_last_login_time,
        doc="The date and time the user last successfully logged in.")

    # Screen names
    def _get_screen_name(self):
        return self._screen_name

    def _set_screen_name(self, new_screen_name):
        self._screen_name = new_screen_name
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("ScreenName changed to %(new)s for %(account_name)s") % {
                'new': new_screen_name,
                'account_name': self.account_name
            })

    screen_name = property(_get_screen_name,
                           _set_screen_name,
                           doc="The screen name or alias for the User")

    # Session
    def add_session(self, session):
        self._sessions.append(session)

    def remove_session(self, session):
        try:
            self._sessions.remove(session)
        except:
            pass

    def get_sessions(self):
        return tuple(self._sessions)

    # Anonymous user
    def is_anonymous(self):
        return False

    # Timeouts
    def is_session_absolute_timeout(self):
        session = ESAPI.http_utilities().current_request.session
        if session is None:
            return True

        deadline = session.creation_time + self.ABSOLUTE_TIMEOUT_LENGTH
        return datetime.now() > deadline

    def is_session_timeout(self):
        session = ESAPI.http_utilities().current_request.session
        if session is None:
            return True

        deadline = session.last_accessed_time + self.IDLE_TIMEOUT_LENGTH
        return datetime.now() > deadline

    # Locking
    def lock(self):
        self._locked = True
        self.logger.info(
            Logger.SECURITY_SUCCESS,
            _("Account locked: %(account_name)s") %
            {'account_name': self.account_name})

    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 is_locked(self):
        return self._locked

    def __getstate__(self):
        # Copy the object's state from self.__dict__ which contains
        # all our instance attributes. Always use the dict.copy()
        # method to avoid modifying the original state.
        state = self.__dict__.copy()
        # Remove the unpicklable entries.
        del state['logger']
        return state

    def __setstate__(self, state):
        """
        Restore unpickleable instance attributes like logger.
        """
        self.__dict__.update(state)
        self.logger = ESAPI.logger("DefaultUser")
 def set_content_type(self, response=None):
     if response is None:
         response = self.current_response
         
     response.content_type = ESAPI.security_configuration().get_response_content_type()
    def load_rules(self, rule_file):
        """
        Loads the access rules by storing them in a dictionary. This method
        reads the file specified by the rule_file parameter, ignoring any lines
        that begin with the '#' character as comments. Sections of the access
        rules file are split by the '|' character. The method loads all paths,
        replacing '\\\\' characters with '/' for uniformity, then loads the list
        of comma separated roles. The roles are validated to be sure they are
        within the length and character set limitations specified in the 
        validate_roles method. Then the permissions are stored for each item
        in the rules list.
        
        If the word 'allow' appears on the line, the specified roles are
        granted access to the data - otherwise, they will be denied access.
        
        Each path may only appear once in the access rules file. Any entry,
        after the first, containing the same path will be logged and ignored.
        
        @param rule_file: the name of the file that contains the access rules
        @return: a dictionary mapping path -> Rule object
        """
        ret = {}
        input_file = None
        try:
            filename = ESAPI.security_configuration().get_resource_file(
                rule_file)
            input_file = open(filename, 'r')
            line = ESAPI.validator().safe_read_line(input_file, 500)
            while line != '':
                line = line.strip()
                if len(line) > 0 and line[0] != '#':
                    rule = Rule()
                    parts = line.split('|')

                    rule.path = parts[0].strip().replace("\\\\", "/")

                    roles = parts[1].strip().lower().split(',')
                    roles = self.validate_roles(roles)
                    for role in roles:
                        rule.roles.append(role.strip())

                    action = parts[2].strip().lower()
                    if action == 'allow' or action == 'deny':
                        rule.allow = action == 'allow'
                    else:
                        for act in action.split(','):
                            rule.actions.append(act.strip())

                    if ret.has_key(rule.path):
                        self.logger.warning(
                            Logger.SECURITY_FAILURE,
                            _("Problem in access control file. Duplicate rule ignored: %(rule)s"
                              ) % {'rule': rule})
                    else:
                        ret[rule.path] = rule

                line = ESAPI.validator().safe_read_line(input_file, 500)

        except Exception, extra:
            raise
            self.logger.warning(
                Logger.SECURITY_FAILURE,
                _("Problem in access control file: %(file)s") %
                {'file': rule_file}, extra)
 def get_cc_rule(self, encoder):
     pattern = ESAPI.security_configuration().get_validation_pattern("CreditCard")
     ccr = StringValidationRule("ccrule", encoder, pattern)
     ccr.set_maximum_length(CC_MAX_LENGTH)
     ccr.set_allow_none(False)
     return ccr
 def get_new_arm(self, *args):
     klass = ESAPI.security_configuration().get_class_for_interface("access_reference_map")
     return klass(*args)
예제 #31
0
 def get_new_arm(self, *args):
     klass = ESAPI.security_configuration().get_class_for_interface(
         "access_reference_map")
     return klass(*args)
예제 #32
0
    def test_canonicalize(self):
        codecs = [HTMLEntityCodec(), PercentCodec()]
        encoder_class = ESAPI.security_configuration().get_class_for_interface('encoder')
        instance = encoder_class(codecs)
        
        # Test None paths
        self.assertEquals( None, instance.canonicalize(None))
        self.assertEquals( None, instance.canonicalize(None, True))
        self.assertEquals( None, instance.canonicalize(None, False))
        
        # test exception paths
        self.assertEquals( "%", instance.canonicalize("%25", True))
        self.assertEquals( "%", instance.canonicalize("%25", False))
        
        self.assertEquals( "%", instance.canonicalize("%25"))
        self.assertEquals( "%F", instance.canonicalize("%25F"))
        self.assertEquals( "<", instance.canonicalize("%3c"))
        self.assertEquals( "<", instance.canonicalize("%3C"))
        self.assertEquals( "%X1", instance.canonicalize("%X1"))

        self.assertEquals( "<", instance.canonicalize("&lt"))
        self.assertEquals( "<", instance.canonicalize("&LT"))
        self.assertEquals( "<", instance.canonicalize("&lt;"))
        self.assertEquals( "<", instance.canonicalize("&LT;"))
        
        self.assertEquals( "%", instance.canonicalize("&#37;"))
        self.assertEquals( "%", instance.canonicalize("&#37"))
        self.assertEquals( "%b", instance.canonicalize("&#37b"))

        self.assertEquals( "<", instance.canonicalize("&#x3c"))
        self.assertEquals( "<", instance.canonicalize("&#x3c;"))
        self.assertEquals( "<", instance.canonicalize("&#x3C"))
        self.assertEquals( "<", instance.canonicalize("&#X3c"))
        self.assertEquals( "<", instance.canonicalize("&#X3C"))
        self.assertEquals( "<", instance.canonicalize("&#X3C;"))

        # percent encoding
        self.assertEquals( "<", instance.canonicalize("%3c"))
        self.assertEquals( "<", instance.canonicalize("%3C"))

        # html entity encoding
        self.assertEquals( "<", instance.canonicalize("&#60"))
        self.assertEquals( "<", instance.canonicalize("&#060"))
        self.assertEquals( "<", instance.canonicalize("&#0060"))
        self.assertEquals( "<", instance.canonicalize("&#00060"))
        self.assertEquals( "<", instance.canonicalize("&#000060"))
        self.assertEquals( "<", instance.canonicalize("&#0000060"))
        self.assertEquals( "<", instance.canonicalize("&#60;"))
        self.assertEquals( "<", instance.canonicalize("&#060;"))
        self.assertEquals( "<", instance.canonicalize("&#0060;"))
        self.assertEquals( "<", instance.canonicalize("&#00060;"))
        self.assertEquals( "<", instance.canonicalize("&#000060;"))
        self.assertEquals( "<", instance.canonicalize("&#0000060;"))
        self.assertEquals( "<", instance.canonicalize("&#x3c"))
        self.assertEquals( "<", instance.canonicalize("&#x03c"))
        self.assertEquals( "<", instance.canonicalize("&#x003c"))
        self.assertEquals( "<", instance.canonicalize("&#x0003c"))
        self.assertEquals( "<", instance.canonicalize("&#x00003c"))
        self.assertEquals( "<", instance.canonicalize("&#x000003c"))
        self.assertEquals( "<", instance.canonicalize("&#x3c;"))
        self.assertEquals( "<", instance.canonicalize("&#x03c;"))
        self.assertEquals( "<", instance.canonicalize("&#x003c;"))
        self.assertEquals( "<", instance.canonicalize("&#x0003c;"))
        self.assertEquals( "<", instance.canonicalize("&#x00003c;"))
        self.assertEquals( "<", instance.canonicalize("&#x000003c;"))
        self.assertEquals( "<", instance.canonicalize("&#X3c"))
        self.assertEquals( "<", instance.canonicalize("&#X03c"))
        self.assertEquals( "<", instance.canonicalize("&#X003c"))
        self.assertEquals( "<", instance.canonicalize("&#X0003c"))
        self.assertEquals( "<", instance.canonicalize("&#X00003c"))
        self.assertEquals( "<", instance.canonicalize("&#X000003c"))
        self.assertEquals( "<", instance.canonicalize("&#X3c;"))
        self.assertEquals( "<", instance.canonicalize("&#X03c;"))
        self.assertEquals( "<", instance.canonicalize("&#X003c;"))
        self.assertEquals( "<", instance.canonicalize("&#X0003c;"))
        self.assertEquals( "<", instance.canonicalize("&#X00003c;"))
        self.assertEquals( "<", instance.canonicalize("&#X000003c;"))
        self.assertEquals( "<", instance.canonicalize("&#x3C"))
        self.assertEquals( "<", instance.canonicalize("&#x03C"))
        self.assertEquals( "<", instance.canonicalize("&#x003C"))
        self.assertEquals( "<", instance.canonicalize("&#x0003C"))
        self.assertEquals( "<", instance.canonicalize("&#x00003C"))
        self.assertEquals( "<", instance.canonicalize("&#x000003C"))
        self.assertEquals( "<", instance.canonicalize("&#x3C;"))
        self.assertEquals( "<", instance.canonicalize("&#x03C;"))
        self.assertEquals( "<", instance.canonicalize("&#x003C;"))
        self.assertEquals( "<", instance.canonicalize("&#x0003C;"))
        self.assertEquals( "<", instance.canonicalize("&#x00003C;"))
        self.assertEquals( "<", instance.canonicalize("&#x000003C;"))
        self.assertEquals( "<", instance.canonicalize("&#X3C"))
        self.assertEquals( "<", instance.canonicalize("&#X03C"))
        self.assertEquals( "<", instance.canonicalize("&#X003C"))
        self.assertEquals( "<", instance.canonicalize("&#X0003C"))
        self.assertEquals( "<", instance.canonicalize("&#X00003C"))
        self.assertEquals( "<", instance.canonicalize("&#X000003C"))
        self.assertEquals( "<", instance.canonicalize("&#X3C;"))
        self.assertEquals( "<", instance.canonicalize("&#X03C;"))
        self.assertEquals( "<", instance.canonicalize("&#X003C;"))
        self.assertEquals( "<", instance.canonicalize("&#X0003C;"))
        self.assertEquals( "<", instance.canonicalize("&#X00003C;"))
        self.assertEquals( "<", instance.canonicalize("&#X000003C;"))
        self.assertEquals( "<", instance.canonicalize("&lt"))
        self.assertEquals( "<", instance.canonicalize("&lT"))
        self.assertEquals( "<", instance.canonicalize("&Lt"))
        self.assertEquals( "<", instance.canonicalize("&LT"))
        self.assertEquals( "<", instance.canonicalize("&lt;"))
        self.assertEquals( "<", instance.canonicalize("&lT;"))
        self.assertEquals( "<", instance.canonicalize("&Lt;"))
        self.assertEquals( "<", instance.canonicalize("&LT;"))
        
        self.assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript%3Ealert%28%22hello%22%29%3B%3C%2Fscript%3E") )
        self.assertEquals( "<script>alert(\"hello\");</script>", instance.canonicalize("%3Cscript&#x3E;alert%28%22hello&#34%29%3B%3C%2Fscript%3E", False) )
        
        # javascript escape syntax
        js = [JavascriptCodec()]
        instance = encoder_class( js )

        self.assertEquals( "\0", instance.canonicalize("\\0"))
        self.assertEquals( "\b", instance.canonicalize("\\b"))
        self.assertEquals( "\t", instance.canonicalize("\\t"))
        self.assertEquals( "\n", instance.canonicalize("\\n"))
        self.assertEquals( unichr(0x0b), instance.canonicalize("\\v"))
        self.assertEquals( "\f", instance.canonicalize("\\f"))
        self.assertEquals( "\r", instance.canonicalize("\\r"))
        self.assertEquals( "\'", instance.canonicalize("\\'"))
        self.assertEquals( "\"", instance.canonicalize("\\\""))
        self.assertEquals( "\\", instance.canonicalize("\\\\"))
        self.assertEquals( "<", instance.canonicalize("\\<"))
        
        self.assertEquals( "<", instance.canonicalize("\\u003c"))
        self.assertEquals( "<", instance.canonicalize("\\U003c"))
        self.assertEquals( "<", instance.canonicalize("\\u003C"))
        self.assertEquals( "<", instance.canonicalize("\\U003C"))
        self.assertEquals( "<", instance.canonicalize("\\x3c"))
        self.assertEquals( "<", instance.canonicalize("\\X3c"))
        self.assertEquals( "<", instance.canonicalize("\\x3C"))
        self.assertEquals( "<", instance.canonicalize("\\X3C"))

        # css escape syntax
        # be careful because some codecs see \0 as null byte
        css = [CSSCodec()]
        instance = encoder_class( css )
        self.assertEquals( "<", instance.canonicalize("\\3c"));  # add strings to prevent null byte
        self.assertEquals( "<", instance.canonicalize("\\03c"))
        self.assertEquals( "<", instance.canonicalize("\\003c"))
        self.assertEquals( "<", instance.canonicalize("\\0003c"))
        self.assertEquals( "<", instance.canonicalize("\\00003c"))
        self.assertEquals( "<", instance.canonicalize("\\3C"))
        self.assertEquals( "<", instance.canonicalize("\\03C"))
        self.assertEquals( "<", instance.canonicalize("\\003C"))
        self.assertEquals( "<", instance.canonicalize("\\0003C"))
        self.assertEquals( "<", instance.canonicalize("\\00003C"))
예제 #33
0
 def __init__(self, test_name=""):
     """       
     @param test_name: the test name
     """     
     unittest.TestCase.__init__(self, test_name)
     self.user_class = ESAPI.security_configuration().get_class_for_interface('user')
예제 #34
0
    def set_content_type(self, response=None):
        if response is None:
            response = self.current_response

        response.content_type = ESAPI.security_configuration(
        ).get_response_content_type()
예제 #35
0
class DefaultHTTPUtilities(HTTPUtilities):
    """
    The default implementation of the HTTPUtilities class.
    
    Note: get_file_uploads() and send_redirect() are not implemented because
    they are highly dependent upon the framework.
    
    @author: Craig Younkins ([email protected])
    """

    max_bytes = ESAPI.security_configuration().get_allowed_file_upload_size()

    def __init__(self):
        self.logger = ESAPI.logger("HTTPUtilities")
        self.current_request = None
        self.current_response = None

    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 add_csrf_token(self, href):
        user = ESAPI.authenticator().current_user
        if user.is_anonymous():
            return href

        # If there are already parameters, append with an &, otherwise append
        # with a ?
        token = self.CSRF_TOKEN_NAME + "=" + user.csrf_token
        if href.find('?') == -1:
            # No params yet
            return href + "?" + token
        else:
            # href has params already
            return href + "&" + token

    def add_header(self, name, value, response=None):
        if response is None:
            response = self.current_response

        stripped_name = name.strip()
        stripped_value = value.strip()

        try:
            safe_name = ESAPI.validator().get_valid_input(
                "addHeader", stripped_name, "HTTPHeaderName", 20, False)
            safe_value = ESAPI.validator().get_valid_input(
                "addHeader", stripped_value, "HTTPHeaderValue", 500, False)
            response.headers[safe_name] = safe_value
        except ValidationException, extra:
            self.logger.warning(Logger.SECURITY_FAILURE,
                                _("Attempt to add invalid header denied"),
                                extra)
    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)
    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 load_rules(self, rule_file):
     """
     Loads the access rules by storing them in a dictionary. This method
     reads the file specified by the rule_file parameter, ignoring any lines
     that begin with the '#' character as comments. Sections of the access
     rules file are split by the '|' character. The method loads all paths,
     replacing '\\\\' characters with '/' for uniformity, then loads the list
     of comma separated roles. The roles are validated to be sure they are
     within the length and character set limitations specified in the 
     validate_roles method. Then the permissions are stored for each item
     in the rules list.
     
     If the word 'allow' appears on the line, the specified roles are
     granted access to the data - otherwise, they will be denied access.
     
     Each path may only appear once in the access rules file. Any entry,
     after the first, containing the same path will be logged and ignored.
     
     @param rule_file: the name of the file that contains the access rules
     @return: a dictionary mapping path -> Rule object
     """
     ret = {}
     input_file = None
     try:
         filename = ESAPI.security_configuration().get_resource_file(rule_file)
         input_file = open(filename, 'r')
         line = ESAPI.validator().safe_read_line(input_file, 500)
         while line != '':
             line = line.strip()
             if len(line) > 0 and line[0] != '#':
                 rule = Rule()
                 parts = line.split('|')
                 
                 rule.path = parts[0].strip().replace("\\\\", "/")
                 
                 roles = parts[1].strip().lower().split(',')
                 roles = self.validate_roles(roles)
                 for role in roles:
                     rule.roles.append(role.strip())
                     
                 action = parts[2].strip().lower()
                 if action == 'allow' or action == 'deny':
                     rule.allow = action == 'allow'
                 else:
                     for act in action.split(','):
                         rule.actions.append(act.strip())
                 
                 if ret.has_key(rule.path):
                     self.logger.warning( Logger.SECURITY_FAILURE,
                         _("Problem in access control file. Duplicate rule ignored: %(rule)s") % 
                         {'rule' : rule} )
                 else:
                     ret[rule.path] = rule
                     
             line = ESAPI.validator().safe_read_line(input_file, 500)
         
     except Exception, extra:
         raise
         self.logger.warning( Logger.SECURITY_FAILURE, 
             _("Problem in access control file: %(file)s") % 
             {'file' : rule_file},