From 33de1003213c3ecc540ae0f93859ec8d91407734 Mon Sep 17 00:00:00 2001 From: orchid-hybrid Date: Tue, 10 Jun 2014 02:54:06 +0100 Subject: [PATCH 1/2] Update password.php Initial commit adding password hashing support to password fields. This adds two new options to the password field: Hash (a boolean) and Salt (a string). Hashing is enabled by default but is optional (for backwards compatability) and can be turned off. We've chosen one iteration and 32 bytes for the size of the derived key. I think these values would be suitable in general. Note that this means users should not inspect the value of a password field anymore: The way to interface with hashed passwords is to hash the value you want to test it against with the same salt and compare. Reference for the choice of pbkdf2 with sha1: http://security.stackexchange.com/a/31846/45116 --- classes/fields/password.php | 41 +++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/classes/fields/password.php b/classes/fields/password.php index c3b898cd64..738cb7b985 100644 --- a/classes/fields/password.php +++ b/classes/fields/password.php @@ -60,7 +60,19 @@ public function options () { 'default' => 255, 'type' => 'number', 'help' => __( 'Set to -1 for no limit', 'pods' ) - )/*, + ), + self::$type . '_hash' => array( + 'label' => __( 'Hash', 'pods' ), + 'default' => 1, + 'type' => 'boolean', + 'help' => __( 'Securely hash passwords when storing them', 'pods' ) + ), + self::$type . '_salt' => array( + 'label' => __( 'Salt', 'pods' ), + 'default' => "", + 'type' => 'string', + 'help' => __( 'Used for salting the hashes of secure fields', 'pods' ) + )/*, self::$type . '_size' => array( 'label' => __( 'Field Size', 'pods' ), 'default' => 'medium', @@ -85,14 +97,19 @@ public function options () { * @since 2.0 */ public function schema ( $options = null ) { - $length = (int) pods_var( self::$type . '_max_length', $options, 255 ); + if( pods_var( self::$type . '_hash', $options ) ) { + return 'VARCHAR(32)'; + } + else { + $length = (int) pods_var( self::$type . '_max_length', $options, 255 ); - $schema = 'VARCHAR(' . $length . ')'; + $schema = 'VARCHAR(' . $length . ')'; - if ( 255 < $length || $length < 1 ) - $schema = 'LONGTEXT'; + if ( 255 < $length || $length < 1 ) + $schema = 'LONGTEXT'; - return $schema; + return $schema; + } } /** @@ -174,12 +191,14 @@ public function validate ( $value, $name = null, $options = null, $fields = null * @since 2.0 */ public function pre_save ( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) { - $length = (int) pods_var( self::$type . '_max_length', $options, 255 ); + $length = (int) pods_var( self::$type . '_max_length', $options, 255 ); - if ( 0 < $length && $length < pods_mb_strlen( $value ) ) { - $value = pods_mb_substr( $value, 0, $length ); - } + if ( 0 < $length && $length < pods_mb_strlen( $value ) ) + $value = pods_mb_substr( $value, 0, $length ); - return $value; + if ( pods_var( self::$type . '_hash', $options ) ) + return hash_pbkdf2('sha1', $value, pods_var( self::$type . '_salt', $options ), 1, 32); + else + return $value; } } From ebefe906347adb95b5f2890855a093168f663b00 Mon Sep 17 00:00:00 2001 From: orchid-hybrid Date: Wed, 11 Jun 2014 00:01:08 +0100 Subject: [PATCH 2/2] Update password.php Improved the schema function so that it sets the length variable. Added a helper predicate called tagged_hash_string that checks whether a string is of the form "{SHA1}" . 32 hextets. Updated pre_save to tag the hashes it computes with "{SHA1}" as well as making sure not to re-hash passwords. --- classes/fields/password.php | 50 ++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/classes/fields/password.php b/classes/fields/password.php index 738cb7b985..3598ef96b9 100644 --- a/classes/fields/password.php +++ b/classes/fields/password.php @@ -97,19 +97,17 @@ public function options () { * @since 2.0 */ public function schema ( $options = null ) { - if( pods_var( self::$type . '_hash', $options ) ) { - return 'VARCHAR(32)'; - } - else { + if( pods_var( self::$type . '_hash', $options ) ) + $length = 6 + 32; + else $length = (int) pods_var( self::$type . '_max_length', $options, 255 ); - $schema = 'VARCHAR(' . $length . ')'; + $schema = 'VARCHAR(' . $length . ')'; - if ( 255 < $length || $length < 1 ) - $schema = 'LONGTEXT'; + if ( 255 < $length || $length < 1 ) + $schema = 'LONGTEXT'; - return $schema; - } + return $schema; } /** @@ -142,6 +140,24 @@ public function input ( $name, $value = null, $options = null, $pod = null, $id pods_view( PODS_DIR . 'ui/fields/password.php', compact( array_keys( get_defined_vars() ) ) ); } + /** + * Helper function to check if a string is a tagged hash or not + * The format of a tagged hash string is the string "{SHA1}" + * followed by 32 hexets + * + * @param string $value + * + * @return bool + */ + function tagged_hash_string( $value ) { + if ( 6 + 32 == strlen( $value ) ) { + if ("{SHA1}" == pods_mb_substr($value, 0, 6) ) { + return true; + } + } + return false; + } + /** * Validate a value before it's saved * @@ -193,12 +209,18 @@ public function validate ( $value, $name = null, $options = null, $fields = null public function pre_save ( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) { $length = (int) pods_var( self::$type . '_max_length', $options, 255 ); - if ( 0 < $length && $length < pods_mb_strlen( $value ) ) + if ( 0 < $length && $length < pods_mb_strlen( $value ) ) { $value = pods_mb_substr( $value, 0, $length ); - - if ( pods_var( self::$type . '_hash', $options ) ) - return hash_pbkdf2('sha1', $value, pods_var( self::$type . '_salt', $options ), 1, 32); - else + } + + if( pods_var( self::$type . '_hash', $options ) ) { + if ( PodsField_Password::tagged_hash_string( $value ) ) + return $value; + else + return "{SHA1}" . hash_pbkdf2('sha1', $value, pods_var( self::$type . '_salt', $options ), 1, 32, false); + } + else { return $value; + } } }