Ваше приложение может использовать старый, небезопасный алгоритм хеширования для хранения пароля, такой как MD5 (без использования соли)
Эта статья объясняет как преобразовать уже имеющиеся пароли, зашифрованные уязвимым алгоритмом в пароли зашифрованные с использованием безопасного метода хеширования (например с использованием Bcrypt )
Что бы решить проблему, мы сделаем конвертацию на лету, когда пользователь успешно входит в систему. Будем использовать интерфейс EncoderAwareInterface
login listener и использовать не очень хорошо известные параметры в security.yml
.
Аутентификация до миграции
Если ваше приложение использует зашифрованные при помощи MD5 пароли, файл security.yml
будет выглядеть примерно так что бы аутентификация Symfony работала
# app/config/security.yml security: encoders: AppBundle\Entity\User: algorithm: md5 encode_as_base64: false iterations: 1
В этой статье, мы предполагаем что сущность User
выглядит следующим образом:
# src/AppBundle/Entity/User.php Все пользователи могут выполнять вход, вне зависимости от используемого алгоритма шифрования.
Добавляем «слушатель», который будет выполнять миграцию
Мы присоединим слушатель на событие Symfony security.interactive_login
это событие срабатывает, когда пользователь выполнил успешный вход.
Для начала определим этот слушатель в файле services.yml
file:
Declare first the listener in the services.yml
file:
# app/config/services.yml services: app.login_listener: class: AppBundle\EventListener\LoginListener tags: - { name: kernel.event_listener, event: security.interactive_login } arguments: - "@security.encoder_factory" - "@doctrine.orm.entity_manager"
Создадим слушатель
# src/AppBundle/EventListener/LoginListener.php <?php namespace AppBundle\EventListener; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; class LoginListener { private $encoderFactory; private $om; public function __construct(EncoderFactoryInterface $encoderFactory, ObjectManager $om) { $this->encoderFactory = $encoderFactory; $this->om = $om; } public function onSecurityInteractiveLogin(InteractiveLoginEvent $event) { $user = $event->getAuthenticationToken()->getUser(); $token = $event->getAuthenticationToken(); // Migrate the user to the new hashing algorithm if is using the legacy one if ($user->hasLegacyPassword()) { // Credentials can be retrieved thanks to the false value of // the erase_credentials parameter in security.yml $plainPassword = $token->getCredentials(); $user->setOldPassword(null); $encoder = $this->encoderFactory->getEncoder($user); $user->setPassword( $encoder->encodePassword($plainPassword, $user->getSalt()) ); $this->om->persist($user); $this->om->flush(); } // We don't need any more credentials $token->eraseCredentials(); } }
Этот слушатель обновляет пароль пользователь только в случае если пользователь все еще использует устаревшую систему паролей.
Что бы перекодировать пароль, нам нужен незахешированный пароль, введенный пользователем, который по-умолчанию недоступен в authentication token
предоставленный объектом InteractiveLoginEvent
. Что бы сделать его доступным, сделайте следующие изменения в файле security.yml:
# app/config/security.yml security: erase_credentials: false # ...
Как только ваши пользователи будут входить, данные о них будут обновлены в базе данных, делая их более безопасными со временем. Однажды, основная часть пользователей хотя бы раз выполнит вход и вы сможете удалить колонку old_password
и реализовать фичу «Забыли пароль?» для тех, кто не был мигирован.
Автор: Michaël Perrin, ссылка на оригинальный пост