Index
    Preface
      What This Book Is About
      What You Need to Know
      How This Book Is Organized
      How to Use This Book
      Conventions Used in This Book
      Using Code Examples
      How to Contact Us
      Web Site and Code Examples
      Acknowledgments
      Chapter 1.  Database Applications and the Web
      Section 1.1.  The Web
      Section 1.2.  Three-Tier Architectures
      Chapter 2.  The PHP Scripting Language
      Section 2.1.  Introducing PHP
      Section 2.2.  Conditions and Branches
      Section 2.3.  Loops
      Section 2.4.  Functions
      Section 2.5.  Working with Types
      Section 2.6.  User-Defined Functions
      Section 2.7.  A Working Example
      Chapter 3.  Arrays, Strings, and Advanced Data Manipulation in PHP
      Section 3.1.  Arrays
      Section 3.2.  Strings
      Section 3.3.  Regular Expressions
      Section 3.4.  Dates and Times
      Section 3.5.  Integers and Floats
      Chapter 4.  Introduction to Object-Oriented Programming with PHP 5
      Section 4.1.  Classes and Objects
      Section 4.2.  Inheritance
      Section 4.3.  Throwing and Catching Exceptions
      Chapter 5.  SQL and MySQL
      Section 5.1.  Database Basics
      Section 5.2.  MySQL Command Interpreter
      Section 5.3.  Managing Databases and Tables
      Section 5.4.  Inserting, Updating, and Deleting Data
      Section 5.5.  Querying with SQL SELECT
      Section 5.6.  Join Queries
      Section 5.7.  Case Study: Adding a New Wine
      Chapter 6.  Querying Web Databases
      Section 6.1.  Querying a MySQL Database Using PHP
      Section 6.2.  Processing User Input
      Section 6.3.  MySQL Function Reference
      Chapter 7.  PEAR
      Section 7.1.  Overview
      Section 7.2.  Core Components
      Section 7.3.  Packages
      Chapter 8.  Writing to Web Databases
      Section 8.1.  Database Inserts, Updates, and Deletes
      Section 8.2.  Issues in Writing Data to Databases
      Chapter 9.  Validation with PHP and JavaScript
      Section 9.1.  Validation and Error Reporting Principles
      Section 9.2.  Server-Side Validation with PHP
      Section 9.3.  JavaScript and Client-Side Validation
      Chapter 10.  Sessions
      Section 10.1.  Introducing Session Management
      Section 10.2.  PHP Session Management
      Section 10.3.  Case Study: Using Sessions in Validation
      Section 10.4.  When to Use Sessions
      Section 10.5.  PHP Session API and Configuration
      Chapter 11.  Authentication and Security
      Section 11.1.  HTTP Authentication
      Section 11.2.  HTTP Authentication with PHP
      Section 11.3.  Form-Based Authentication
      Section 11.4.  Protecting Data on the Web
      Chapter 12.  Errors, Debugging, and Deployment
      Section 12.1.  Errors
      Section 12.2.  Common Programming Errors
      Section 12.3.  Custom Error Handlers
      Chapter 13.  Reporting
      Section 13.1.  Creating a Report
      Section 13.2.  Producing PDF
      Section 13.3.  PDF-PHP Reference
      Chapter 14.  Advanced Features of Object-Oriented Programming in PHP 5
      Section 14.1.  Working with Class Hierarchies
      Section 14.2.  Class Type Hints
      Section 14.3.  Abstract Classes and Interfaces
      Section 14.4.  Freight Calculator Example
      Chapter 15.  Advanced SQL
      Section 15.1.  Exploring with SHOW
      Section 15.2.  Advanced Querying
      Section 15.3.  Manipulating Data and Databases
      Section 15.4.  Functions
      Section 15.5.  Automating Querying
      Section 15.6.  Table Types
      Section 15.7.  Backup and Recovery
      Section 15.8.  Managing Users and Privileges
      Section 15.9.  Tuning MySQL
      Chapter 16.  Hugh and Dave's Online Wines:A Case Study
      Section 16.1.  Functional and System Requirements
      Section 16.2.  Application Overview
      Section 16.3.  Common Components
      Chapter 17.  Managing Customers
      Section 17.1.  Code Overview
      Section 17.2.  Customer Validation
      Section 17.3.  The Customer Form
      Chapter 18.  The Shopping Cart
      Section 18.1.  Code Overview
      Section 18.2.  The Winestore Home Page
      Section 18.3.  The Shopping Cart Implementation
      Chapter 19.  Ordering and Shipping at the Online Winestore
      Section 19.1.  Code Overview
      Section 19.2.  Credit Card and Shipping Instructions
      Section 19.3.  Finalizing Orders
      Section 19.4.  HTML and Email Receipts
      Chapter 20.  Searching and Authentication in the Online Winestore
      Section 20.1.  Code Overview
      Section 20.2.  Searching and Browsing
      Section 20.3.  Authentication
      Appendix A.  Linux Installation Guide
      Section A.1.  Finding Out What's Installed
      Section A.2.  Installation Overview
      Section A.3.  Installing MySQL
      Section A.4.  Installing Apache
      Section A.5.  Installing PHP
      Section A.6.  What's Needed for This Book
      Appendix B.  Microsoft Windows Installation Guide
      Section B.1.  Installation Overview
      Section B.2.  Installing with EasyPHP
      Section B.3.  What's Needed for This Book
      Appendix C.  Mac OS X Installation Guide
      Section C.1.  Getting Started
      Section C.2.  Installing MySQL
      Section C.3.  Setting Up Apache and PHP
      Section C.4.  What's Needed for This Book
      Appendix D.  Web Protocols
      Section D.1.  Network Basics
      Section D.2.  Hypertext Transfer Protocol
      Appendix E.  Modeling and Designing Relational Databases
      Section E.1.  The Relational Model
      Section E.2.  Entity-Relationship Modeling
      Appendix F.  Managing Sessions in theDatabase Tier
      Section F.1.  Using a Database to Keep State
      Section F.2.  PHP Session Management
      Section F.3.  MySQL Session Store
      Appendix G.  Resources
      Section G.1.  Client Tier Resources
      Section G.2.  Middle-Tier Resources
      Section G.3.  Database Tier Resources
      Section G.4.  Security and Cryptography Resources
      Appendix H.  The Improved MySQL Library
      Section H.1.  New Features
      Section H.2.  Getting Started
      Section H.3.  Using the New Features
    Colophon
    Copyright



 

Previous Section  < Day Day Up >  Next Section

20.3 Authentication

The authentication module consists of the authentication.inc include file, the login and logout scripts, and the password change scripts. The code is closely based on that presented in Chapter 11 and we describe it only briefly here.

20.3.1 General-Purpose Functions

Example 20-4 shows the helper functions stored in the authentication.inc include file. The function newUser( ) creates a new row in the users table, and digests the password that's passed as a parameter using the md5( ) hash function. This is discussed in detail in Chapter 11.

The function authenticateUser( ) checks whether a row in the users table matches the supplied username and password (the supplied password is digested prior to comparison with those stored in the database). It returns true when there's a match and false otherwise.

The registerLogin( ) function saves the user's username as a session variable, and also stores the IP address from which they've accessed the winestore. The presence of the $_SESSION["loginUsername"] variable indicates the user has logged in successfully. The function unregisterLogin( ) deletes the same two session variables.

The function sessionAuthenticate( ) checks whether a user is logged in (by testing for the presence of $_SESSION["loginUsername"]) and that they're returning from the same IP address. If either test fails, the script calls unregisterLogin( ) and redirects to the script supplied as a parameter. This approach won't work for all situations—for example, if a user's ISP accesses the winestore through different web proxy servers, their IP address may change. It's up to you to decide whether this additional security step is needed in your applications. This is discussed in more detail in Chapter 11.

Example 20-4. The authentication.inc include file
<?php

// Add a new user to the users table

function newUser($loginUsername, $loginPassword, $cust_id, $connection)

{

   // Create the encrypted password

   $stored_password = md5(trim($loginPassword));



   // Insert a new user into the users table

   $query = "INSERT INTO users SET

             cust_id = {$cust_id},

             password = '{$stored_password}',

             user_name = '{$loginUsername}'";



   $result = $connection->query($query);



   if (DB::isError($result))

      trigger_error($result->getMessage( ), E_USER_ERROR);

}



// Check if a user has an account that matches the username and password

function authenticateUser($loginUsername, $loginPassword, $connection)

{

   // Create a digest of the password collected from the challenge

   $password_digest = md5(trim($loginPassword));



   // Formulate the SQL to find the user

   $query = "SELECT password FROM users

             WHERE user_name = '$loginUsername'

             AND password = '$password_digest'";



   $result = $connection->query($query);



   if (DB::isError($result))

      trigger_error($result->getMessage( ), E_USER_ERROR);



   // exactly one row? then we have found the user

   if ($result->numRows( ) != 1)

      return false;

   else

      return true;

}



// Register that user has logged in

function registerLogin($loginUsername)

{

   // Register the loginUsername to show the user is logged in

   $_SESSION["loginUsername"] = $loginUsername;



   // Register the IP address that started this session

   $_SESSION["loginIP"] = $_SERVER["REMOTE_ADDR"];

}



// Logout (unregister the login)

function unregisterLogin( )

{

   // Ensure login is not registered

   if (isset($_SESSION["loginUsername"]))

      unset($_SESSION["loginUsername"]);



   if (isset($_SESSION["loginIP"]))

      unset($_SESSION["loginIP"]);

}



// Connects to a session and checks that the user has

// authenticated and that the remote IP address matches

// the address used to create the session.

function sessionAuthenticate($destinationScript)

{

  // Check if the user hasn't logged in

  if (!isset($_SESSION["loginUsername"]))

  {

    // The request does not identify a session

    $_SESSION["message"] = "You are not authorized to access the URL

                            {$_SERVER["REQUEST_URI"]}";



    unregisterLogin( );

    header("Location: {$destinationScript}");

    exit;

  }



  // Check if the request is from a different IP address to previously

  if (isset($_SESSION["loginIP"]) &&

     ($_SESSION["loginIP"] != $_SERVER["REMOTE_ADDR"]))

  {

    // The request did not originate from the machine

    // that was used to create the session.

    // THIS IS POSSIBLY A SESSION HIJACK ATTEMPT



    $_SESSION["message"] = "You are not authorized to access the URL

                            {$_SERVER["REQUEST_URI"]} from the address

                            {$_SERVER["REMOTE_ADDR"]}";



    unregisterLogin( );

    header("Location: {$destinationScript}");

    exit;

  }

}

?>

20.3.2 Logging In and Out

The auth/login.php and auth/logincheck.php scripts are shown in Example 20-5 and Example 20-6 respectively. The auth/login.php script is a straightforward use of the winestoreFormTemplate class described in Chapter 16, and it simply collects the user's login name (their email address) and their password. The auth/logincheck.php script validates the username and password using functions from validate.inc discussed in Chapter 16, and then uses the helper functions discussed in the previous section to check the user's credentials and complete the login process.

The auth/logout.php script that logs the user out is shown in Example 20-7.

Example 20-5. The auth/login.php script that collects the user's credentials
<?php

// Show the login page



require_once "../includes/template.inc";

require_once "../includes/winestore.inc";

require_once "../includes/validate.inc";



set_error_handler("customHandler");



session_start( );



// Takes <form> heading, instructions, action, formVars name, and

// formErrors name as parameters

$template = new winestoreFormTemplate("Login",

                "Please enter your username and password.",

                S_LOGINCHECK, "loginFormVars", "loginErrors");





$template->mandatoryWidget("loginUsername", "Username/Email:", 50);

$template->passwordWidget("loginPassword", "Password:", 8);



// Add buttons and messages, and show the page

$template->showWinestore(NO_CART, B_HOME);

?>

Example 20-6. The auth/logincheck.php that validates and checks the user's credentials
<?php

// This script manages the login process.

// It should only be called when the user is not logged in.

// If the user is logged in, it will redirect back to the calling page.

// If the user is not logged in, it will show a login <form>



require_once "DB.php";

require_once "../includes/winestore.inc";

require_once "../includes/authenticate.inc";

require_once "../includes/validate.inc";



set_error_handler("customHandler");



function checkLogin($loginUsername, $loginPassword, $connection)

{



  if (authenticateUser($loginUsername, $loginPassword, $connection))

  {

     registerLogin($loginUsername);



     // Clear the formVars so a future <form> is blank

     unset($_SESSION["loginFormVars"]);

     unset($_SESSION["loginErrors"]);



     header("Location: " . S_MAIN);

     exit;

  }

  else

  {

     // Register an error message

     $_SESSION["message"] = "Username or password incorrect. " . 

                            "Login failed.";



     header("Location: " . S_LOGIN);

     exit;

  }

}



// ------



session_start( );



$connection = DB::connect($dsn, true);



if (DB::isError($connection))

  trigger_error($connection->getMessage( ), E_USER_ERROR);



// Check if the user is already logged in

if (isset($_SESSION["loginUsername"]))

{

     $_SESSION["message"] = "You are already logged in!";

     header("Location: " . S_HOME);

     exit;

}



// Register and clear an error array - just in case!

if (isset($_SESSION["loginErrors"]))

   unset($_SESSION["loginErrors"]);

$_SESSION["loginErrors"] = array( );



// Set up a formVars array for the POST variables

$_SESSION["loginFormVars"] = array( );



foreach($_POST as $varname => $value)

   $_SESSION["loginFormVars"]["{$varname}"] =

   pearclean($_POST, $varname, 50, $connection);



// Validate password -- has it been provided and is the length between 

// 6 and 8 characters?

if (checkMandatory("loginPassword", "password",

              "loginErrors", "loginFormVars"))

  checkMinAndMaxLength("loginPassword", 6, 8, "password",

                  "loginErrors", "loginFormVars");



// Validate email -- has it been provided and is it valid?

if (checkMandatory("loginUsername", "email/username",

              "loginErrors", "loginFormVars"))

  emailCheck("loginUsername", "email/username",

             "loginErrors", "loginFormVars");



// Check if this is a valid user and, if so, log them in

checkLogin($_SESSION["loginFormVars"]["loginUsername"],

           $_SESSION["loginFormVars"]["loginPassword"],

           $connection);

?>

Example 20-7. The auth/logout.php script that logs the user out
<?php

// This script logs a user out and redirects

// to the calling page.



require_once '../includes/winestore.inc';

require_once '../includes/authenticate.inc';



set_error_handler("customHandler");



// Restore the session

session_start( );



// Check they're logged in

sessionAuthenticate(S_LOGIN);



// Destroy the login and all associated data

session_destroy( );



// Redirect to the main page

header("Location: " . S_MAIN);

exit;

?>

20.3.3 Changing Passwords

The password change feature is implemented in the auth/password.php script shown in Example 20-8 and the auth/changepassword.php script in Example 20-9. The password change form that's output by auth/password.php is based on the winestoreFormTemplate class described in Chapter 16, and requires the user to enter their current password and two copies of their new password.

Example 20-8. The auth/password.php script that collects the user's old and new passwords
<?php

// This script shows the user a <form> to change their password

// The user must be logged in to view it.



require_once "../includes/template.inc";

require_once "../includes/winestore.inc";

require_once "../includes/authenticate.inc";



set_error_handler("customHandler");



session_start( );



// Check the user is properly logged in

sessionAuthenticate(S_MAIN);



// Takes <form> heading, instructions, action, formVars name, 

// and formErrors name as parameters

$template = new winestoreFormTemplate("Change Password",

                "Please enter your existing and new passwords.",

                S_CHANGEPASSWORD, "pwdFormVars", "pwdErrors");



// Create the password change widgets

$template->passwordWidget("currentPassword", "Current Password:", 8);

$template->passwordWidget("newPassword1", "New Password:", 8);

$template->passwordWidget("newPassword2", "Re-enter New Password:", 8);



// Add buttons and messages, and show the page

$template->showWinestore(NO_CART, B_HOME);

?>

The auth/changepassword.php script checks that all three passwords are supplied and that each is between 6 and 8 characters in length. It then checks that the two copies of the new password are identical, that the new password is different to the old one, and that the old password matches the one stored in the users table. If all the checks pass, the new password is digested with the md5( ) hash function, and the users table updated with the new value. The script then redirects to the winestore home page, where a success message is displayed. If any check fails, the script redirects to auth/password.php .

Example 20-9. The auth/changepassword.php script that validates a password change and updates the database
<?php

require_once "DB.php";

require_once "../includes/winestore.inc";

require_once "../includes/authenticate.inc";

require_once "../includes/validate.inc";



set_error_handler("customHandler");



session_start( );



// Connect to a authenticated session

sessionAuthenticate(S_MAIN);



$connection = DB::connect($dsn, true);



if (DB::isError($connection))

   trigger_error($connection->getMessage( ), E_USER_ERROR);



// Register and clear an error array - just in case!

if (isset($_SESSION["pwdErrors"]))

   unset($_SESSION["pwdErrors"]);

$_SESSION["pwdErrors"] = array( );



// Set up a formVars array for the POST variables

$_SESSION["pwdFormVars"] = array( );



foreach($_POST as $varname => $value)

   $_SESSION["pwdFormVars"]["{$varname}"] =

     pearclean($_POST, $varname, 50, $connection);



// Validate passwords - between 6 and 8 characters

if (checkMandatory("currentPassword", "current password",

              "pwdErrors", "pwdFormVars"))

  checkMinAndMaxLength("loginPassword", 6, 8, "current password",

                  "pwdErrors", "pwdFormVars");



if (checkMandatory("newPassword1", "first new password",

              "pwdErrors", "pwdFormVars"))

  checkMinAndMaxLength("newPassword1", 6, 8, "first new password",

                  "pwdErrors", "pwdFormVars");



if (checkMandatory("newPassword2", "second new password",

              "pwdErrors", "pwdFormVars"))

  checkMinAndMaxLength("newPassword2", 6, 8, "second new password",

                  "pwdErrors", "pwdFormVars");



// Did we find no errors? Ok, check the new passwords are the

// same, and that the current password is different.

// Then, check the current password.

if (count($_SESSION["pwdErrors"]) == 0)

{

   if ($_SESSION["pwdFormVars"]["newPassword1"] !=

       $_SESSION["pwdFormVars"]["newPassword2"])

     $_SESSION["pwdErrors"]["newPassword1"] =

       "The new passwords must match.";



   elseif ($_SESSION["pwdFormVars"]["newPassword1"] ==

           $_SESSION["pwdFormVars"]["currentPassword"])

     $_SESSION["pwdErrors"]["newPassword1"] =

       "The password must change.";



   elseif (!authenticateUser($_SESSION["loginUsername"],

                             $_SESSION["pwdFormVars"]["currentPassword"],

                             $connection))

     $_SESSION["pwdErrors"]["currentPassword"] =

       "The current password is incorrect.";

}



// Now the script has finished the validation,

// check if there were any errors

if (count($_SESSION["pwdErrors"]) > 0)

{

    // There are errors.  Relocate back to the password form

    header("Location: " . S_PASSWORD);

    exit;

}



// Create the encrypted password

$stored_password = md5(trim($_SESSION["pwdFormVars"]["newPassword1"]));



// Update the user row

$query = "UPDATE users SET password = '$stored_password'

          WHERE user_name = '{$_SESSION["loginUsername"]}'";



$result = $connection->query($query);

if (DB::isError($result))

   trigger_error($result->getMessage( ), E_USER_ERROR);



// Clear the formVars so a future <form> is blank

unset($_SESSION["pwdFormVars"]);

unset($_SESSION["pwdErrors"]);



// Set a message that says

 that the page has changed

$_SESSION["message"] = "Your password has been successfully changed.";



// Relocate to the customer details page

header("Location: " . S_DETAILS);

?>

    Previous Section  < Day Day Up >  Next Section







    Copyright © 2010 | Domen maybe sale - bye this domen