PHP Classes

Improving PHP Password Authentication Robustness of a PHP Framework - PHP User Credentials package blog

Recommend this page to a friend!
  All package blogs All package blogs   PHP User Credentials PHP User Credentials   Blog PHP User Credentials package blog   RSS 1.0 feed RSS 2.0 feed   Blog Improving PHP Passwor...  
  Post a comment Post a comment   See comments See comments (0)   Trackbacks (0)  

Author:

Viewers: 210

Last month viewers: 3

Package: PHP User Credentials

Categories: Password Management And Policy

The PHP User Credentials package provides several means to make password generation and the user authentication process more robust, helping to prevent several types of security problems.

Read this article to learn how to use this class as a plugin to improve the security capabilities of a PHP framework, in this example the OpenBiz-Cubi Framework.




Loaded Article

Contents

Introduction

Plugins for PHP Frameworks

Openbiz-Cubi Overview

Developing the UserCredential Plugin for Openbiz-Cubi

Pseudo Code of the Login Process

Conclusion


Introduction

The PHP User Credentials package is able to integrate password entropy and policy checks into an existing application to make it more robust in terms of the security of the user passwords and policies that make it harder to exploit eventual security breaches.

This article demonstrates how to integrate the PHP User Credentials package as a plug in into an existing framework, in this example the Openbiz-Cubi framework, a data driven framework.

Plugins for PHP Frameworks

PHP Web Frameworks have helped propelled the language into an Enterprise ready state, providing several components as well as reducing the time to build applications by providing reusable components.

Most PHP frameworks support integration of other third party libraries or packages through a plugin architecture.

Based on the definition from the Wikipedia, in computing, a plug-in (or extension or add-on) is a software component that adds a specific feature to an existing application. When an application supports plug-ins, it enables customization.

As per the above definition, plugins have varying names. In Symfony 1 they are known as plugins and in version 2 as bundles, in Yii as extensions, in Openbiz-Cubi they are known as services.

Openbiz-Cubi Overview

Openbiz-Cubi is a PHP based rapid application development (RAD) platform. The goal of Openbiz-Cubi is to provide a set of commonly used modules and tools at a platform level to boost productivity. Openbiz-Cubi is metadata driven, and is heavily inspired by JSF and Struts Java Frameworks.

The platform is a full stack framework. Once installed it already provides an administration module complete with UI for various tasks like creating users.

The framework supports module development via generation of component metadata from XML files, thus with minimal programming effort.

Like in Java, modules are referred to by the notation package.subpackage.ClassName, which refer to the file in the location package/subpackage/ClassName.php located in modules folder of the framework.

Likewise, service.usercredentialService refers to a file usercredentialService.php located in the service directory of the framework.

Component view and model files are located inside a module folder. Therefore. user.view.LoginView refers to a metadata configuration for a view called LoginView.xml file located in the module folder user/view; and  system.do.UserDO refers to a model file UserDO.xml in the system/do directory. Note that do stands for data object.

A detailed description of the framework is beyond the scope of this article. However, the main principles of the framework are metadata oriented development, MVC architecture, Object-Relational Mapping and plugin services.

Notice that the framework has claimed ability to work with different database vendors, but will need some work on your end if you are not using MySQL, MariaDB or Percona. I recommend using MySQL or its variants.

For a description of how to install the framework, visit the documentation Wiki page.

Developing the UserCredential Plugin for Openbiz-Cubi

a) Default Openbiz-Cubi Authentication Flow Of Control

The first step is to quickly explain the default authentication flow of control in Openbiz-Cubi.

Logging In

Action

View

Form

Model

Class

Service (Plugin)

User visits application home screen, whose relative path is /user/login

user. view. LoginView

user. form. LoginForm


user. form. LoginForm


user.form.LoginForm class calls the authService, which is the default authentication plugin for the Openbiz-Cubi framework




user. form. LoginForm

authService

authService performs username against password check using the method checkPassword() on the User table set the in system.do.UserDO metadata file and redirects to default home page or issues login error as an inline div on the login screen



system. do. UserDO


authService

Session Authentication


View

Form

Model

Class

Service (Plugin)

User visits a resource that requires authentication






The application Controller (BizController) calls the method BizSystem::getUserProfile.




BizController

BizSystem

profileService

profileService authenticates user using getProfileByCookie() method. This in turn calls authenticateUserByCookies() method provided by authService; provided the session is not exceeded the time limit set by the session service




services. profileService

authService

If session is not expired and the cookies checked out, profileService builds array of the users profile, and returns it to BizSystem else redirects to the view for session expired (or could not access page using credentials)





profileService

Password Change


View

Form

Model

Class

Service (Plugin)

User visits the location /myaccount/ reset_password in the Openbiz-Cubi application

myaccount. view. ResetPasswordView

myaccount. form. ResetPasswordForm

system. do. UserDO

myaccount. form. ResetPasswordForm


User Edits and Saves new password through the ResetPassword() method provided by the class myaccount. form. Reset PasswordForm. A notification of success in edit is provided to user via an inline div element on the Password change form

myaccount. view. ResetPasswordView

myaccount. form. ResetPasswordForm

system. do. UserDO

myaccount. form. ResetPasswordForm


b. Customized Openbiz-Cubi Authentication Flow Of Control

In this step, we explain how to plug in the support for UserCredential service in the various processes described in section a.

Schema Changes

To support the new UserCredential Service, you will need some additional tables. Below is the SQL to create these tables, which have been tested in MySQL and its variants.

SQL

DESCRIPTION

CREATE TABLE `usercredential_userstatetype` (

  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

  `description` varchar(255) NOT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `description_UNIQUE` (`description`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;

INSERT INTO usercredential_userstatetype VALUES (‘1’,’USERCREDENTIAL ACCOUNTSTATE LOGGEDOUT’);

INSERT INTO usercredential_userstatetype VALUES (‘2’,’USERCREDENTIAL ACCOUNTSTATE LOGGEDIN’);

INSERT INTO usercredential_userstatetype VALUES (‘3’,’USERCREDENTIAL ACCOUNTSTATE LOCKED1’);

INSERT INTO usercredential_userstatetype VALUES (‘4’,’USERCREDENTIAL ACCOUNTSTATE LOCKED2’);

INSERT INTO usercredential_userstatetype VALUES (‘5’,’USERCREDENTIAL ACCOUNTSTATE RESET’);

INSERT INTO usercredential_userstatetype VALUES (‘6’,’USERCREDENTIAL ACCOUNTSTATE SUSPENDED’);

INSERT INTO usercredential_userstatetype VALUES (‘7’,’USERCREDENTIAL ACCOUNTSTATE WEAKPASSWD’);

This table stores the states a user account can have in the system. The primary keys of the values in this table are symmetrical to the USERCREDENTIAL_USERSATE* named constants that come in the file config/NamedConstant.php which is in the PHPUserCredential package. As such, it is imperative to run the INSERT SQL on the left as well.

NB: For additional security, you may give the database user under which the application runs only read only rights at table level for this particular table, as well as usercredential_passwordhistorytype table.

CREATE TABLE `usercredential_passwordhistorytype` (

  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

  `description` varchar(255) NOT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `description_UNIQUE` (`description`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1

INSERT INTO usercredential_passwordhistorytype VALUES(‘1’,’ADMIN RESET’);

INSERT INTO usercredential_passwordhistorytype VALUES(‘2’,’USER CHANGE’);

This table stores values that represent the circumstance under which a user password may change. The two entries ADMIN RESET and USER CHANGE should be included by default, so you should run the INSERT queries as well

 CREATE TABLE `usercredential_passwordhistory` (

  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

  `user_id` int(10) unsigned NOT NULL,

  `history_type_id` int(11) unsigned NOT NULL,

  `password` varchar(60) NOT NULL,

  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY (`id`),

  UNIQUE KEY `usercredentialpasswordhistory_UNIQUE_1` (`user_id`,`timestamp`),

  KEY `passwordhistorytype_passwordhistory_fk1_idx` (`history_type_id`),

  CONSTRAINT `passwordhistorytype_passwordhistory_fk1` FOREIGN KEY (`history_type_id`) REFERENCES `usercredential_passwordhistorytype` (`id`) ON UPDATE CASCADE

) ENGINE=InnoDB DEFAULT CHARSET=latin1

Stores the historical password hashes against which password history changes are checked. It will need to be maintained, as may grow large over time. I shall discuss this in another post

CREATE TABLE `usercredential_userstate` (

  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,

  `user_id` int(10) unsigned NOT NULL,

  `userstatetype_id` int(11) unsigned NOT NULL,

  `timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

  `description` varchar(255) NOT NULL,

  PRIMARY KEY (`id`),

  UNIQUE KEY `usercredentialuserstate_UNIQUE_1` (`user_id`,`timestamp`,`userstatetype_id`),

  KEY `userstatetype_usercredential_fk1_idx` (`userstatetype_id`),

  CONSTRAINT `userstatetype_usercredential_fk1` FOREIGN KEY (`userstatetype_id`) REFERENCES `usercredential_userstatetype` (`id`) ON UPDATE CASCADE

) ENGINE=InnoDB  DEFAULT CHARSET=latin1

Each change in state in a user table is recorded in this table. The user state table is the most frequently accessed by applications in terms of reads. It also is the table that grows fastest, so maintenance is required. We will cover that in another topic going forward.

ALTER TABLE user

ADD COLUMN  `lastpasswordchange` DATETIME NULL DEFAULT NULL,

ADD COLUMN `failedlogincount` TINYINT(1) NULL DEFAULT NULL,

ADD COLUMN `lastfailedlogin` DATETIME NULL DEFAULT NULL

The Openbiz-Cubi framework comes with a user table which is created when you install the framework. The ALTER script adds three columns that enable us plug in the account policies to support PHPUserCredential service. If you are not comfortable altering the default table, you may have this in a separate table and maintain a 1 - 1 relationship to the user table.

Logging In Via PHP UserCredentials


View

Form

Model

Class

Service (Plugin)

User visits application home screen, whose relative path is /user/login

user. view. LoginView

user. form. LoginForm


user. form. LoginForm


LoginForm class calls the authService, which is the default authentication plugin for the Openbiz-Cubi framework




user. form. LoginForm

authService

We have modified the authService method checkPassword() to authenticate  by using the usercredentialService method called authenticate(). This enables us to use the bcrypt authentication enforced by usercredentialService instead Openbiz-Cubi which uses unsalted one way hashes





user credential Service

If Login is not successful, we update user account state by calling usercredentialService method updateAccountState() and increment failed login attempts.

Else it continues by verifying user policy as described below.





user credential Service

usercredentialService will then build the userprofile. This is enabled by its method getUserProfile()  which returns a data array as required by the PHPUserCredential class. Example array is below:

array(
   "username" => "james",
   "password" => "m&$1eLe6Ke()", //Password provided by user when loggin in, else null if youre running this in session and not log in
   "fullname" => "James Rodriguez",
   "passhash" => "bcrypt",
   "passhist" => array( //These should be already stored as encrypted in your backend store :)
       \password_hash('abc', \PASSWORD_DEFAULT),
       \password_hash('def', \PASSWORD_DEFAULT),
       \password_hash('ghi', \PASSWORD_DEFAULT),
       \password_hash('jkl', \PASSWORD_DEFAULT),
       \password_hash('mno', \PASSWORD_DEFAULT),
       \password_hash('pqr', \PASSWORD_DEFAULT),
       \password_hash('stu', \PASSWORD_DEFAULT),
       \password_hash('vwx', \PASSWORD_DEFAULT),
       \password_hash('xyz', \PASSWORD_DEFAULT)
   ),
   "policyinfo"=>array(
       'failed_attempt_count' => 0,
       'password_ last_ changed_ datetime' => new \DateTime('2014-05-04'),
       'last_login_attempt_datetime'    => new \DateTime('2014-05-16 23:45:10')
   ),
   "account_state" => \USERCREDENTIAL_ ACCOUNTSTATE_ LOGGEDIN
);



system.do.UserDO

system. do. Usercredential passwordhistory typeDO

system. do. Usercredential passwordhistory

system. do. Usercredential userstatetypeDO

system. do. Usercredential userstateDO


user credential Service

usercredentialService performs performs password policy check e.g. for password expiry, account is locked, account is suspended, password is weak. It throws an Exception if there is an error, else returns flow of control to the LoginForm





user credential Service

LoginForm, if there are no exceptions, redirects to default page

If Exception was thrown, LoginForm should have try / catch block for UserCredentialException to handle such exceptions.



system.do.UserDO

user. form. LoginForm

Session Authentication

Action

View

Form

Model

Class

Service (Plugin)

User visits a resource that requires authentication






Controller calls the method BizSystem::getUserProfile.




BizController

profileService

profileService authenticates user using Cookies method supported by authService, provided the session is not exceeded the limit set by the session service




services.profileService

authService

Our authService is customized to delegate authentication to the usercredentialService





usercredentialService

UserCredentialService builds userProfile





usercredentialService

usercredentialService verifies the policy and if okay returns flow of control. If there is a policy issue, it throws a UserCredentialException





usercredentialService

If session is not expired and the cookies checked out, profileService builds array of the users profile, and returns it to BizSystem else redirects to the views for session expired (or could not access page using credentials)

If an Exception is thrown, BizSystem needs to handle UserCredentialException to take the appropriate action.




BizSystem

profileService

Password Change


View

Form

Model

Class

Service (Plugin)

User visits the location /myaccount /reset_password in the Openbiz-Cubi application

myaccount. view. ResetPasswordView

myaccount. form. ResetPasswordForm

system. do. UserDO

myaccount. form. Reset Password Form


User Edits and Saves new password.

myaccount. view. ResetPasswordView

myaccount. form. ResetPasswordForm

system. do. UserDO

myaccount. form. Reset Password Form


The Password Reset form class delegates the entropy and policy verification to UserCredentialService





user credential Service

On Success, user receives a popup. If failed, the Reset Class has to handle a User CredentialException in order to properly handle this e.g. entropy is not sufficient, password history error






Pseudo Code of the Login Process

- User visits Login View

- Login View loads Login Form

- User fills in username and password then Submits Form

- Log in Form submits data to the Log in Manager class (AJAX, POST)

- Log in Manager authenticates username / password via the PHP UserCredential package

- If authentication details have been verified then

        Build the userProfile array as required by PHP UserCredential

        Get the users Account state from userProfile array

        If users account state is \USERCREDENTIAL_ACCOUNTSTATE_SUSPENDED then

                Load notification on user interface that account is suspended

                Exit

        ElseIf users account state is \USERCREDENTIAL_ACCOUNTSTATE_LOCKED2 then

                Load notification on user interface that account is locked out indefinitely for multiple illegal attempts

                Exit

        ElseIf users account state is \USERCREDENTIAL_ACCOUNTSTATE_LOCKED1

                Get the boolean value of true if required seconds elapsed since account was locked has been attained, else false (provided by the processTempLockout() method in our Openbiz-Cubi plugin

                If seconds elapsed check returned true

                        Continue Processing

                Else

                        Load notification on user interface to user indicating the account has been temporarily locked out for illegal attempts and they should attempt to log in again in a few minutes

                        Exit

        ElseIf users account state is \USERCREDENTIAL_ACCOUNTSTATE_RESET or \USERCREDENTIAL_ACCOUNTSTATE_LOGGEDIN or \USERCREDENTIAL_ACCOUNTSTATE_LOGGEDOUT

                Try

                        Validate Entropy of the Password (Provided by validateEntropy() method of the Openbiz-Cubi plugin)

                Catch UserCredentialException

                        User interface should notify and redirect user to change the password as is not of required entropy

                Try

                        Validate Policy of the User Profile (Provided by validatePolicy() method of Openbiz-Cubi plugin). Based on the state, it will validate for password expiry

                Catch UserCredentialException

                        User interface should notify and redirect user to change the password due to policy restriction on the profile

                Update user account state to \USERCREDENTIAL_ACCOUNTSTATA_LOGGEDIN (provided by updateAccountState() method of the Openbiz-Cubi plugin

                Direct User to Home Screen

- Else if Authentication Failed

                Build the userProfile array as required by PHP UserCredential

                Get the users Account state from userProfile array

                Increment The Uses Account Failed Login state by 1 via updateUser() method of usercredentialService plugin

                Try

                        Validate user Profile against policy (provided by ValidatePolicy method)

                Catch

                        If Exception Code is \USERCREDENTIAL_ACCOUNTOPOLICY_ATTEMPTLIMIT1 Then

                                Update users account state to \USERCREDENTIAL_ACCOUNTSTATE_LOCKED1, i.e they are temporarily locked

                        ElseIf Exception Code is \USERCREDENTIAL_ACCOUNTPOLICY_ATTEMPTLIMIT2 Then

                                Update users account to \USERCREDENTIAL_ACCOUNTSTATE_LOCKED2, i.e they are indefinetly locked

                        Else

                                Update users account to \USERCREDENTIAL_ACCOUNTSTATE_LOGGEDOUT

                User Interface should inform user of the appropriate state of their account. If it is not yet one of the locked out states, it should invite them to attempt to log in again.

                Exit

Conclusion

When in college, I remember one of my classmates telling me “The Login process is long”. Not so nuch for the user, but he was studying Oracle at the moment and noted the authentication and authorization steps involved are quite many on the back end.

The UserCredential Service provides capability to implement the authentication policies in an environment where passwords are primary method used used to authenticate, strengthening the process by providing policies for the password management.

To use the package, you generally require ability to build the user profile, as well as to handle the exception codes returned by the package if it raises one. In a future post, we will implement the package as a Yii extension.

The Openbiz-Cubi authentication Service plugin can be downloaded here as part of the PHP User Credentials packageIt may also provide an illustration of how you can implement a plugin for your application.

If you liked this article or have questions regarding using this package with the Openbiz-Cubi or other frameworks, post a comment here.




You need to be a registered user or login to post a comment

Login Immediately with your account on:



Comments:

No comments were submitted yet.



  Post a comment Post a comment   See comments See comments (0)   Trackbacks (0)  
  All package blogs All package blogs   PHP User Credentials PHP User Credentials   Blog PHP User Credentials package blog   RSS 1.0 feed RSS 2.0 feed   Blog Improving PHP Passwor...