Вход Регистрация
Файл: framework/security/PasswordEncryptor.php
Строк: 475
<?php
/**
 * Allows pluggable password encryption.
 * By default, this might be PHP's integrated sha1()
 * function, but could also be more sophisticated to facilitate
 * password migrations from other systems.
 * Use {@link register()} to add new implementations.
 *
 * Used in {@link Security::encrypt_password()}.
 *
 * @package framework
 * @subpackage security
 */
abstract class PasswordEncryptor {

    
/**
     * @var array
     * @config
     */
    
private static $encryptors = array();

    
/**
     * @return Array Map of encryptor code to the used class.
     */
    
public static function get_encryptors() {
        return 
Config::inst()->get('PasswordEncryptor''encryptors');
    }

    
/**
     * @param String $algorithm
     * @return PasswordEncryptor
     * @throws PasswordEncryptor_NotFoundException
     */
    
public static function create_for_algorithm($algorithm) {
        
$encryptors self::get_encryptors();
        if(!isset(
$encryptors[$algorithm])) {
            throw new 
PasswordEncryptor_NotFoundException(
                
sprintf('No implementation found for "%s"'$algorithm)
            );
        }

        
$class=key($encryptors[$algorithm]);
        if(!
class_exists($class)) {
            throw new 
PasswordEncryptor_NotFoundException(
                
sprintf('No class found for "%s"'$class)
            );

        }
        
$refClass = new ReflectionClass($class);
        if(!
$refClass->getConstructor()) {
            return new 
$class;
        }

        
$arguments $encryptors[$algorithm];
        return(
$refClass->newInstanceArgs($arguments));
    }

    
/**
     * Return a string value stored in the {@link Member->Password} property.
     * The password should be hashed with {@link salt()} if applicable.
     *
     * @param String $password Cleartext password to be hashed
     * @param String $salt (Optional)
     * @param Member $member (Optional)
     * @return String Maximum of 512 characters.
     */
    
abstract public function encrypt($password$salt null$member null);

    
/**
     * Return a string value stored in the {@link Member->Salt} property.
     *
     * @uses RandomGenerator
     *
     * @param String $password Cleartext password
     * @param Member $member (Optional)
     * @return String Maximum of 50 characters
     */
    
public function salt($password$member null) {
        
$generator = new RandomGenerator();
        return 
substr($generator->randomToken('sha1'), 050);
    }

    
/**
     * This usually just returns a strict string comparison,
     * but is necessary for retain compatibility with password hashed
     * with flawed algorithms - see {@link PasswordEncryptor_LegacyPHPHash} and
     * {@link PasswordEncryptor_Blowfish}
     */
    
public function check($hash$password$salt null$member null) {
        return 
$hash === $this->encrypt($password$salt$member);
    }
}

/**
 * Blowfish encryption - this is the default from SilverStripe 3.
 * PHP 5.3+ will provide a php implementation if there is no system
 * version available.
 *
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_Blowfish extends PasswordEncryptor {
    
/**
     * Cost of encryption.
     * Higher costs will increase security, but also increase server load.
     * If you are using basic auth, you may need to decrease this as encryption
     * will be run on every request.
     * The two digit cost parameter is the base-2 logarithm of the iteration
     * count for the underlying Blowfish-based hashing algorithmeter and must
     * be in range 04-31, values outside this range will cause crypt() to fail.
     */
    
protected static $cost 10;

    
/**
     * Sets the cost of the blowfish algorithm.
     * See {@link PasswordEncryptor_Blowfish::$cost}
     * Cost is set as an integer but
     * Ensure that set values are from 4-31
     *
     * @param int $cost range 4-31
     * @return null
     */
    
public static function set_cost($cost) {
        
self::$cost max(min(31$cost), 4);
    }

    
/**
     * Gets the cost that is set for the blowfish algorithm
     *
     * @param int $cost
     * @return null
     */
    
public static function get_cost() {
        return 
self::$cost;
    }

    public function 
encrypt($password$salt null$member null) {
        
// See: http://nz.php.net/security/crypt_blowfish.php
        // There are three version of the algorithm - y, a and x, in order
        // of decreasing security. Attempt to use the strongest version.
        
$encryptedPassword $this->encryptY($password$salt);
        if(!
$encryptedPassword) {
            
$encryptedPassword $this->encryptA($password$salt);
        }
        if(!
$encryptedPassword) {
            
$encryptedPassword $this->encryptX($password$salt);
        }

        
// We *never* want to generate blank passwords. If something
        // goes wrong, throw an exception.
        
if(strpos($encryptedPassword'$2') === false) {
            throw new 
PasswordEncryptor_EncryptionFailed('Blowfish password encryption failed.');
        }

        return 
$encryptedPassword;
    }

    public function 
encryptX($password$salt) {
        
$methodAndSalt '$2x$' $salt;
        
$encryptedPassword crypt($password$methodAndSalt);

        if(
strpos($encryptedPassword'$2x$') === 0) {
            return 
$encryptedPassword;
        }

        
// Check if system a is actually x, and if available, use that.
        
if($this->checkAEncryptionLevel() == 'x') {
            
$methodAndSalt '$2a$' $salt;
            
$encryptedPassword crypt($password$methodAndSalt);

            if(
strpos($encryptedPassword'$2a$') === 0) {
                
$encryptedPassword '$2x$' substr($encryptedPasswordstrlen('$2a$'));
                return 
$encryptedPassword;
            }
        }

        return 
false;
    }

    public function 
encryptY($password$salt) {
        
$methodAndSalt '$2y$' $salt;
        
$encryptedPassword crypt($password$methodAndSalt);

        if(
strpos($encryptedPassword'$2y$') === 0) {
            return 
$encryptedPassword;
        }

        
// Check if system a is actually y, and if available, use that.
        
if($this->checkAEncryptionLevel() == 'y') {
            
$methodAndSalt '$2a$' $salt;
            
$encryptedPassword crypt($password$methodAndSalt);

            if(
strpos($encryptedPassword'$2a$') === 0) {
                
$encryptedPassword '$2y$' substr($encryptedPasswordstrlen('$2a$'));
                return 
$encryptedPassword;
            }
        }

        return 
false;
    }

    public function 
encryptA($password$salt) {
        if(
$this->checkAEncryptionLevel() == 'a') {
            
$methodAndSalt '$2a$' $salt;
            
$encryptedPassword crypt($password$methodAndSalt);

            if(
strpos($encryptedPassword'$2a$') === 0) {
                return 
$encryptedPassword;
            }
        }

        return 
false;
    }

    
/**
     * The algorithm returned by using '$2a$' is not consistent -
     * it might be either the correct (y), incorrect (x) or mostly-correct (a)
     * version, depending on the version of PHP and the operating system,
     * so we need to test it.
     */
    
public function checkAEncryptionLevel() {
        
// Test hashes taken from
        // http://cvsweb.openwall.com/cgi/cvsweb.cgi/~checkout~/Owl/packages/glibc
        //    /crypt_blowfish/wrapper.c?rev=1.9.2.1;content-type=text%2Fplain
        
$xOrY crypt("xffxa334xffxffxffxa3345"'$2a$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi')
            == 
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.o./n25XVfn6oAPaUvHe.Csk4zRfsYPi';
        
$yOrA crypt("xa3"'$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq')
            == 
'$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq';

        if(
$xOrY && $yOrA) {
            return 
'y';
        } elseif(
$xOrY) {
            return 
'x';
        } elseif(
$yOrA) {
            return 
'a';
        }

        return 
'unknown';
    }

    
/**
     * self::$cost param is forced to be two digits with leading zeroes for ints 4-9
     */
    
public function salt($password$member null) {
        
$generator = new RandomGenerator();
        return 
sprintf('%02d'self::$cost) . '$' substr($generator->randomToken('sha1'), 022);
    }

    public function 
check($hash$password$salt null$member null) {
        if(
strpos($hash'$2y$') === 0) {
            return 
$hash === $this->encryptY($password$salt);
        } elseif(
strpos($hash'$2a$') === 0) {
            return 
$hash === $this->encryptA($password$salt);
        } elseif(
strpos($hash'$2x$') === 0) {
            return 
$hash === $this->encryptX($password$salt);
        }

        return 
false;
    }
}

/**
 * Encryption using built-in hash types in PHP.
 * Please note that the implemented algorithms depend on the PHP
 * distribution and architecture.
 *
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_PHPHash extends PasswordEncryptor {

    protected 
$algorithm 'sha1';

    
/**
     * @param String $algorithm A PHP built-in hashing algorithm as defined by hash_algos()
     */
    
public function __construct($algorithm) {
        if(!
in_array($algorithmhash_algos())) {
            throw new 
Exception(
                
sprintf('Hash algorithm "%s" not found in hash_algos()'$algorithm)
            );
        }

        
$this->algorithm $algorithm;
    }

    
/**
     * @return string
     */
    
public function getAlgorithm() {
        return 
$this->algorithm;
    }

    public function 
encrypt($password$salt null$member null) {
        return 
hash($this->algorithm$password $salt);
    }
}

/**
 * Legacy implementation for SilverStripe 2.1 - 2.3,
 * which had a design flaw in password hashing that caused
 * the hashes to differ between architectures due to
 * floating point precision problems in base_convert().
 * See http://open.silverstripe.org/ticket/3004
 *
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_LegacyPHPHash extends PasswordEncryptor_PHPHash {
    public function 
encrypt($password$salt null$member null) {
        
$password parent::encrypt($password$salt$member);

        
// Legacy fix: This shortening logic is producing unpredictable results.
        //
        // Convert the base of the hexadecimal password to 36 to make it shorter
        // In that way we can store also a SHA256 encrypted password in just 64
        // letters.
        
return substr(base_convert($password1636), 064);
    }

    public function 
check($hash$password$salt null$member null) {
        
// Due to flawed base_convert() floating poing precision,
        // only the first 10 characters are consistently useful for comparisons.
        
return (substr($hash010) === substr($this->encrypt($password$salt$member), 010));
    }
}

/**
 * Uses MySQL's PASSWORD encryption. Requires an active DB connection.
 *
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_MySQLPassword extends PasswordEncryptor {
    public function 
encrypt($password$salt null$member null) {
        return 
DB::prepared_query("SELECT PASSWORD(?)", array($password))->value();
    }

    public function 
salt($password$member null) {
        return 
false;
    }
}

/**
 * Uses MySQL's OLD_PASSWORD encyrption. Requires an active DB connection.
 *
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_MySQLOldPassword extends PasswordEncryptor {
    public function 
encrypt($password$salt null$member null) {
        return 
DB::prepared_query("SELECT OLD_PASSWORD(?)", array($password))->value();
    }

    public function 
salt($password$member null) {
        return 
false;
    }
}

/**
 * Cleartext passwords (used in SilverStripe 2.1).
 * Also used when Security::$encryptPasswords is set to FALSE.
 * Not recommended.
 *
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_None extends PasswordEncryptor {
    public function 
encrypt($password$salt null$member null) {
        return 
$password;
    }

    public function 
salt($password$member null) {
        return 
false;
    }
}

/**
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_NotFoundException extends Exception {}

/**
 * @package framework
 * @subpackage security
 */
class PasswordEncryptor_EncryptionFailed extends Exception {}
Онлайн: 0
Реклама