Hashes, salts, Doctrine and "binary" types

Questions about the MySQL, PostgreSQL, and most other databases, as well as using it with PHP can be asked here.

Moderator: General Moderators

Post Reply
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Hashes, salts, Doctrine and "binary" types

Post by Chris Corbyn »

I recently learned a hard lesson trying to migrate a vBulletin user table into a new application. vBulletin uses salts for passwords. The vBulletin user table was all in latin1 and the app I was moving to was utf8.

Now, vBulletin salt column was (stupidly!!) a char(3) even though the data in it was basically:

Code: Select all

pack('C*', rand(0, 255), rand(0, 255), rand(0, 255));
To me, this should never have been char(3) and it should have been binary(3). It's not character data it's just 3 random bytes.

This caused loads of problems when moving the data to a utf8 table since MySQL tried to transcode some of the salts which meant they no longer worked with the hashes.

So now I'm trying to set up a schema in Doctrine that needs to use salts...

Is it just me or is the only binary column type doctrine supports "blob" ? That's hella overkill for a 3 byte salt. I may resort to using an ascii-only salt or base64 encoding it if Doctrine can't do small binary types.
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Hashes, salts, Doctrine and "binary" types

Post by josh »

Well if you're not using schema abstraction you could just specify the column type, but if you did try to write 4 bytes a byte would be mysteriously lost
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Hashes, salts, Doctrine and "binary" types

Post by Chris Corbyn »

I tried setting the field type as blob(3) which Doctrine decided to make tinyblob (without specified length). I guess it'll do. Hopefully Doctrine add support for small binary types in future.

Still getting used to Doctrine but got a few gripes with it. Lack of accessor generation plain sucks (you have to use $obj->fieldName = $value). That's fine if your domain objects are pure data container but when you start adding logic such as setPassword() that generates the data for "hash" and "salt" it starts looking like a messy combination of method invocations and property setting. I've just hacked the source to support accessors for now.

Anyone familiar with Doctrine who can give any pointers regarding accessor/mutator generation in the cleanest way (without patching Doctrine itself)?
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Hashes, salts, Doctrine and "binary" types

Post by josh »

Well what I did was created mutators and accessory that interally called $this->property. There's no "generation" in the sense it just uses __call I believe. I meant set the field type on the table itself even though doctrine will think its a regular binary field. Doctrine should not change the field type unless you're using the migrations components
User avatar
Chris Corbyn
Breakbeat Nuttzer
Posts: 13098
Joined: Wed Mar 24, 2004 7:57 am
Location: Melbourne, Australia

Re: Hashes, salts, Doctrine and "binary" types

Post by Chris Corbyn »

josh wrote:Well what I did was created mutators and accessory that interally called $this->property. There's no "generation" in the sense it just uses __call I believe. I meant set the field type on the table itself even though doctrine will think its a regular binary field. Doctrine should not change the field type unless you're using the migrations components
Ah got ya regarding the field type.

Regarding the accessors, __call() is not used for the columns. For example, if I have fields:

username
password_hash
password_salt

I have to do:

Code: Select all

$user->username = 'person';
$user->password_hash = '...';
$user->password_salt = '...';
Now the properties with the underscores look weird for one thing, and also I'd like to avoid revealing the logic for the salting so I'd prefer to combine password_hash and password_salt setting into a single method setPassword().

Code: Select all

$user->username = 'person';
$user->setPassword('...');
Inside the setPassword() method it deal with the salt.

It just looks weird having the combination of method calls and __set() invocations.

Granted, I could go through all of my fields as add the methods to the User class but that would be really tedious (and half the reason I adopted Doctrine was to cut out all that time wasted writing getters and setters).

What I did in the end was hacked Record::__call() to add this logic (needs optimizing, I just moved on in a hurry)...

Code: Select all

   public function __call($method, $args)
    {
        if (($template = $this->_table->getMethodOwner($method)) !== false) {
            $template->setInvoker($this);
            return call_user_func_array(array($template, $method), $args);
        }
 
        foreach ($this->_table->getTemplates() as $template) {
            if (is_callable(array($template, $method))) {
                $template->setInvoker($this);
                $this->_table->setMethodOwner($method, $template);
 
                return call_user_func_array(array($template, $method), $args);
            }
        }
        
        //Patch to add accessors (Chris Corbyn)
        $accessor = substr($method, 0, 3);
        if ($accessor == 'get' || $accessor == 'set') {
          $transformedName = substr($method, 3);
          foreach ($this->_table->getColumnNames() as $column) {
            $columnParts = explode('_', $column);
            foreach ($columnParts as $i => $part) {
              $columnParts[$i] = ucfirst($part);
            }
            if (implode('', $columnParts) == $transformedName) {
              if ($accessor == 'get') {
                return $this->get($column);
              } else {
                return $this->set($column, array_shift($args));
              }
            }
          }
        }
        //End patching
 
        throw new Doctrine_Record_Exception(sprintf('Unknown method %s::%s', get_class($this), $method));
    }
This turns the username, password_hash and password_salt fields into:

Code: Select all

$user->setUsername('person');
$user->setPasswordHash('...');
$user->setPasswordSalt('...');
So now when I decide to hide the hash/salt stuff and just add a setPassword() method my interface looks consistent:

Code: Select all

$user->setUsername('person');
$user->setPassword('...');
I'd have thought Doctrine would have already included this functionality, and more. Propel (almost identical to Doctrine) lets you provide names for the accessors and mutators.

But the symfony framework uses Doctrine and they have accessors and mutators generated so I'm confused how they do it. I can't find anything online about it, apart from other people asking why it's not included.

Did you just hard-code mutators and accessors for everything? That must be pretty tedious?
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Re: Hashes, salts, Doctrine and "binary" types

Post by josh »

I started using it for a while but ran into problems, I decided itd be easier to write data shuffler then try to extend something, plus AR itself isnt as extensible as DM. I find its very rare I have a pure setter or getter w/ no business logic. In the rare event I do I try to factor it into a super class and if that can't be done I can have code snippets in my IDE. You could subclass Doctrine_Record and write your own __call method that calls __set if you have a lot of empty data container objects. When I wrote __call before I guess I meant __set. Thing is later when you come looking for setSomething() and can't find it you'll pull out your hair. Abstraction is always a mixed blessing
Post Reply