Archive

Posts Tagged ‘php’

Securing Passwords, One Way Hashes, PBKDF2, PHP and You

June 8th, 2012 2 comments

Plain text passwords and simple one way hashes are not enough to protect your users. You need salt, pepper, and peanut butter. Am I crazy you ask? Maybe, but read on.

It happens to big huge companies (LinkedIn, Last.fm, eHarmony), the little guys, and everything in between. Databases get breached and passwords get hacked. It always surprises me when I hear about how many thousands of users had the password “password”, or that the target’s password hashes were cracked in a matter of hours or days- or worse, their passwords were plain text. At this point, it is so easy to make passwords pretty secure with just basic knowledge of cryptography and hashing. As a matter of fact, as a competent developer, you don’t need to know much at all about the how’s and why’s of crypto to secure your users’ data.

First, do not think you are safe because you run your passwords through MD5 or SHA-256. MD5 has been cracked and SHA-256 is barely better than storing their passwords in plain text. Cryptograhic hash functions are NOT password hash functions!

One Way Hashing

A one way hash performs a bunch of mathematical operations that transform input into a (mostly) unique output, called a digest. Because these operations are one way, you cannot ‘decrypt’ the output- you can’t turn a digest into the original input. Good cryptographic hash functions should not generate digests that are the same for different input. Additionally, when the input is changed, just slightly, the resulting digest should be very different.

A typical use case would be when a user signs up for a website and creates a password. The conscientious developer takes the plain text password, runs it through a hashing function (let’s say, MD5) and stores the result in the database. When the user goes to log in the next time they enter their password and the authentication mechanism runs it through MD5 and compares the result against what is stored in the database.

That sounds pretty safe, right? Wrong. It’s akin to locking the door and leaving the window open. If the database was stolen it might make it harder to infer anything about the passwords just by looking at the data, but it doesn’t really make it any harder to guess or “crack” the password.

Password Hash Functions

… are not the same as cryptographic hash functions

Just using a cryptographic function on a plain text password doesn’t defend it very well. There a number of major problems and threats that are not being avoided. The two biggest are speed and recognizability of hashes.

Hashing Speed

Cryptographic hash functions are used for lots of things, most of them have to do with fingerprinting and verifying data. They are designed to be very fast so that the encryption processes isn’t slowed down. This presents a big problem for password hashing. Speed. The faster a function creates a digest, the more frequently an attacker can guess the password and compare the output. MD5, for instance, is so fast that on basic hardware you could guess over 5 billion times per second. Think about it for a second, do you need that speed to allow your users to log in? When it takes 15 seconds to enter your username and password, a few second to log in, and a few seconds of perceived page load time, will they notice the difference between .000001 seconds or 1 second for the authentication mechanism? The answer is no, not to enough of a degree to degrade their experience. For password hashing, slower is good.

Recognizability of Hashes

What happens when 10,000 people all use “password” as their password? Their hashes are all the same! If you just get one account cracked, you automatically crack everyone else with the same hash. If an attacker has a huge, precomputed list of hashes (called a rainbow table), they can scan your database looking for any hashes that match. They’ve already cracked accounts without even guessing a password yet! They could have a huge percentage of your system’s passwords before ever once making a guess.

Fortunately though, there are a few relatively easy things you can do to make their life harder. You don’t need to do anything heroic and the code isn’t even that tricky. Heck, most of it already exists and is free to use.

Salting

Talk about low hanging fruit. All you have to do is add some random characters to their password (and keep track of them). A salt is a random sequence of data which is added to the hash function or the password string itself. Say you generated a salt “12345″ and had a password “password”, you could put them together “password12345″ and run that through your hash function to produce a digest that wouldn’t be so easily given up. Every password should have its own salt and should be at minimum, 32 characters or more to make it harder to guess the digest.

This is a basic salt generation algorithm. Do NOT use this function for generating salts where you are trying to protect details like credit card numbers, or even email addresses for that matter. It’s a pretty poor implementation, really.

public static function CreateSalt($length = 128, $validChars = null)
{
    $salt = '';
    list($usec, $sec) = explode(' ', microtime());

    $seed = ((float)$sec + ((float)$usec * 100000)) * ((float)microtime() * 1000000);
    mt_srand($seed);

    $inputs = array_merge(range('z','a'), range(0,9), range('A','Z'), array('@','!','#','%','&','*','+','_','-','~','?','.'));

    $inputsLength = count($inputs) - 1;

    for($i = 0; $i < $length; $i++)
        $salt .= $inputs{mt_rand(0, $inputsLength)};

    return $salt;
}

When we create a user password we’ll generate a salt, add it to the password string, hash the password to get a digest, then store the salt and digest in the database. To log the user in subsequently we could use functions like the following:

function HashPassword($password, $salt)
{
    return hash('sha256', $password . $salt);
}

function IsValidPassword($password, $salt, $digest)
{
    return (HashPassword($password, $salt) == $digest);
}

Password Stretching

Stretching is creating a digest of a digest (of a digest of a digest … of a digest … you get it.) If you create a digest of a password, then create a digest of that X number of times you can no longer simply create a digest (from a rainbow table or otherwise) and compare it directly to the digest that is stored in the database. To compare passwords you’ll have to run the exact same number of iterations if hashing digests to compare passwords. This is useful on multiple fronts: it slows things down and (in conjunction with salted passwords) your hashes no longer look the same as everyone else’s. It stands to reason that if hashing a password once takes X amount of time, hashing it twice will take approximately 2X. You’ve just cut in half the number of times an attacker can guess your passwords. Congratulations! A good system takes so long to process a single digest that guessing a password using brute force will take more than a lifetime.

Let’s modify our password hashing function:

function HashPassword($password, $salt, $iterations = 1024)
{
    // Create the first digest
    $output = hash('sha256', $password . $salt);
    for($i = 0; $i < $iterations; $i++)
    {
        // Re-salt every hash for extra randomization
        $output = hash('sha256', $output . $salt);
    }
}

Notice that I have re-salted every hash to add extra randomization to the digest… just another wrinkle to throw at an attacker.

Pepper

Additionally, you can have an application wide salt, called a pepper. Think of it as a salt for the salt, except this salt is unique only to the application, server, environment, or database.
You could use it like that hash('sha256', $pepper . $password . $salt);

Adaptive Key Derivation

Adaptive key derivation functions generate digests from passwords while applying salts and stretching. They implement many more wrinkles and are tested against attack vectors you may never think of- which is the important part. They are tested against attack vectors. Rolling your own cryptographic functions introduce a lot of unnecessary exposure and take more time than using generally accepted libraries, implementations and functions. I’m going to focus on the one I know best, PBKDF2. There are others such as bcrypt and mcrypt

Peanut Butter Keeps Dogs Friendly Too

PBKDF2 (Password-Based Key Derivation Function) is probably the most widely used derivation function. It is a container for a hash function, e.g. SHA-1 or RIPEMD,. For each input it applies a salt and iterates the hash many times in such a way that not much entropy (length and randomness) is lost. Primarily, it is done in such a way that it is SLOW to generate a single digest. The US government and NSA use this for generating strong encryption keys.

Adaptive keys are great first step, but remember, this is one tiny piece of securing user data.

Below is a very basic class I created that can be used for generating salts and digests through a variety of ways. You can download it here. This file will be updated regularly, so stay in touch!

<?php
/**
 * PasswordUtil.php
 * @package AC
 */

 /**
  * AC_PasswordUtil
  *
  * Password hashing and generation utilities
  * @package AC
  * @category Security
  * @version $Id:$
  * @author Mustafa Ashurex <[email protected]>
  */
class AC_PasswordUtil
{
    /**
     * @var int Default hash key length
     */
    const DEFAULT_KEY_LENGTH = 256;
    /**
     * @var int Default number of times to iterate a hash
     */
    const DEFAULT_ITERATIONS = 1024;
    /**
     * @var string Default hash algorithm to use for PBKDF2
     */
    const DEFAULT_PBKDF2_ALGO = 'SHA256';
    /**
     * @var string PBKDF2 algorithm name
     */
    const ALGO_PBKDF2 = 'PBKDF2';
    /**
     * @var string Whirlpool algorithm name
     */
    const ALGO_WHIRLPOOL = 'WHIRLPOOL';


    /**
     * Return the default characters to use for generating salts.
     * @static
     * @return string[] Default characters to use for generating salts.
     */
    public static function DefaultSaltChars()
    {
        return array_merge(range('z','a'), range(0,9), range('A','Z'), array('@','!','#','%','&amp;amp;amp;amp;amp;','*','+','_','-','~','?','.'));
    }

    /**
     * Returns the supported text hashing algorithm names
     * @static
     * @return string[] Supported text hashing algorithm names
     */
    public static function PasswordHashAlgorithms()
    {
        return array(
            self::ALGO_PBKDF2,
            self::ALGO_WHIRLPOOL,
        );
    }


    /**
     * Hashes a plaintext password using the parameters defined. If provided, $pepper
     * will be appended to the beginning of $password and $salt will be used in every hash
     * iteration in various ways (depending on the hash method used).
     * @static
     * @param string $password Plaintext password to hash.
     * @param string $salt A random sequence of bytes to add to the hash function.
     * @param string $pepper Another random sequence of bytes to add an extra secret to the hash generation.
     * @param string $algorithm Password hashing algorithm to use.
     * @param int $keyLength The number of bytes to return.
     * @param int $iterations The number of times to hash the text before returning the value.
     * @return string Returns $keyLength bytes of hashed $password.
     */
    public static function HashPassword($password, $salt, $pepper = null, $algorithm = self::ALGO_PBKDF2,
        $keyLength = self::DEFAULT_KEY_LENGTH, $iterations = self::DEFAULT_ITERATIONS)
    {
        if(strlen(trim($pepper)) > 0)
            $password = $pepper . $password;

        switch($algorithm)
        {
            case self::ALGO_WHIRLPOOL:
                return AC_PasswordUtil::WhirlpoolHash($password, $salt, $keyLength, $iterations);
            case self::ALGO_PBKDF2:
                // Base64 encode the output of PBKDF2 because it's binary
                return base64_encode(AC_PasswordUtil::PBKDF2($password, $salt, $keyLength, $iterations));
            default:
                throw new Exception('Unknown hash algorithm (' . $algorithm . ')!');
        }
    }

    /**
     * Create a random salt string
     * @static
     * @param int $length Number of bytes to return.
     * @param string[] $validChars Array of characters to use for the salt, overrides the default.
     * @return string Randomized salt string of $length bytes.
     */
    public static function CreateSalt($length = 128, $validChars = null)
    {
        $salt = '';
        list($usec, $sec) = explode(' ', microtime());

        $seed = ((float)$sec + ((float)$usec * 100000)) * ((float)microtime() * 1000000);
        mt_srand($seed);

        if(is_array($validChars))
            $inputs = $validChars;
        else
            $inputs = self::DefaultSaltChars();

        $inputsLength = count($inputs) - 1;

        for($i = 0; $i < $length; $i++)
            $salt .= $inputs{mt_rand(0, $inputsLength)};

        return $salt;
    }


    /**
     * Hashes the provided plaintext password using Whirlpool hash and provided parameters.
     * If the Whirlpool algorithm is not present on the system, it will fall back to MD5 if allowed which
     * is not nearly as effective. If not allowed, an exception will be thrown.
     * @static
     * @param string $password
     * @param string $salt
     * @param int $keyLength
     * @param int $iterations
     * @param bool $fallBack
     */
    public static function WhirlpoolHash($password, $salt, $keyLength = self::DEFAULT_KEY_LENGTH, $iterations = self::DEFAULT_ITERATIONS, $fallBack = false)
    {
        $hashMethod = 'whirlpool';

        if($iterations <= 0)
            throw new Exception('Iterations must be greater than 0.');
        elseif($keyLength <= 0)
            throw new Exception('Key length must be greater than 0.');
        elseif(!in_array($hashMethod,hash_algos(),true))
            $hashMethod = 'md5';

        if((!$fallBack)&amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;($hashMethod == 'md5'))
            throw new Exception('Whirlpool hash algorithm not found! Either allow for fallback to MD5 or install Whirlpool.');

        // First thing, stretch the password
        // md5 is used because it is the only hashing function that can be guaranteed to be on a majority of systems
        $output = md5($password . $salt);

        // Hash the output repeatedly
        for($i = 0; $i < $iterations; $i++)
            $output = hash($hashMethod, $output . $salt);

        // If the requested key length is too long, shrink the requested key length
        if(strlen($output) < $keyLength)
            $keyLength = strlen($output);

        return substr($output, 0, $keyLength);
    }


    /**
     * Password-Based Key Derivation Function using PBKDF2
     * as described by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
     * Note: You will want to run base64_encode on the output of this method to use it
     * as text as the output is binary.
     * @static
     * @param string $password The plaintext password to hash.
     * @param string $salt A salt that is unique to the password.
     * @param int $keyLength The length of the derived key in bytes.
     * @param string $iterations The number of times to hash the password before returning.
     * @param string $algorithm The hash algorithm to use.
     * @return Binary string of $keyLength bytes, derived from the provided $password and $salt.
     */
    public static function PBKDF2($password, $salt, $keyLength, $iterations = self::DEFAULT_ITERATIONS, $algorithm = self::DEFAULT_PBKDF2_ALGO)
    {
        $algorithm = strtolower($algorithm);

        if(!in_array($algorithm, hash_algos(), true))
            throw new Exception($algorithm . ' is not found.');
        elseif($iterations <= 0)
            throw new Exception('Iterations must be greater than 0.');
        elseif($keyLength <= 0)
            throw new Exception('Key length must be greater than 0.');

        // Determine the length of the specified hash
        $hashLength = strlen(hash($algorithm, null, true));

        // The number of iterations of the hash necessary to fill $keyLength characters
        // IE: If $keyLength is 256 but $hashLength is only 128, we'd need 2 blocks
        // to fill our $keyLength. If $keyLength was 128 and $hashLength is 256, we'd just
        // take a subset of $output when we're done.
        $blockCount = ceil($keyLength / $hashLength);

        $output = '';

        for($i = 1; $i <= $blockCount; $i++)
        {
            // Beginning hash for this block/iteration
            $iterate = $block = hash_hmac($algorithm, $salt . pack('N', $i), $password, true);

            // Hash each block the specified number of times
            for($j = 1; $j < $iterations; $j++)
            {
                // XOR each iterate
                $iterate ^= ($block = hash_hmac($algorithm, $block, $password, true));
            }
            // Block is completed, append to the output and move on to the next
            $output .= $iterate;
        }

        // Return up to $keyLength characters
        return substr($output, 0, $keyLength);
    }
}
Categories: PHP, Programming Tags: , , , ,

Authentication Using WordPress and Zend Framework

December 19th, 2011 Comments off

I recently had the need to implement a Zend Framework web app that could authorize against WordPress without necessarily using WordPress as the front end. I was very relieved to find out it was quite easy to do!

In your application’s index.php (typically found at public/index.php) you need to include the WordPress header file to make sure you have access to the WordPress functions later in your application:

defined('WORDPRESS_DIR') || define('WORDPRESS_DIR', realpath(dirname(__FILE__) . '/wordpress'));
require_once(WORDPRESS_DIR . '/wp-blog-header.php');

I have the following code in my LoginController.php (application/modules/public/controllers/LoginController.php) file.

doWordpressAuth function

protected function doWordpressAuth($username, $password)
{

    // Optional - Sanitize user input in this method
    // or handle it before calling authenticate
    $username = sanitize_user($username);
    $password = trim($password);

    $creds = array('user_login' => $username,
                   'user_password' => $password,
                   'remember' => true
            );
    $user = wp_signon($creds, false);

    if($user == null)
    {
        // Invalid username or password
        return null;
    }

     $ignore = array('empty_username', 'empty_password');
     if((is_wp_error($user))&amp;amp;&amp;amp;(!(in_array($user->get_error_code(),$ignore))))
     {
        // Login failed due to some error...
        return null;
     }

     return $user;
}

IndexAction

public function indexAction()
{
    $request = $this->getRequest();
    if(!$request->isPost()){ return $this->render('index'); }

    $username = $request->getParam('username');
    $password = $request->getParam('userpass');

    if((is_null($username))||(is_null($password)))
    {
        $this->addError('Missing username or password');
        return $this->render('index');
    }
    // Enforce username and password minimum lengths
    elseif((strlen($username) < 4)||(strlen($password) < 6))
    {
        $this->addError('Invalid username or password');
        return $this->render('index');
    }

    // Use wordpress for authentication
    if(AC_Utilities::getApplicationSetting('use_wp_auth') == true)
    {
        $user = $this->doWordpressAuth($username, $password);
        /**
         * ...
         * Various user detail handling procedures...
         * ...
         */
        $session->user = $user;
    }
    // Use built-in authentication mechanism
    else
    {
        /**
         * ...
         * Use built in login mechanism ...
         * ...
         */
    }
    // Logged in, redirect to the IndexController
    $this->_helper->redirector('index','index');
}

How to Specify Non-String PHP Data Types in Zend Config XML and INI Files

September 7th, 2011 Comments off

Zend Config INI and Zend Config XML are adapters in the Zend Framework that allow you to store your application or website configuration data (such as database connector information and salt values) in an easy to interpret text file (I happen to prefer INI files over XML because INI is more terse).

The biggest downside to these files is that they don’t store PHP data types or values like NULL, FALSE, or TRUE, and you can’t specify an int, double, or float, the adapters only return strings.

Here is the recursive code for converting all your values:

public static function convertConfigValuesFromString($config)
    {
        foreach($config as $key => $value)
        {
            if(is_string($value))
            {
                switch($value)
                {
                    case 'BOOL_FALSE':
                        $config[$key] = false;
                        break;
                    case 'BOOL_TRUE':
                        $config[$key] = true;
                        break;
                    case 'CONST_NULL':
                        $config[$key] = null;
                        break;
                    default:
                        if(substr($value,0,4) == 'INT_')
                        {
                            $config[$key] = (int) substr($value,4);
                        }
                        elseif(substr($value,0,4) == "DBL_")
                        {
                            $config[$key] = (double) substr($value,4);
                        }
                        elseif(substr($value,0,4) == "FLT_")
                        {
                            $config[$key] = (float) substr($value,4);
                        }
                        break;
                }
            }
            elseif(is_array($value))
            {
                $config[$key] = self::convertConfigValuesFromString($value);
            }
        }
        return $config;
    }

Here is how I use it in my Bootstrap.php file in my Zend Framework application:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected $_namespace = 'Ashurex';
    protected $_config;

    ... other code ...

    protected function _initConfig()
    {        
        // Use the default mechanisms built into the bootlstrap
        // for reading in the app configuration
        $config = $this->getOptions();

        $config = Ashurex_Utilities::convertConfigValuesFromString($config);   
   
        // After gathering and assigning the 
        // correct data types to our config values
        // Turn into an instance of Zend_Config for later use
        $config = new Zend_Config($config);
        // Store the config in the registry
        Zend_Registry::set('config', $config);

        $this->_config = $config;
        return $config;
    }

    ... other code ...

}

The following config file will end up looking like the following array (before turning it into a Zend_Config object)

[default]
phpSettings.date.timezone = "America/Los_Angeles"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
resources.modules[] =
resources.view.doctype = "HTML5"
app.debug.enabled = "BOOL_TRUE"
resources.db.adapter = PDO_MYSQL

[development : default]
resources.db.params.host = "localhost"
resources.db.params.username = "dbuser"
resources.db.params.password = "dbpassword"
resources.db.params.dbname = "dbname"
app.integer = "INT_1234567890"
app.double = "DBL_123456789012345.678"
app.float = "FLT_12.0000001"
session.name = "session_name"
array
  'phpSettings' => 
    array
      'date' => 
        array
          'timezone' => string 'America/Los_Angeles' (length=19)
  'bootstrap' => 
    array
      'path' => string '/var/www/html/application/Bootstrap.php' (length=50)
  'resources' => 
    array
      'modules' => 
        array
          0 => string '' (length=0)
      'view' => 
        array
          'doctype' => string 'HTML5' (length=5)
      'db' => 
        array
          'adapter' => string 'PDO_MYSQL' (length=9)
          'params' => 
            array
              ...
  'app' => 
    array
      'debug' => 
        array
          'enabled' => boolean true
      'integer' => int 1234567890
      'double' => float 1.2345678901235E+14
      'float' => float 12.0000001
  'session' => 
    array
      'name' => string 'session_name' (length=12)

Install / Upgrade PHP 5.3 on CentOS 5 / RHEL 5

September 6th, 2011 Comments off

As I was setting up a staging VM (CentOS 5), I quickly realized I could only get PHP 5.1 from the standard repositories. Thanks to this blog post, I was able to quickly get up and running with PHP 5.3.

From the command line on your server, run the following:

wget http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm
wget http://rpms.famillecollet.com/enterprise/remi-release-5.rpm
rpm -Uvh epel-release-5-4.noarch.rpm
rpm -Uvh remi-release-5.rpm
yum --enablerepo=remi update php php-* mysql

This, of course, assumes that your LAMP stack is already installed. If not, you would want to replace ‘update’ with ‘install’.

Make sure that you run the last update command (if you’re running an update) as noted above. Not including one of the three packages will result in a bunch of file conflict errors.

Categories: Linux Tags: , , , , ,

Using mysqli Extension to Query MySQL from PHP Instead of the mysql Extension

August 11th, 2011 Comments off

I was answering a very basic question on stackoverflow today that boiled down to “How do I query mysql from PHP using a user input” and was horrified to see the first two answers.

The first answer went along the lines of:

$result = mysql_query("SELECT * FROM table WHERE field = '". $_POST['input'] ."'");
while($row = mysql_fetch_array($result)){
    //do stuff
}

This is bad. Very bad. Very, very bad.
This is the kind of thing that allowed (maybe a bit of hyperbole, but stick with me) Sony to be brought to its knees. This is how all your user data gets stolen. This is how Anonymous makes you look like a fascist who wants to eat babies. It’s bad. It did come from someone with a pretty low score on SO, but I’m living proof that a low score on SO doesn’t mean you don’t know what you’re doing.

Why?

The first MAJOR problem is: You are taking direct, unsanitized user input. You have not validated that the input is correct, even relates to what you are having them input, or is safe.
By taking direct input straight from the user (by directly using the $_POST variable nonetheless) they could attack via sql injection at worst, and the $_POST field may not exist, giving you an error, at best.

The second problem is the use of the mysql extension in PHP. According to the manual, the mysql extension should only be used in 4.1.3 and lower, anything newer should be using mysqli for improved performance and security.

I have heard there are some issues with mysqli in certain cases that essentially require the use of the mysql_ extension to get around them. As far as I know, these are few in number and getting better by the day. I personally, haven’t ever had a problem with mysqli.

On to a more correct example:

$db = new mysqli("localhost","user","password","database");

if(mysqli_connect_error())
{
    printf("Connection failed:%s \n",mysqli_connect_error());
    exit();
}

$input = mysqli_real_escape_string($db, $_POST['search']);

/* 
 * Validate that $input is correct after escaping
 * Implement your validation below!
 */

if($result = $db->query("SELECT * FROM table WHERE field = $input", MYSQLI_ASSOC))
{
    while($row = $result->fetch_object())
    {
        // $row is an associative array
        // Do something here
    }
}
$db->close();

What’s better about this code?

First, it’s not perfect. You could pick it apart, but it’s a good example of where to start and I wrote it in about 45 seconds.
It shows:

  • Connecting to the mysql database
  • Ensuring a connection occured and handling the mysql connection error
  • Querying the database
  • Escaping user input AND THEN validating
  • Iterating through the mysql results
  • Closing the database connection (people never remind beginners to do this)!

The basics shouldn’t be so complicated that it takes two months to make heads or tails of what your code really does (I’m looking at you Zend Framework), but it should start with a good explanation of the big picture.

Categories: Programming, Tutorial Tags: , , ,

Adding HTML Tags to a Zend Form Element

July 13th, 2011 Comments off

For the purposes of styling I had the need to put an opening <div> at the beginning of one element, and a closing </div> tag at the end of another. The Zend Framework guides don’t do a great job of detailing this.

On the element you want to add the opening div tag you would do the following:

$open_element->addDecorator(array(
    array('openDiv' =>'HtmlTag'),
    array('tag' => 'div', 'openOnly' => true)
));

On the element you want to add the closing div tag:

$close_element->addDecorator(array(
    array('closeDiv' =>'HtmlTag'),
    array('tag' => 'div', 'closeOnly' => true)
));

Removing ShareThis from your WordPress Front Page

July 7th, 2011 Comments off

After installing the ShareThis social plugin for WordPress I was surprised to find that there isn’t an easy option to exclude the social links from showing up on the front page. Luckily, it’s as easy as inserting a small bit of code into the sharethis.php plugin file.
On my system the file is located at

{wordpress root directory}/wp-content/plugins/share-this/sharethis.php

Open up the file in your favorite editor and go to nearly the bottom of the file and find the function (around line 601 in the version that I have) st_makeEntries

Add in the following code

if(is_front_page()){ return ''; }

So that the beginning of your function looks like this:

function st_makeEntries(){
        if(is_front_page()){ return ''; }
        global $post;
        ... the rest of the function follows ...

We are basically bypassing the code that builds the social badges and returning nothing instead of the HTML that the code would otherwise build.

Categories: Tutorial Tags: , , ,

Custom Username Validator Using Zend_Validate_Abstract for Zend Framework

July 6th, 2011 Comments off

Do you find yourself having to use multiple steps to validate a new username? If you’ve got a create user screen, or some other form that requires the creation of a username (or some other input that must be unique and is in a datastore), you can create a custom validator that uses a database mapper to validate that the input is unique.

First step, create a lookup method in your mapper (or whatever class you use for datastore access) that will query your datastore to verify the input is unique. In this case, I’m using a MySQL database backend and am using a mapper/model hierarchy and am only showing the relavant function from my mapper class.

...
public static function isRegisteredUser($username)
{
    try
    {
        $table = new Ashurex_Model_DbTable_UserView();
        $stmt = $table->select()
                    ->where('username = ?')
                    ->bind(array($username));

        $row = $table->fetchRow($stmt);

        if(!($row == null))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    catch(Exception $e)
    {
        self::logException(__METHOD__,$ex);
        return null;
    }
}
...

Next, we’ll create a custom validator that extends Zend_Validate_Abstract and utilizes our mapper function during validation.

class Ashurex_Validate_Text_Username extends Zend_Validate_Abstract
{
	const INVALID      	= 'textInvalid';
    const NOT_NAME    	= 'notName';
    const STRING_EMPTY	= 'textStringEmpty';
    const STRING_LENGTH = 'textStringLength';
    const NAME_TAKEN    = 'textNameTaken';

	protected static $_filter = null;
	protected $_allowEmpty;

    protected $_messageTemplates = array(
        self::INVALID      	=> 'Invalid characters entered',
        self::NOT_NAME 		=> 'Input contains characters not valid for a username',
        self::STRING_EMPTY 	=> 'No input entered',
        self::STRING_LENGTH => 'Username must be between 6 and 64 characters',
        self::NAME_TAKEN    => 'Username is already taken'
    );

	public function isValid($value)
    {
		// Make sure a value was entered
        if(($value == null)||($value == ''))
        {
            $this->_error(self::STRING_EMPTY);
            return false;
        }

		// The value must be a string
        if (!is_string($value)) {
            $this->_error(self::INVALID);
            return false;
        }

		// Set the internal value to the input
        $this->_setValue($value);

		// Validate that the input is alphanumeric
        $al = new Zend_Validate_Alnum();
        if(!$al->isValid($value))
        {
            $this->_error(self::INVALID);
            return false;
        }

		// Validate that the string length meets requirements
        $sl = new Zend_Validate_StringLength(array('min' => 6,'max' => 64));
        if(!$sl->isValid($value))
        {
            $this->_error(self::STRING_LENGTH);
            return false;
        }

		// Make sure the username doesn't already exist
        if(Ashurex_Model_Mapper_User::isRegisteredUser($value))
        {
            $this->_error(self::NAME_TAKEN);
            return false;
        }

        return true;
    }
}

Finally, we can use our new validator just as we would any other validator. The following code is an excerpt from my custom create user form:

class Ashurex_Form_Admin_AddUser extends Zend_form
{
    public function init()
	{
		$this->setName('add_user_form');
        $this->setMethod('post');

		$username = new Zend_Form_Element_Text('username');
		$username->setFilters(
			array(
				new Zend_Filter_StringTrim(),
				new Zend_Filter_StringToLower(),
			));
		$username->setValidators(
			array(
				new Ashurex_Validate_Text_Username(),
			));
		$username->setRequired(true);
		$username->setLabel('Username');

                ... lots of other code follows ...
		
	}
}

Zend Framework HTML5 Form Elements

July 6th, 2011 Comments off

By default, Zend Framework 1.11 doesn’t have any native HTML5 form elements, but with some help from Enrise you can use their HTML5 Form Elements. I won’t go over the intricacies of creating custom forms and elements, but I will quickly show how to use the Glitch_Form_Element objects in a useful manner.

One of the neat things about the Glitch HTML5 elements is that they auto-detect the doctype being used in your Zend Framework application and will switch how they render to HTML. For example, if your doctype is set to ‘HTML5′ the Glitch_Form_Element_Text_Email object will render as an HTML5 email input and look like:

<input id="email" type="email" name="email" value="" />

However, if your doctype is set to something else, it will render it as a standard text input like so

<input id="email" type="text" name="email" value="" />

Pretty slick, eh? The best part is, the functionality is already built in. All you have to do is make sure you are setting your doctype.
I set mine in my Bootstrap.php file like so:

protected function _initView()
{
    // create view here if you need to change default options, but front
    // controller will create view automatically.
    $documentType = new Zend_View_Helper_Doctype();
    $documentType->doctype($this->_config->resources->view->doctype);
}

My application.ini config file has the default configuration block:

[default]
phpSettings.date.timezone = "America/Los_Angeles"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
resources.modules[] =
resources.view.doctype = "HTML5"

app.debug.enabled = 1
resources.db.adapter = PDO_MYSQL

Finally, my form class looks just about like any other Zend_Form:

class Ashurex_Form_Admin_AddUser extends Zend_Form
{
    public function init()
    {
	$this->setName('add_user_form');
	$this->setMethod('post');

	$username = new Zend_Form_Element_Text('username');
	$username->setFilters(
		array(
			new Zend_Filter_StringTrim(),
			new Zend_Filter_StringToLower(),
		));
	$username->setValidators(
		array(
			new Zend_Validate_Alnum(),
			new Zend_Validate_StringLength(array('min' => 6,'max' => 64)),
		));
	$username->setRequired(true);
	$username->setLabel('Username');

	$firstname = new Zend_Form_Element_Text('first_name');
	$firstname->setLabel('First Name');
	$firstname->setRequired(true);
	$firstname->setFilters(
		array(
			new Zend_Filter_StringTrim(),
		));
	$firstname->setValidators(
		array(
			new Ashurex_Validate_Text_PersonName(),
			new Zend_Validate_StringLength(array('min' => 1,'max' => 64)),
		));

	$lastname = new Zend_Form_Element_Text('last_name');
	$lastname->setLabel('Last Name');
	$lastname->setRequired(true);
	$lastname->setFilters(
		array(
			new Zend_Filter_StringTrim(),
		));
	$lastname->setValidators(
		array(
			new Ashurex_Validate_Text_PersonName(),
			new Zend_Validate_StringLength(array('min' => 1,'max' => 64)),
		));

	$email = new Glitch_Form_Element_Text_Email('email');
	$email->setLabel('Email');
	$email->setRequired(true);
	$email->setFilters(
		array(
			new Zend_Filter_StringTrim(),
			new Zend_Filter_StringToLower(),
		));

	$submit = new Zend_Form_Element_Submit('submit');
	$submit->setRequired(false);
	$submit->setIgnore(true);
	$submit->setLabel('Save');

	$cancel = new Zend_Form_Element_Submit('cancel');
	$cancel->setRequired(false);
	$cancel->setIgnore(true);
	$cancel->setLabel('Cancel');

	$this->addElements(array($username,$email,$firstname,$lastname,$submit, $cancel));
    }
}