Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
/**
* @author Elnur Abdurrakhimov <elnur@elnur.pro>
* @author Terje Bråten <terje@braten.be>
*/
class BCryptPasswordEncoder extends BasePasswordEncoder
{
const MAX_PASSWORD_LENGTH = 72;
/**
* @var string
*/
private $cost;
/**
* Constructor.
*
* @param int $cost The algorithmic cost that should be used
*
* @throws \RuntimeException When no BCrypt encoder is available
* @throws \InvalidArgumentException if cost is out of range
*/
public function __construct($cost)
{
$cost = (int) $cost;
if ($cost < 4 || $cost > 31) {
throw new \InvalidArgumentException('Cost must be in the range of 4-31.');
}
$this->cost = $cost;
}
/**
* Encodes the raw password.
*
* It doesn't work with PHP versions lower than 5.3.7, since
* the password compat library uses CRYPT_BLOWFISH hash type with
* the "$2y$" salt prefix (which is not available in the early PHP versions).
*
* @see https://github.com/ircmaxell/password_compat/issues/10#issuecomment-11203833
*
* It is almost best to **not** pass a salt and let PHP generate one for you.
*
* @param string $raw The password to encode
* @param string $salt The salt
*
* @return string The encoded password
*
* @throws BadCredentialsException when the given password is too long
*
* @see http://lxr.php.net/xref/PHP_5_5/ext/standard/password.c#111
*/
public function encodePassword($raw, $salt)
{
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
$options = array('cost' => $this->cost);
if ($salt) {
// Ignore $salt, the auto-generated one is always the best
}
return password_hash($raw, PASSWORD_BCRYPT, $options);
}
/**
* {@inheritdoc}
*/
public function isPasswordValid($encoded, $raw, $salt)
{
return !$this->isPasswordTooLong($raw) && password_verify($raw, $encoded);
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
/**
* BasePasswordEncoder is the base class for all password encoders.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class BasePasswordEncoder implements PasswordEncoderInterface
{
const MAX_PASSWORD_LENGTH = 4096;
/**
* Demerges a merge password and salt string.
*
* @param string $mergedPasswordSalt The merged password and salt string
*
* @return array An array where the first element is the password and the second the salt
*/
protected function demergePasswordAndSalt($mergedPasswordSalt)
{
if (empty($mergedPasswordSalt)) {
return array('', '');
}
$password = $mergedPasswordSalt;
$salt = '';
$saltBegins = strrpos($mergedPasswordSalt, '{');
if (false !== $saltBegins && $saltBegins + 1 < strlen($mergedPasswordSalt)) {
$salt = substr($mergedPasswordSalt, $saltBegins + 1, -1);
$password = substr($mergedPasswordSalt, 0, $saltBegins);
}
return array($password, $salt);
}
/**
* Merges a password and a salt.
*
* @param string $password the password to be used
* @param string $salt the salt to be used
*
* @return string a merged password and salt
*
* @throws \InvalidArgumentException
*/
protected function mergePasswordAndSalt($password, $salt)
{
if (empty($salt)) {
return $password;
}
if (false !== strrpos($salt, '{') || false !== strrpos($salt, '}')) {
throw new \InvalidArgumentException('Cannot use { or } in salt.');
}
return $password.'{'.$salt.'}';
}
/**
* Compares two passwords.
*
* This method implements a constant-time algorithm to compare passwords to
* avoid (remote) timing attacks.
*
* @param string $password1 The first password
* @param string $password2 The second password
*
* @return bool true if the two passwords are the same, false otherwise
*/
protected function comparePasswords($password1, $password2)
{
return hash_equals($password1, $password2);
}
/**
* Checks if the password is too long.
*
* @param string $password The password to check
*
* @return bool true if the password is too long, false otherwise
*/
protected function isPasswordTooLong($password)
{
return strlen($password) > static::MAX_PASSWORD_LENGTH;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
/**
* @author Christophe Coevoet <stof@notk.org>
*/
interface EncoderAwareInterface
{
/**
* Gets the name of the encoder used to encode the password.
*
* If the method returns null, the standard way to retrieve the encoder
* will be used instead.
*
* @return string
*/
public function getEncoderName();
}

View File

@@ -0,0 +1,123 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
/**
* A generic encoder factory implementation.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class EncoderFactory implements EncoderFactoryInterface
{
private $encoders;
public function __construct(array $encoders)
{
$this->encoders = $encoders;
}
/**
* {@inheritdoc}
*/
public function getEncoder($user)
{
$encoderKey = null;
if ($user instanceof EncoderAwareInterface && (null !== $encoderName = $user->getEncoderName())) {
if (!array_key_exists($encoderName, $this->encoders)) {
throw new \RuntimeException(sprintf('The encoder "%s" was not configured.', $encoderName));
}
$encoderKey = $encoderName;
} else {
foreach ($this->encoders as $class => $encoder) {
if ((is_object($user) && $user instanceof $class) || (!is_object($user) && (is_subclass_of($user, $class) || $user == $class))) {
$encoderKey = $class;
break;
}
}
}
if (null === $encoderKey) {
throw new \RuntimeException(sprintf('No encoder has been configured for account "%s".', is_object($user) ? get_class($user) : $user));
}
if (!$this->encoders[$encoderKey] instanceof PasswordEncoderInterface) {
$this->encoders[$encoderKey] = $this->createEncoder($this->encoders[$encoderKey]);
}
return $this->encoders[$encoderKey];
}
/**
* Creates the actual encoder instance.
*
* @param array $config
*
* @return PasswordEncoderInterface
*
* @throws \InvalidArgumentException
*/
private function createEncoder(array $config)
{
if (isset($config['algorithm'])) {
$config = $this->getEncoderConfigFromAlgorithm($config);
}
if (!isset($config['class'])) {
throw new \InvalidArgumentException(sprintf('"class" must be set in %s.', json_encode($config)));
}
if (!isset($config['arguments'])) {
throw new \InvalidArgumentException(sprintf('"arguments" must be set in %s.', json_encode($config)));
}
$reflection = new \ReflectionClass($config['class']);
return $reflection->newInstanceArgs($config['arguments']);
}
private function getEncoderConfigFromAlgorithm($config)
{
switch ($config['algorithm']) {
case 'plaintext':
return array(
'class' => PlaintextPasswordEncoder::class,
'arguments' => array($config['ignore_case']),
);
case 'pbkdf2':
return array(
'class' => Pbkdf2PasswordEncoder::class,
'arguments' => array(
$config['hash_algorithm'],
$config['encode_as_base64'],
$config['iterations'],
$config['key_length'],
),
);
case 'bcrypt':
return array(
'class' => BCryptPasswordEncoder::class,
'arguments' => array($config['cost']),
);
}
return array(
'class' => MessageDigestPasswordEncoder::class,
'arguments' => array(
$config['algorithm'],
$config['encode_as_base64'],
$config['iterations'],
),
);
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* EncoderFactoryInterface to support different encoders for different accounts.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface EncoderFactoryInterface
{
/**
* Returns the password encoder to use for the given account.
*
* @param UserInterface|string $user A UserInterface instance or a class name
*
* @return PasswordEncoderInterface
*
* @throws \RuntimeException when no password encoder could be found for the user
*/
public function getEncoder($user);
}

View File

@@ -0,0 +1,72 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
/**
* MessageDigestPasswordEncoder uses a message digest algorithm.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class MessageDigestPasswordEncoder extends BasePasswordEncoder
{
private $algorithm;
private $encodeHashAsBase64;
private $iterations;
/**
* Constructor.
*
* @param string $algorithm The digest algorithm to use
* @param bool $encodeHashAsBase64 Whether to base64 encode the password hash
* @param int $iterations The number of iterations to use to stretch the password hash
*/
public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 5000)
{
$this->algorithm = $algorithm;
$this->encodeHashAsBase64 = $encodeHashAsBase64;
$this->iterations = $iterations;
}
/**
* {@inheritdoc}
*/
public function encodePassword($raw, $salt)
{
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
if (!in_array($this->algorithm, hash_algos(), true)) {
throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
}
$salted = $this->mergePasswordAndSalt($raw, $salt);
$digest = hash($this->algorithm, $salted, true);
// "stretch" hash
for ($i = 1; $i < $this->iterations; ++$i) {
$digest = hash($this->algorithm, $digest.$salted, true);
}
return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
}
/**
* {@inheritdoc}
*/
public function isPasswordValid($encoded, $raw, $salt)
{
return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt));
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
/**
* PasswordEncoderInterface is the interface for all encoders.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface PasswordEncoderInterface
{
/**
* Encodes the raw password.
*
* @param string $raw The password to encode
* @param string $salt The salt
*
* @return string The encoded password
*/
public function encodePassword($raw, $salt);
/**
* Checks a raw password against an encoded password.
*
* @param string $encoded An encoded password
* @param string $raw A raw password
* @param string $salt The salt
*
* @return bool true if the password is valid, false otherwise
*/
public function isPasswordValid($encoded, $raw, $salt);
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
/**
* Pbkdf2PasswordEncoder uses the PBKDF2 (Password-Based Key Derivation Function 2).
*
* Providing a high level of Cryptographic security,
* PBKDF2 is recommended by the National Institute of Standards and Technology (NIST).
*
* But also warrants a warning, using PBKDF2 (with a high number of iterations) slows down the process.
* PBKDF2 should be used with caution and care.
*
* @author Sebastiaan Stok <s.stok@rollerscapes.net>
* @author Andrew Johnson
* @author Fabien Potencier <fabien@symfony.com>
*/
class Pbkdf2PasswordEncoder extends BasePasswordEncoder
{
private $algorithm;
private $encodeHashAsBase64;
private $iterations;
private $length;
/**
* Constructor.
*
* @param string $algorithm The digest algorithm to use
* @param bool $encodeHashAsBase64 Whether to base64 encode the password hash
* @param int $iterations The number of iterations to use to stretch the password hash
* @param int $length Length of derived key to create
*/
public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 1000, $length = 40)
{
$this->algorithm = $algorithm;
$this->encodeHashAsBase64 = $encodeHashAsBase64;
$this->iterations = $iterations;
$this->length = $length;
}
/**
* {@inheritdoc}
*
* @throws \LogicException when the algorithm is not supported
*/
public function encodePassword($raw, $salt)
{
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
if (!in_array($this->algorithm, hash_algos(), true)) {
throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
}
$digest = hash_pbkdf2($this->algorithm, $raw, $salt, $this->iterations, $this->length, true);
return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
}
/**
* {@inheritdoc}
*/
public function isPasswordValid($encoded, $raw, $salt)
{
return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt));
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
/**
* PlaintextPasswordEncoder does not do any encoding.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class PlaintextPasswordEncoder extends BasePasswordEncoder
{
private $ignorePasswordCase;
/**
* Constructor.
*
* @param bool $ignorePasswordCase Compare password case-insensitive
*/
public function __construct($ignorePasswordCase = false)
{
$this->ignorePasswordCase = $ignorePasswordCase;
}
/**
* {@inheritdoc}
*/
public function encodePassword($raw, $salt)
{
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
return $this->mergePasswordAndSalt($raw, $salt);
}
/**
* {@inheritdoc}
*/
public function isPasswordValid($encoded, $raw, $salt)
{
if ($this->isPasswordTooLong($raw)) {
return false;
}
$pass2 = $this->mergePasswordAndSalt($raw, $salt);
if (!$this->ignorePasswordCase) {
return $this->comparePasswords($encoded, $pass2);
}
return $this->comparePasswords(strtolower($encoded), strtolower($pass2));
}
}

View File

@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* A generic password encoder.
*
* @author Ariel Ferrandini <arielferrandini@gmail.com>
*/
class UserPasswordEncoder implements UserPasswordEncoderInterface
{
/**
* @var EncoderFactoryInterface
*/
private $encoderFactory;
/**
* @param EncoderFactoryInterface $encoderFactory The encoder factory
*/
public function __construct(EncoderFactoryInterface $encoderFactory)
{
$this->encoderFactory = $encoderFactory;
}
/**
* {@inheritdoc}
*/
public function encodePassword(UserInterface $user, $plainPassword)
{
$encoder = $this->encoderFactory->getEncoder($user);
return $encoder->encodePassword($plainPassword, $user->getSalt());
}
/**
* {@inheritdoc}
*/
public function isPasswordValid(UserInterface $user, $raw)
{
$encoder = $this->encoderFactory->getEncoder($user);
return $encoder->isPasswordValid($user->getPassword(), $raw, $user->getSalt());
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Security\Core\Encoder;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* UserPasswordEncoderInterface is the interface for the password encoder service.
*
* @author Ariel Ferrandini <arielferrandini@gmail.com>
*/
interface UserPasswordEncoderInterface
{
/**
* Encodes the plain password.
*
* @param UserInterface $user The user
* @param string $plainPassword The password to encode
*
* @return string The encoded password
*/
public function encodePassword(UserInterface $user, $plainPassword);
/**
* @param UserInterface $user The user
* @param string $raw A raw password
*
* @return bool true if the password is valid, false otherwise
*/
public function isPasswordValid(UserInterface $user, $raw);
}