Throw an exception if password hash would be truncated by DB
authorBrian Wolff <bawolff+wn@gmail.com>
Tue, 15 Nov 2016 06:09:24 +0000 (06:09 +0000)
committerBrian Wolff <bawolff+wn@gmail.com>
Tue, 15 Nov 2016 06:21:03 +0000 (06:21 +0000)
DB uses a tinyblob field. With layered encrypted passwords, the
length gets close to 255 and can exceed if you use a long name
for the password type. Previously these would be silently inserted
into the DB and truncated, which would lock user out of their
account.

Change-Id: Idf0d0248b181f42d92e3ad6c3220b5331cd4d4d0

includes/password/ParameterizedPassword.php
includes/password/Password.php

index 954d403..c929797 100644 (file)
@@ -90,7 +90,9 @@ abstract class ParameterizedPassword extends Password {
                        $str .= $this->getDelimiter();
                }
 
-               return $str . $this->hash;
+               $res = $str . $this->hash;
+               $this->assertIsSafeSize( $res );
+               return $res;
        }
 
        /**
index 4e395b5..13d1e6d 100644 (file)
@@ -81,6 +81,11 @@ abstract class Password {
         */
        protected $config;
 
+       /**
+        * Hash must fit in user_password, which is a tinyblob
+        */
+       const MAX_HASH_SIZE = 255;
+
        /**
         * Construct the Password object using a string hash
         *
@@ -168,9 +173,28 @@ abstract class Password {
         * are considered equivalent.
         *
         * @return string
+        * @throws PasswordError if password cannot be serialized to fit a tinyblob.
         */
        public function toString() {
-               return ':' . $this->config['type'] . ':' . $this->hash;
+               $result = ':' . $this->config['type'] . ':' . $this->hash;
+               $this->assertIsSafeSize( $result );
+               return $result;
+       }
+
+       /**
+        * Assert that hash will fit in a tinyblob field.
+        *
+        * This prevents MW from inserting it into the DB
+        * and having MySQL silently truncating it, locking
+        * the user out of their account.
+        *
+        * @param string $hash The hash in question.
+        * @throws PasswordError If hash does not fit in DB.
+        */
+       final protected function assertIsSafeSize( $hash ) {
+               if ( strlen( $hash ) > self::MAX_HASH_SIZE ) {
+                       throw new PasswordError( "Password hash is too big" );
+               }
        }
 
        /**