2

I'm starting to wonder if the Oreily publishing house has severely lowered its standards or something. I'm not even going to go into the first two PHP books I got from them, but this third one seems pretty messed up too.

In any case, here is the issue. In the project from the book I'm working on, the book says to create a user database and then create a program based on that database that allows a user to log in. Here is the code that supposedly accomplishes this:

Create user database:

    <?php //setupusers.php
    require_once 'login.php';
    $db_server = mysql_connect($db_hostname, $db_username, $db_password);
    if (!$db_server) die("Unable to connect to MySQL: " . mysql_error());
    mysql_select_db($db_database)
        or die("Unable to select database: " . mysql_error());

    $query = "CREATE TABLE users (
                forename VARCHAR(32) NOT NULL,
                surname  VARCHAR(32) NOT NULL,
                username VARCHAR(32) NOT NULL UNIQUE,
                password VARCHAR(32) NOT NULL
            )";

    $result = mysql_query($query);
    if (!$result) die ("Database access failed: " . mysql_error());

    $salt1 = "qm&h*";
    $salt2 = "pg!@";

    $forename = 'Bill';
    $surname  = 'Smith';
    $username = 'bsmith';
    $password = 'mysecret';
    $token    = md5("$salt1$password$salt2");
    add_user($forename, $surname, $username, $token);

    $forename = 'Pauline';
    $surname  = 'Jones';
    $username = 'pjones';
    $password = 'acrobat';
    $token    = md5("$salt1$password$salt2");
    add_user($forename, $surname, $username, $token);

    function add_user($fn, $sn, $un, $pw)
    {
        $query = "INSERT INTO users VALUES('$fn', '$sn', '$un', '$pw')";
        $result = mysql_query($query);
        if (!$result) die ("Database access failed: " . mysql_error());
    }
    ?>

allow users to log in

<?php // authenticate.php
require_once 'login.php';
$db_server = mysql_connect($db_hostname, $db_username, $db_password);
if (!$db_server) die("Unable to connect to MySQL: " . mysql_error());
mysql_select_db($db_database)
    or die("Unable to select database: " . mysql_error());

if (isset($_SERVER['PHP_AUTH_USER']) &&
    isset($_SERVER['PHP_AUTH_PW']))
{
    $un_temp = mysql_entities_fix_string($_SERVER['PHP_AUTH_USER']);
    $pw_temp = mysql_entities_fix_string($_SERVER['PHP_AUTH_PW']);

    $query = "SELECT * FROM users WHERE username='$un_temp'";
    $result = mysql_query($query);
    if (!$result) die("Database access failed: " . mysql_error());
    elseif (mysql_num_rows($result))
    {
        $row = mysql_fetch_row($result);
        $salt1 = "qm&h*";
        $salt2 = "pg!@";
        $token = md5("$salt1$pw_temp$salt2");

        if ($token == $row[3]) echo "$row[0] $row[1] : 
            Hi $row[0], you are now logged in as '$row[2]'";
        else die("Invalid username/password combination");
    }
    else die("Invalid username/password combination");
}
else
{
    header('WWW-Authenticate: Basic realm="Restricted Section"');
    header('HTTP/1.0 401 Unauthorized');
    die ("Please enter your username and password");
}

function mysql_entities_fix_string($string)
{
    return htmlentities(mysql_fix_string($string));
}   

function mysql_fix_string($string)
{
    if (get_magic_quotes_gpc()) $string = stripslashes($string);
    return mysql_real_escape_string($string);
}
?>

The trouble is, I never get an error message and am never allowed to log in. I can enter a username and password into a popup window that the program generates, but nothing ever happens as a result of entering anything, correct or incorrect. When I hit "ok" the username and password fields are just cleared.

I suspect it has something to do with this line:

$query = "SELECT * FROM users WHERE username='$un_temp'";

But I don't know.

Just for reference, here is an sql file containing the code from users.

-- MySQL dump 10.13  Distrib 5.1.50, for Win32 (ia32)
--
-- Host: localhost    Database: publications
-- ------------------------------------------------------
-- Server version   5.1.50-community

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `users`
--

DROP TABLE IF EXISTS `users`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `users` (
  `forename` varchar(32) NOT NULL,
  `surname` varchar(32) NOT NULL,
  `username` varchar(32) NOT NULL,
  `password` varchar(32) NOT NULL,
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `users`
--

LOCK TABLES `users` WRITE;
/*!40000 ALTER TABLE `users` DISABLE KEYS */;
INSERT INTO `users` VALUES ('Bill','Smith','bsmith','be9d31ad4315b2ad9900a8526cd3edb1'),('Pauline','Jones','pjones','b1334d37914cf7561a006f656e27600c');
/*!40000 ALTER TABLE `users` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2012-12-31 11:38:10

YES, I KNOW SOME OF THE CODE HERE MAY BE "OLD".

Please hear me out. If you would like to suggest a book or tutorial for PHP that doesn't use "old" code, please do. I'm at my wits end trying to learn this programming language.

However if you just don't know how to trouble shoot something is "old" code in it, don't waste my time by telling me it's "old" as an "answer". There is no function in any language that forces a program not to work because a certain line of code is "old" or "underused". That only matters if the line of code is in fact no longer recognized. I'll have to learn better eventually I suppose, but right now I'm trying to learn php IN THE FIRST PLACE!

Trojan
  • 2,256
  • 28
  • 40
David
  • 95
  • 8
  • Could you please post the contents of your mysql-database to verify, that the user table has been correctly filled? – Lars Dec 31 '12 at 19:10
  • 2
    I'm not answering, but to learn how to use [PDO](http://php.net/manual/en/intro.pdo.php), the more-contemporary alternative to the `mysql_*` functions, have a look at this question: http://stackoverflow.com/questions/1943069/are-there-good-tutorials-on-how-to-use-pdo – David Thomas Dec 31 '12 at 19:11
  • Where is login.php? Is that file relevant? – Quentin Skousen Dec 31 '12 at 19:11
  • 1
    " I never get an error message " may be related to your PHP installation. If you have a standard installation, chances are that you have a `php.ini` file with production settings. In that case, "`display_errors`" is `Off` in your `php.ini`, which is why you don't get any feedback. Try setting that to `On` and/or put `error_reporting(E_ALL);` on the next line after your first PHP opening tag (` – Rem.co Dec 31 '12 at 19:15
  • Sounds like the problem is somewhere else, like the javascript: `I can enter a username and password into a popup window that the program generates, but nothing ever happens as a result of entering anything, correct or incorrect. When I hit "ok" the username and password fields are just cleared.` – jeroen Dec 31 '12 at 19:17
  • Oh my god that is old code LOL. – David Harris Dec 31 '12 at 19:26
  • The login file is about five lines of code. It just logs in. – David Dec 31 '12 at 19:26
  • What I mean when I say "no error message", is that I'm never told my username/password is incorrect, which the program is supposed to do if I type in a bad combination. Again, the fields just clear, nothing else happens. As to, if I seriously funked up the code would I get an error message from my php, yes, yes I would. – David Dec 31 '12 at 19:29
  • Why is $token set to check for being equal to row[3]? The user only enters two values, shouldn't the salted password then be row[2]? – David Dec 31 '12 at 20:04
  • Oh, holy cow, the salt algorithm is completely broken in this example. The salt needs to be much longer, and it needs to be different, and random, for each user row created, and it needs to be stored with the hash token. http://crackstation.net/hashing-security.htm ... – O. Jones Jan 01 '13 at 00:53
  • Here are the publisher's errata for the book in question. http://oreilly.com/catalog/errata.csp?isbn=0636920023487 I have found the folks at O'Reilly Media to be very responsive. – O. Jones Jan 01 '13 at 00:59
  • It's row[3] because there are four columns of data in each row of the `users` table, numbered 0..3. The hashed token (called `password` in the database) is in the last column. And this example uses, gulp, `SELECT *` rather than naming the required columns in the query. – O. Jones Jan 01 '13 at 01:17

3 Answers3

1

The book in question (Learning PHP, MySQL, JavaScript, and CSS) makes it clear that Basic Authentication is not enabled on all web servers. There's a good reason for that; it's impossibly insecure unless your web site uses https (a trivial packet sniffer can recover the plain text user name and password from any web request in the session).

If it's not available on your web server, things will just silently fail. That's one of the challenging (=== pain in the neck) aspects of doing web development.

Web security is hard enough to get right that you may want to consider building your system on top of a competent content management system (CMS) instead of creating your own authentication / authorization package. Here's a helpful article by one of the WordPress developers that should clue you into some of the subtleties of the subject.

http://markjaquith.wordpress.com/2006/06/02/wordpress-203-nonces/

I guess it's a good thing for your future users the example didn't work for you. Even if the code works, it has an insecure salt algorithm as well as the Basic Auth problem.

O. Jones
  • 103,626
  • 17
  • 118
  • 172
  • 1
    I'm curious as to why you think I would have the most secure salt algorithm, rather than just a sample, on a piece of sample code that will never be published online that I feel no shame blasting in front of complete strangers. – David Jan 01 '13 at 16:27
  • Well, the accepted route to secure algorithms is to show the code in its entirety. The only secret part of an algorithm should be the private keys. In other words, security through obscurity doesn't work. In this case the salt is set to a particular value. It should be randomized so crackers can't predict it. – O. Jones Mar 02 '13 at 13:43
0

Instead of just blindly copying/pasting the example code and expecting it to work try the following:

  1. Write a few lines.
  2. Test it.
  3. Fix anything that's gone wrong.
  4. Test it.
  5. Lather, rinse, repeat until done.

This is both the best way to use example code from other people, and the best way to write your own original code.

Some things you should be checking in your code:

  • Before you send a query to mySQL you should echo the query to be sure that it has been formed correctly by your program logic.
  • After you run the query you should look at if the result set has returned the expected values or, in the case of an insert, that the expected values have been inserted properly.

The reason you're getting so much guff from people is that you don't appear to have done this testing yourself, which is what we all spend the majority of our time doing with our own code. For me it's usually 15% writing, 75% testing, 10% cursing/drinking.

Sammitch
  • 30,782
  • 7
  • 50
  • 77
  • 1
    I'm curious to how you think I got 12 chapters into a book full of errors in its example code, without ever trouble shooting the examples. The problem here is, one piece of this code runs the log in program and the rest is just security and calling login details. I don't think I can just set up an echo break and have anything happen. – David Dec 31 '12 at 19:52
  • @David Actually, one thing that sticks out about the code is that it is using HTTP Authentication. If you're running this on a server with certain extensions it just won't work. ie: I've tried to make something like this work on Dreamhost, and there's just no way. Also, HTTP Auth has some serious drawbacks, the foremost being that [users cannot log out](http://stackoverflow.com/questions/449788/http-authentication-logout-via-php) by any means short of changing their password. I would suggest re-jiggering the code to use form-based login and sessions. It will drive you less crazy. – Sammitch Dec 31 '12 at 20:03
  • You would figure the author would show how to do that before showing how to create a log in with HTTP authentication. He even acknowledges there could be a problem, but gives no work around. Is there a way to check if HTTP authentication is on my system and functioning? – David Dec 31 '12 at 20:22
  • Come to think of it, if HTTP authenticate wasn't active on my system, wouldn't I just get an error message an not be able to type in a username and password? – David Dec 31 '12 at 20:35
  • No, since the response header will still be sent. The appropriate variables/elements will just never be set. – Ignacio Vazquez-Abrams Dec 31 '12 at 20:44
  • @David PHP can send you the necessary header, and you can send the necessary header back, ***but*** certain servers/modules/methods of running PHP interfere with these headers being passed back to the PHP process properly. Try `var_dump($_SERVER)` on the return page. – Sammitch Dec 31 '12 at 20:56
  • I don't know what that means, but I'm sure by this point I should. Like I said, Oreily is really dropping the ball on PHP, check out their Head First and Missing Manual titles on it if you don't believe me, their examples even have code that points to broken links and pages with invalid jpegs. The book I'm reading right now is a second edition, which was apparently published without any significant changes, just fixes to a bunch of mistakes made in the first edition. – David Dec 31 '12 at 22:00
  • I wonder if these guys are any better... it's free and I'm tried of buying poorly proofread books, so at least no more money down the drain. – David Dec 31 '12 at 22:09
  • W3Schools has... issues. Be wary of using them for more than a reference, and only after you've gone through the actual documentation. – Ignacio Vazquez-Abrams Jan 01 '13 at 11:00
  • Well what am I supposed to do? No one wants to write a worth while book, and the author's don't even want to visit their own forums anymore! – David Jan 01 '13 at 16:22
  • 1
    The problem with writing/reading a book on PHP is that it is a constantly moving target. So much is always being changer or phased out that you're always going to run into something like this unless the book has *just* been published. You basically need to *live* at PHP.net and google to write PHP. – Sammitch Jan 01 '13 at 18:50
  • w3 seems okay so far, they seem more interested in teaching basics and letting you figure out what to do with it. Better than teaching specific methods and having them blow up in your face! – David Jan 01 '13 at 23:13
0

@David I feel the frustration. I tried this example without creating the database beforehand (e.g., referencing an existing one on localhost) and the salt algorithm was a hard fail. For me, the error concerned

    $token = md5($salt1$pw$salt2); 

that is, where $pw was dubbed an 'unexpected occurrence'. Not true, but idk I'm almost thinking they did it on purposes to force us to learn PDO.

OcelotXL
  • 159
  • 2
  • 6
  • 14