AUTHENTICATING USERS WITH A DATABASE 471 Strictly speaking, its not necessary to use a bound parameter for $key, because it doesnt come from user input. If you embed it directly in the query, the whole query needs to be wrapped in double quotes, and $key needs to be in single quotes. User authentication with two-way encryption Creating a login page with two-way encryption is very simple. After connecting to the database, you incorporate the username, secret key, and unencrypted password in the WHERE clause of a SELECT query. If the query finds a match, the user is allowed into the restricted part of the site. If theres no match, the login is rejected. The code is the same as in PHP Solution 17-2, except for the following section. For MySQLi, it looks like this (see authenticate_2way_mysqli.inc.php for the full listing—this shows only the sections that are different): $conn = dbConnect('read'); // create key $key = 'takeThisWith@PinchOfSalt'; $sql = 'SELECT username FROM users_2way WHERE username = ? AND pwd = AES_ENCRYPT(?, ?)'; // initialize and prepare statement $stmt = $conn->stmt_init(); $stmt->prepare($sql); // bind the input parameters $stmt->bind_param('sss', $username, $password, $key); $stmt->execute(); // store the result $stmt->store_result(); // if a match is found, num_rows is 1, which is treated as true if ($stmt->num_rows) { $_SESSION['authenticated'] = 'Jethro Tull'; Note that you need to store the result of the prepared statement before you can access the num_rows property. If you fail to do this, num_rows will always be 0, and the login will fail even if the username and password are correct. The revised code for PDO looks like this (see authenticate_2way_pdo.inc.php for the full listing—this shows only the sections that are different): $conn = dbConnect('read', 'pdo'); // create key $key = 'takeThisWith@PinchOfSalt'; $sql = 'SELECT username FROM users_2way WHERE username = ? AND pwd = AES_ENCRYPT(?, ?)'; // prepare statement $stmt = $conn->prepare($sql); // bind variables when executing statement $stmt->execute(array($username, $password, $key)); // if a match is found, rowCount() produces 1, which is treated as true if ($stmt->rowCount()) { $_SESSION['authenticated'] = 'Jethro Tull'; CHAPTER 17 472 Decrypting a password Decrypting a password encrypted with two-way encryption simply involves passing the secret key as the second argument to AES_DECRYPT() in a prepared statement like this: $key = 'takeThisWith@PinchOfSalt'; $sql = "SELECT AES_DECRYPT(pwd, '$key') AS pwd FROM users_2way WHERE username = ?"; The key must be exactly the same as the one originally used to encrypt the password. If you lose the key, the passwords remain as inaccessible as those stored using one-way encryption. Normally, the only time you need to decrypt a password is when a user requests a password reminder. Creating the appropriate security policy for sending out such reminders depends a great deal on the type of site that youre operating. However, it goes without saying that you shouldnt display the decrypted password onscreen. You need to set up a series of security checks, such as asking for the users date of birth or mothers maiden name, or posing a question whose answer only the user is likely to know. Even if the user gets the answer right, you should send the password by email to the users registered address. All the necessary knowledge should be at your fingertips if you have succeeded in getting this far in this book. Updating user details I havent included any update forms for the user registration pages. Its a task that you should be able to accomplish by yourself at this stage. The most important point about updating user registration details is that you should not display the users existing password in the update form. If youre using one-way encryption, you cant anyway. Where next? This book has covered a massive amount of ground. If you master all the techniques covered here, you are well on your way to becoming an intermediate PHP developer, and with a little more effort, you will enter the advanced level. If its been a struggle, dont worry. Go over the earlier chapters again. The more you practice, the easier it becomes. Youre probably thinking, “How on earth can I remember all this?” You dont need to. Dont be ashamed to look things up. Bookmark the PHP online manual (http://docs.php.net/manual/en/), and use it regularly. Its constantly updated, and it has lots of useful examples. Type a function name into the search box at the top right of every page (as shown in the following screenshot), and it takes you straight to a full description of that function. Even if you cant remember the correct function name, it takes you to a page that suggests the most likely candidates. Most pages have practical examples showing how the function or class is used. Download from Wow! eBook <www.wowebook.com> AUTHENTICATING USERS WITH A DATABASE 473 What makes dynamic web design easy is not an encyclopedic knowledge of PHP functions and classes but a solid grasp of how conditional statements, loops, and other structures control the flow of a script. Once you can visualize your projects in terms of “if this happens, what should happen next?” youre the master of your own game. I consult the PHP online manual many times a day. To me, its like a dictionary. Most of the time, I just want to check that I have the arguments in the right order, but I often find that something catches my eye and opens up new horizons. I may not use that knowledge immediately, but I store it at the back of my mind for future use and go back when I need to check the details. The MySQL online manual (http://dev.mysql.com/doc/refman/5.1/en/index.html) is equally useful. Make both the PHP and MySQL online manuals your friends, and your knowledge will grow by leaps and bounds. CHAPTER 17 474 475 Index Symbols $_FILES, 37 error levels, table of, 157 handling multiple files, 171 inspecting the $_FILES array, 144 tmp_name, 148 $_GET, 36, 107, 109 $_POST, 36, 106–113, 116, 120–121, 132 $_REQUEST, 108 $_SERVER, 37, 77, 100 $_SESSION, 37 __construct(), 153 => operator, 55 -> operator, 44 A abs(), 223 action attribute, 105, 110 Add field(s) text box, 418 add(), 406, 409 ADDDATE(), 392 addPermittedTypes(), 162–163, 165, 241 admin.css, 358, 442 Advanced Encryption Standard, 460 AES_DECRYPT(), 472 AES_ENCRYPT(), 460, 469 affected_rows property, 361, 371, 464 After radio button, 418 alias assigning to a column, 384 allow_url_fopen, 203 disabling of, by hosting companies, 180 allow_url_include, 101, 180, 203 anti-spam techniques, CAPTCHA tests, 129 Apache web server, 10, 12 checking the error log, 14 starting Apache and MySQL automatically with XAMPP, 15 arguments passing to a function, 43 separating with commas, 43 arithmetic operators, table of, 49 arrays => operator, 55 array elements, identifying, 36 array(), using to build an associative array, 55 array(), using to build an indexed array, 55 array(), using to create an empty array, 56 array_merge(), 162, 238, 266 array_pop(), 388 array_slice(), 183 associative arrays, 36, 55 count(), 186, 388 empty arrays treated as false, 113 explode(), 183, 186 $_FILES, 37 $_GET, 36 get method, 36 implode(), 183, 388, 446–447 in_array(), 163, 168–169 INDEX 476 indexed arrays, 36, 55 inspect_array1.php, 57 key (index) value, 36 multidimensional arrays, 48, 56, 87 parse_url(), 429 $_POST, 36 post method, 36 print_r(), using to inspect the contents of an array, 57 $_SERVER, 37 $_SESSION, 37 superglobal arrays, 36 types of, 55 underscores, 36 See also data types; superglobal arrays article2cat table, 432, 444, 446, 448 column settings, 437 AS keyword, 384, 391 ASC keyword, 317 assignment (=) operator, 33 authenticate.inc.php, 253, 270 authenticate_2way_mysqli.inc.php, 471 authenticate_2way_pdo.inc.php, 471 authenticate_mysqli.inc.php, 467–468 authenticate_pdo.inc.php, 467–468 AUTO_INCREMENT, 292, 295, 357, 418 B backticks, using in SQL queries, 287 basename(), 77, 79, 212, 228, 274 BETWEEN, 378 BINARY keyword, 319 bind_param(), 328, 360, 422, 464, 467 bind_result(), 327, 369 bindColumn(), 331, 373 binding the parameters, 360, 362 bindParam(), 465 BLOB data type, 301 blog table, 416, 418, 422 admin.css, 358 affected_rows property, 371 bind_result(), 369 bindColumn(), 373 blog.php, 393 blog.sql, 356–366 blog_insert_01.php, 358, 361 blog_insert_mysqli.php, 361, 366, 439 blog_insert_mysqli_03.php, 444 blog_insert_pdo.php, 361, 366 blog_limit_mysqli.php, 394 blog_limit_pdo.php, 394 blog_list.php, 366 blog_list_mysqli.php, 361, 365, 370–371, 392 blog_list_mysqli_01.php, 364 blog_list_norec_mysqli.php, 366 blog_list_norec_pdo.php, 366 blog_list_pdo.php, 363, 365, 375, 392 blog_list_pdo_01.php, 364 blog_mysqli.php, 389 blog_para_mysqli.php, 385 blog_para_pdo.php, 385 blog_pdo.php, 389 blog_ptags_mysqli.php, 387 blog_ptags_pdo.php, 387 blog_update_mysqli.php, 368, 371, 420, 449 blog_update_mysqli_01.php, 358 blog_update_mysqli_03.php, 420 blog_update_mysqli_04.php, 449 blog_update_mysqli_05.php, 450 blog_update_mysqli_06.php, 452 blog_update_pdo.php, 371, 375 blog_update_pdo_01.php, 358 blog_update_pdo_02.php, 374 blog_update_pdo_05.php, 450 blog_update_pdo_06.php, 452 building and scripting the delete page, 375 Cancel button, 376 column definitions, table of, 356 Confirm Deletion button, 376 creating the blog database table, 356 creating the links to the update and delete pages (PHP Solution 13-3), 365 DELETE command, 355 execute(), 362, 371 fetch(), 373 finding the primary key of an update or delete record, 363 htmlentities(), 370, 374 INSERT command, 357 inserting a new record with MySQLi (PHP Solution 13-1), 359 inserting a new record with PDO (PHP Solution 13-2), 361 list of columns, 355 phpsols database, 356 prepare(), 362, 371 rowCount(), 375 SELECT command, 355 UPDATE command, 357, 367, 370 Update Entry button, 370, 375 update page, processes performed, 366 INDEX 477 updating a record with MySQLi (PHP Solution 13-4), 367 updating a record with PDO (PHP Solution 13-5), 371 Boolean values, 39, 48 comparison operators, table of, 58 determining whether a condition is true or false, 57 explicit and implicit Boolean values, 58 logical operators, table of, 59 See also data types break keyword, 60, 64 Browse tab, 295, 419, 423 buildFileList(), 202–203 buildlist.php, 201, 203 byte order mark (BOM), 213 disabling in PHP pages, 247 C camel case, 33 Cancel button, 376 CAPTCHA tests adding a reCAPTCHA widget to a form (PHP Solution 5-7), 131 case keyword, 60 casting operators, 162 table of, 166 catch block, 47, 95–96 categories table, 417, 448 column settings, 437 categories.sql, 437 <channel> tag, 204 check(), 260–263 check-box groups checked attribute, 137 $errors array, 137 handling check boxes and check-box groups (PHP Solution 5-9), 136 interests check-box group, 136 name attribute, 137 setting a minimum number of required check boxes, 137 See also radio-button groups checkdate(), 397 checked attribute, 135–137 checkError(), 158–160, 173–174 checking for PHP support on your website, 9 checking the current running version of PHP, 9 checkName(), 169–170, 173, 438 CheckPassword.php, 259, 262, 265, 461 CheckPassword_01.php, 262 CheckPassword_02.php, 264 checkSize(), 158–159, 173–174 checkType(), 164–165, 173, 220–221, 231 chmod 777, 146 choosing a web server, 12 class_exists(), 93 classes calling a classs constructor method, 153 class keyword, 151 __construct(), 153 controlling the visibility of properties and methods, 153 creating a basic file-upload class (PHP Solution 6-2), 151 creating a new subclass (child class), 235 creating an object, 153 declaring some variables and functions as protected, 152 definition of, 151 extending a class, 235 extends keyword, 235 methods, 153 namespaces, 151, 176 naming conventions, 151 overriding the methods and properties of the parent class, 235 parent keyword, 235 properties, 153 protected keyword, 152–153 Ps2_Upload class, 151 public keyword, 153 $this->, 153 See also functions; object-oriented programming (OOP) client-side validation, 110 close(), 327 closeCursor(), 310, 349 Collation drop-down menu, 288 columns, 281 combined assignment operators, table of, 51 combined concatenation (.=) operator, 51 commenting scripts, 34 comparison operators, table of, 58 composite primary key, 436 CONCAT(), 378 concatenation (.) operator, 45 conditional statements break keyword, 60 case keyword, 60 comparison operators, table of, 58 default keyword, 61 INDEX 478 determining whether a condition is true or false, 57 elseif clause, 41 empty arrays treated as false, 113 equality (==) operator, 41 explicit Boolean values, 58 greater than (>) operator, 41 if statement, 40 implicit Boolean values, 58 less than (<) operator, 41 logical operators, table of, 59 switch statement, 60 ternary operator, 61 testing for statement equality, 41 See also loops; operators Confirm Deletion button, 376 connection.inc.php, 306, 339 constants, defined and predefined, 49 contact.php, 80, 83, 105, 109 contact_02.php, 108, 111 contact_04.php, 115–116 contact_05.php, 118 contact_06.php, 121, 123 contact_07.php, 125 contact_08.php, 127 contact_09.php, 131 contact_10.php, 132 contact_11.php, 134, 139 contact_us.php, 81 content management system admin.css, 358 bindColumn(), 373 blog table, creating and setting up, 355 building and scripting the delete page, 375 Cancel button, 376 Confirm Deletion button, 376 creating the links to the update and delete pages (PHP Solution 13-3), 365 CRUD cycle, 355 DELETE command, 355 fetch(), 373 finding the primary key of an update or delete record, 363 htmlentities(), 374 INSERT command, 357 phpsols database, 356 rowCount(), 375 SELECT command, 355 setting up a content management system, 355 UPDATE command, 357, 367, 370 Update Entry button, 370, 375 Content-Type header, 122 continue keyword, 64 Control Panel (XAMPP) confirming that Apache and MySQL are running, 13 Explore button, 14 Port-Check button, 14 starting Apache and MySQL automatically with XAMPP, 15 convertDateToMySQL(), 397–398 convertToParas(), 425 Coordinated Universal Time (UTC), 209 copy(), potential security risks of, 150 count(), 86, 186, 310, 348, 388 returning the number of elements in an array, 137 create(), 229, 233–234 create_thumb.php, 221, 225 create_thumb_mac01.php, 217 create_thumb_upload.php, 239 create_thumb_win01.php, 217 createFromDateString(), 410, 413 createFromFormat(), 403–404 createImageResource(), 232, 234 createThumbnail(), 231–234, 238–239 cross-reference (linking) table, 417 CRUD cycle, 355 curly braces, arranging, 42 current(), 154 D data source name (DSN), 314 data types arrays, 48 Boolean, 48 converting strings to integers or floating- point numbers, 48 floating-point numbers, 48 hexadecimal numbers, 48 integers, 48 list of, 48 multidimensional arrays, 48 NULL, 48 objects, 48 resources, 48 side-effects of PHPs weak typing, 48 strings, 48 Database drop-down menu, 291 databases INDEX 479 adding an extra column to a table (PHP Solution 15-1), 418 adding categories to the update form (PHP Solution 16-5), 449 adding the category and image input fields (PHP Solution 16-3), 439 adding the image foreign key (PHP Solution 15-2), 420 altering the structure of an existing table, 417 breaking down information into small chunks, 283 building the details page (PHP Solution 15-3), 423 categories.sql, 437 checking whether a remote server supports InnoDB (PHP Solution 16-1), 432 checkName(), 438 columns, 281 composite primary key, 436 converting an InnoDB table back to MyISAM, 455 converting tables from MyISAM to InnoDB, 452 creating delete scripts with foreign key constraints, 456 creating delete scripts without foreign key constraints, 457 cross-reference (linking) table, 417, 436 DATE_FORMAT(), 424–425 Delete icon, 455 details.php, 423 details_mysqli_03.php, 429 drawing dynamic website content from a database, 279 establishing relationships between tables through foreign keys, 415 fields, 281 finding records that dont have a matching foreign key, 427 FOREIGN KEY (INNODB), 453 foreign key constraints, 432 guidelines for good database design, 284 handling orphaned records, 432 how database information is stored, 281 improving the Ps2_Upload class (PHP Solution 16-2), 437 INNER JOIN, 422 InnoDB storage engine, 432 inserting a foreign key in a table, 419 inserting data into multiple tables (PHP Solution 16-4), 444 joint primary key, 417 LEFT JOIN, 427 linking tables in a SELECT query, 422 linking tables with primary and foreign keys, 282 maintaining referential integrity, 417, 431 many-to-many relationship, 417 Microsoft SQL Server, 279 MyISAM storage engine, 432 MySQL, 279 ON DELETE drop-down menu, 453 one-to-many relationship, 416 one-to-one relationship, 415 Oracle, 279 parent and child (dependent) tables, 417 PostgreSQL, 279 preserving referential integrity on record deletion, 452 primary keys, 281 problems in storing images in a database, 336 processFile(), 438 records, 281 Relation view, 455 returning to the same point in a navigation system (PHP Solution 15-4), 428 setting up foreign key constraints, 453 storage_engines.php, 434 Structure tab, 455 Structured Query Language (SQL), 282 tables, 281 transaction, definition of, 435 understanding table relationships, 415 updating records in a cross-reference table, 449 Upload.php, 437 Upload_06.php, 438 utility_funcs.inc.php, 424 See also foreign keys; InnoDB storage engine; MyISAM storage engine; MySQL; PHP Data Objects (PDO); phpMyAdmin; phpsols database; primary keys; Structured Query Language (SQL); tables Database-specific privileges table, 290 dates add(), 406, 409 ADDDATE(), 392 INDEX 480 adding and subtracting set periods with the DateInterval class, 408 adding or subtracting a specific time period, 392 Apache servers and, 400 blog.php, 393 blog_limit_mysqli.php, 394 blog_limit_pdo.php, 394 blog_list_mysqli.php, 392 blog_list_pdo.php, 392 calculating recurring dates with the DatePeriod class, 412 calculating the difference between two dates using diff(), 410 checkdate(), 397 choosing between date() and the DateTime class, 404 common MySQL date-format specifiers, table of, 390 convertDateToMySQL(), 397–398 createFromDateString(), 410, 413 createFromFormat(), 403–404 creating a DateTime object from a custom format (PHP 5.3 and later), 403 creating an HTML5 date input field, 394 date and time changes in PHP 5.2 and 5.3, 399 date(), 43, 83–85, 401, 403 DATE data type, 300 DATE_ADD(), 392 DATE_ADD() and DATE_SUB(), table of common interval types, 393 date_converter.php, 395 date_converter_01.php, 395 date_converter_02.php, 398 DATE_FORMAT(), 390, 392, 424–425 date_format_01.php, 403 date_format_03.php, 403 date_format_04.php, 404 date_interval_01.php, 410 date_interval_02.php, 410 date_interval_03.php, 411 date_interval_04.php, 412 date_interval_05.php, 413 date_interval_06.php, 413 date_modify.php, 406 DATE_SUB(), 392 DateInterval class, table of ISO 8601 period designators, 409 DateInterval format(), table of format characters, 410 DateTime class, table of methods, 404 DATETIME data type, 300 DateTime object, 400 DateTime(), 400 DateTimeZone class, table of methods, 408 DateTimeZone object, 401, 403, 407 diff(), 406 displaying items updated within the past week (PHP Solution 14-3), 393 ensuring that dates are valid and correctly formatted, 394 EXCLUDE_START_DATE, 413 explode(), 394 format(), 401, 403–404 formatting a date in European style, 391 formatting a date in U.S. style, 391 formatting a MySQL date or timestamp (PHP Solution 14-2), 392 getName(), 407 getTimezone(), 407 how MySQL handles dates, 390 International Organization for Standardization (ISO), 390 main date and time format characters, table of, 402 modify(), 406 NOW(), 393–394 PHP date- and time-related classes and functions, table of, 399 php.ini, date.timezone directive, 400 setDate(), 406 setTime(), 406 setTimezone(), 407 setting the servers default time zone, 400 storing timestamps as 32-bit and 64-bit integers, 399 strftime(), 401 strtotime(), 403, 410 sub(), 406, 409 SUBDATE(), 392 TIMESTAMP column, 394 timezones.php, 407 understanding time zones and their geographic regions, 407 using date picker widgets, 394 utility_funcs.inc.php, 396–398 validating and formatting dates for MySQL input (PHP Solution 14-4), 395 working with dates in PHP, 399 dbConnect(), 388, 463 DECIMAL data type, 300 . blog_update_mysqli_05 .php, 450 blog_update_mysqli_06 .php, 452 blog_update_pdo .php, 371, 375 blog_update_pdo_01 .php, 358 blog_update_pdo_02 .php, 374 blog_update_pdo_05 .php, 450 blog_update_pdo_06 .php, . blog_list_norec_mysqli .php, 366 blog_list_norec_pdo .php, 366 blog_list_pdo .php, 363, 365, 375, 392 blog_list_pdo_01 .php, 364 blog_mysqli .php, 389 blog_para_mysqli .php, 385 blog_para_pdo .php, 385 blog_pdo .php, . blog_ptags_mysqli .php, 387 blog_ptags_pdo .php, 387 blog_update_mysqli .php, 368, 371, 420, 449 blog_update_mysqli_01 .php, 358 blog_update_mysqli_03 .php, 420 blog_update_mysqli_04 .php, 449