A new venture: musketeers.me

This has been in the works for a bit now, and I\'m excited to announce that along with Eli White, Kevin Bruce, and Sandy Smith, we\'ve formed muskeeters.me. We worked well together at our previous venture, that we wanted to keep it going. The new venture will do the usual web consulting, from strategy and planning, systems architecture with an eye on scaling, through design and development. We\'ve also kicked around our own product ideas to work on, which we can share sooner rather than later.

Tags: consulting, Real Life

Automating FTP uploads

{width="300" height="361" style="float: right;"}

I needed to automate copying files for a website that I was building. Since this site was hosted on an inexpensive shared hosting plan, I didn\'t have the luxury of shell or rsync access to automate copying files from my local development environment to the host. The only option was FTP, and after wasting too much time manually tracking down which files I needed to update, I knew I needed an automated solution. Some googling led me to lftp, a command-line and scriptable FTP client. It should be available via your distribution\'s repository. Once installed, you can use a script like the one below to automatically copy files.

{syntaxhighlighter BASH} #!/bin/sh # paths HERE=`pwd` SRC=\"\$HERE/web\" DEST=\"\~/www\" # login credentials HOST=\"ftp.commodity-hosting.com\" USER=\"mysiteusername\" PASS=\"supersecretpassword\" # FTP files to remote host lftp -c \"open \$HOST user \$USER \$PASS mirror -X img/* -X .htaccess -X error_log --only-newer --reverse --delete --verbose \$SRC \$DEST \"

The script does the following:

  • Copy files from the local ./web directory to the remote \~/www directory.
  • Uses \$HOST, \$USER, \$PASS to login, so make sure your script is readable, writeable, and executable only by you and trusted users.
  • the lftp command connects and copies the files. The -c switch specifies the commands to issue, one per line. The magic happens with the mirror command which will copy the files. Since we added the --only-newer and --reverse switches, this will upload only files which have changed.
  • You could be a little safer and remove the --delete switch, which will remove files from the destination which are not on your local machine.
  • You can use the -X to give it glob patterns to ignore. In this case, it won\'t touch the img/ directory or the .htacess file.

If you\'re still moving files over FTP manually, even with a good GUI, it\'ll be worth your time to automate it and make it a quicker, less error-prone process.

Tags: Linux, Programming

Using bcrypt to store passwords

The linkedin password breach highlighted once again the risks associated with storing user passwords. I hope you are not still storing passwords in the clear and are using a one-way salted hash before storing them. But, the algorithm you choose to use is also important. If you don\'t know why, go read You\'re Probably Storing Passwords Incorrectly. The choice, at the moment, seems to come down to SHA512 versus Bcrypt encryption. There\'s a StackOverflow Q&A discussing the merits of each. Bcrypt gets the nod since its goal is to be slow enough that brute force attacks would take too much time to be feasible, but not so slow that honest users would really notice and be inconveniencedĀ [1]. I wanted to switch one of my personal apps to use bcrypt, which on php means using Blowfish encryption via the crypt() function. There\'s no shortage of classes and examples for using bcrypts to hash a string. But I didn\'t find anything that outlined how to setup a database table to store usernames and passwords, salt and store passwords, and then verify a login request.

Storing passwords in Mysql

To store passwords in a MySQL database, all we need is a CHAR field of length 60. And you don\'t need a separate column for the salt, as it will be stored as part of the password. The SQL for a minimal Users table is shown below.

CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` char(60) NOT NULL,
  PRIMARY KEY (`id`),
);

When a user registers providing a username and password, you have to generate a salt and hash the password, before saving it. This gist helped me figure out how to salt and hash them.

function save_user($username, $password, PDO $db)
{
    // create a random salt
    $salt = substr(str_replace('+', '.', base64_encode(sha1(microtime(true), true))), 0, 22);

    // hash incoming password - this works on PHP 5.3 and up
    $hash = crypt($password, '$2a$12$' . $salt);

    // store username and hashed password
    $insert = $db->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
    $insert->execute($username, $hash)
}

Authenticating Users

When a user comes back to your site and tries to login, you retrieve their credentials and then compare the expected password to the supplied password. Remember we were clever and stored the salt as part of our hash in the password field? Now, we can reuse our stored password as the salt for hashing the incoming password. If its the right password, we\'ll have two identical hashes. Magic!

function validate_user($username, $password, PDO $db)
{
    // attempt to lookup user's information
    $query= $db->prepare('SELECT * FROM users WHERE username=?';
    $query->execute(array($username));

    if (0 == $query->rowCount()) {
        // user not found
        return false;
    }

    $user = $query->fetch();
    // compare the password to the expected hash
    if (crypt($password, $user['password']) == $user['password']) {
        // let them in
        return $user;
    }

    // wrong password
    return false;
}

Those are the basics for using bcrypt to store passwords with PHP and MySQL. The main difference I found, was that the hashing and comparison of hashes now happens in PHP. With MD5 and SHA algorithms, you could invoke them using the database functions provided by MySQL. As far as I could find, it doesn\'t have a native Blowfish/bcrypt function. If your system provides a crypt() call, you maybe be able to use Blowfish encryption, but it won\'t be an option on Windows systems.

Tags: Mysql, PHP, Windows