Hack58.Apply Security by Role


Hack 58. Apply Security by Role

Use security roles to provide varying levels of access to your web application.

Not all users who approach a system have the same rights within that system. For example, some users can add and remove users, some can post, some can only read messages, and some can do a combination of all of these.

A proper role-based system not only restricts access to parts of the system, but also reduces the complexity of pages for users with restricted rights. The user should not be able to see links that she cannot use, and she should have links to the tasks appropriate for her. This hack demonstrates a fairly straightforward role-based security system.

Figure 6-18 shows the page flow among the different pages in the hack. The user starts on the index.php page, which has the login. That page submits to the login.php page, which checks the login information. If the login credentials are accepted, the user is logged in and is forwarded on to the welcome.php page. If the login credentials aren't accepted, the user is sent back to the index.php page. From the welcome.php page, the user can do one of two things. She can log out by clicking a link to the logout.php page, which dumps her session and sends her back to index.php, or she can try to go to the manage.php page directly; that page checks her credentials and sends her back to the welcome.php page if she doesn't have proper credentials.

Figure 6-18. The page flow in this hack


The welcome.php page also checks to see whether the user's session is valid. If the session is not valid, the user is sent back to the index.php page to log in. In other words, people can't bypass the login to get into the site simply by typing in a direct URL.

6.9.1. The Code

Save the code in Example 6-19 as dblib.php.

Example 6-19. The database library for the roles system
 <?php require_once( "DB.php" ); $dsn = 'mysql://root:password@localhost/roles'; $db =& DB::Connect( $dsn, array() ); if (PEAR::isError($db)) { die($db->getMessage()); } function get_db() { global $db; return $db; } function get_role_id( $role ) { global $db; $res = $db->query( "SELECT id FROM roles WHERE name=?", array( $role ) ); if ( $res != null ) { $res->fetchInto( $row ); return $row[0]; } return null; } function has_role( $user, $role ) {  global $db; $role_id = get_role_id( $role ); $res = $db->query( "SELECT user_id FROM user_role WHERE user_id=? AND role_  d=?", array( $user, $role_id ) ); if ( $res != null ) { $res->fetchInto( $row ); return ( (int)$row[0] == (int)$user ) ? true : false; } return false; } ?> 

Save the code in Example 6-20 as index.php.

Example 6-20. The login page
 <html> <head><title>Login</title></head> <body> <?php if ( $_GET['bad'] == 1 ) { ?> <font color="red">Bad login or password, please try again<br/></font> <?php } ?> <form action="login.php" method="post"> <table width="300" border="0" cellspacing="0" cellpadding="2"> <tr><td>User name:</td><td><input type="text" name="user" /></td></tr> <tr><td>Password:</td><td><input type="password" name="password" /></td></tr> <tr><td colspan="2"><center><input type="submit" value="Login" /></center></td></ tr> </table> </form> </body> </html> 

Save the code in Example 6-21 as login.php.

Example 6-21. The login processor
 <?php require_once( "dblib.php" ); $db = get_db(); $res = $db->query( "SELECT id FROM users WHERE name=? AND password=MD5(?)",    array( $_POST['user'], $_POST['password'] ) ); $row = array( null );  if ( $res != null )  $res->fetchInto( $row ); if ( $row[0] != null ) { session_start();  $_SESSION['user'] = $row[0];  header( "Location: welcome.php" ); } else { header( "Location: index.php?bad=1" );  } ?> 

Save the code in Example 6-22 as logout.php.

Example 6-22. The logout processor
 <?php session_destroy(); header( "Location: index.php" ); ?> 

Save the code in Example 6-23 as manage.php.

Example 6-23. A page that should be visible only to managers
 <?php require_once( "security.php" ); session_start(); if ( $_SESSION['user'] == null || $_SESSION['user'] < 1 ) { header( "Location: index.php" ); exit; } check_roles( $_SESSION['user'], array( 'manager' ) ); ?> <html> <body> From here you manage the users.<br/><br/> Back to the <a href="welcome.php">home page</a>. </body> </html> 

Save the code in Example 6-24 as security.php.

Example 6-24. The security library functions
 <?php require_once( "dblib.php" ); function check_roles( $user, $roles ) {   foreach( $roles as $role )    { if ( !has_role( $user, $role ) ) { ?>  You do not have permission to access this page.<br/><br/>  Return to the <a href="welcome.php">home page</a>.  <?php exit; }   }  }  ?> 

Save the code in Example 6-25 as users.sql.

Example 6-25. The SQL code for this example
 DROP TABLE IF EXISTS roles; CREATE TABLE roles (  id MEDIUMINT NOT NULL AUTO_INCREMENT,  name TEXT,  PRIMARY KEY( id ) ); DROP TABLE IF EXISTS users; CREATE TABLE users ( id MEDIUMINT NOT NULL AUTO_INCREMENT,  name TEXT,  password TEXT,  PRIMARY KEY( id ) ); DROP TABLE IF EXISTS user_role; CREATE TABLE user_role (  user_id MEDIUMINT,  role_id MEDIUMINT ); INSERT INTO roles VALUES ( 0, 'user' );  INSERT INTO roles VALUES ( 0, 'manager' ); INSERT INTO users VALUES ( 0, 'jack', MD5( 'toronto' ) );  INSERT INTO users VALUES ( 0, 'megan', MD5( 'seattle' ) ); INSERT INTO user_role VALUES ( 1, 1 );  INSERT INTO user_role VALUES ( 2, 1 );  INSERT INTO user_role VALUES ( 2, 2 ); 

Save the code in Example 6-26 as welcome.php.

Example 6-26. The home page for when a user has logged in
 <?php require_once( "dblib.php" ); session_start(); if ( $_SESSION['user'] == null || $_SESSION['user'] < 1 ) { header( "Location: index.php" ); exit; } $db = get_db(); $res = $db->query( "SELECT name FROM users WHERE id=?", array( $_SESSION['user'] ) ); $res->fetchInto( $row ); ?> <html> <head><title>Welcome</title></head> <body> Welcome <?php echo( $row[0] ); ?><br/><br/> <?php if ( has_role( $_SESSION['user'], 'manager' ) ) { ?> <a href="manage.php">Manage the users</a><br/><br/> <?php } ?> <a href="logout.php">Logout</a> </body> </html> 

The system starts with the index.php page, which presents the login form to the user. Once the user inputs his login name and password, the form is submitted to the login.php page, which processes the login. If the login is valid, the code sets up the session with the user ID. From there, the user is forwarded to the welcome.php page, which acts as his home page once he has logged in.

The welcome.php page has a link to the manage.php page. That page is valid only if you are a manager, so the code for that page checks the permissions. If the user doesn't have the manager role, he is sent back to the home page.

The last thing to handle is logging out. That works through the logout.php script, which ends the user's session and sends him back to the login page.

The code at the top of each page in the system needs to check whether the user is logged in. If the user is logged in, the page should render normally. Otherwise, the user needs to be forwarded back to the login page. Obviously the login page doesn't have that code. Otherwise, the user would get into an endless loop just trying to log in.

6.9.2. Running the Hack

Running the hack starts with loading up the database:

 % mysqladmin --user=root --password=password create roles % mysql --user=root --password=password roles < users.sql 

With that done, upload the rest of the code to the web server and point your browser at index.php. This should look like Figure 6-19.

Figure 6-19. Logging in as the manager "megan"


Log in with the username "megan" and the password "seattle,"and you will get the home page shown in Figure 6-20.

Figure 6-20. Megan's home page with the management link


The link to the user management page (management.php) is shown because the "megan" account is linked to the manager role, and this page checks for that role using the has_role() function.

Click on "Manage the users," and you will get something like Figure 6-21.

Figure 6-21. Clicking on the manage pages is allowed


This page makes sure that anyone who looks at it is a manager. In this case, the person who is logged in, Megan, is a manager, so the page is displayed.

Now let's log out; log back in as "jack," as shown in Figure 6-22. The password for this account is "toronto."

Figure 6-22. Logging in as the nonmanager "jack" account


This time the home page does not have the user management link, as seen in Figure 6-23.

Figure 6-23. The home page without the management link


Even a clever user who goes directly to the management.php page by typing in the URL manually will get a page that tells him he doesn't have the correct permissions to look at the page. You can see the error message in Figure 6-24.

Figure 6-24. Refusing direct access to the management page


This is a very rudimentary form of role-based security. If your application is small, or security is divided into just two or three roles, this type of system might suffice.

The next step up from here is to create a model of the objects within the application inside the roles system. Then you can have different permissions for reading, adding, removing, and updating each object.

Following on from that, add the ability to assign groups of permissions to a named role. For example, the administrator role would have the permissions to read, add, remove, and update user accounts. A manager role might only have the ability to update user accounts. The business logic would ask the security system whether the current user has a specific permission, and the system would see whether the user's roles contain that permission. The application does not look for a particular role, since roles are just named collections of permissions.

6.9.3. See Also

  • "Create a Login System" [Hack #57]



PHP Hacks
PHP Hacks: Tips & Tools For Creating Dynamic Websites
ISBN: 0596101392
EAN: 2147483647
Year: 2006
Pages: 163

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net