Хеширование

Хеширование — это как надежный сейф для паролей. Оно работает в одну сторону: вы превращаете пароль в набор символов, который невозможно превратить обратно в исходный пароль.

Imhotep предлагает три алгоритма хеширования:

Bcrypt — проверенный временем алгоритм, подходит для большинства проектов.

Argon — современный алгоритм, устойчив к атакам через кэш-память.

Argon2id — гибридный вариант, сочетает защиту от двух типов атак.

Конфигурирование

Настройки хеширования находятся в файле config/hash.php. Изучите содержимое файла.

Настройка сложности Bcrypt

Параметр cost (или rounds — поддерживаются оба названия) определяет, сколько времени займет хеширование. Чем выше число, тем безопаснее, но и медленнее.

// Оба варианта работают одинаково
$hash = Hash::make('password', [
    'cost' => 12,
]);

$hash = Hash::make('password', [
    'rounds' => 12,  // Тоже работает!
]);
PHP

Совет: Не ставьте cost слишком высоким без необходимости. Если хеширование одного пароля занимает около 1 секунды, при 1000 регистраций в час ваша база данных может захлебнуться. Начните с 10, тестируйте производительность и постепенно повышайте.

Ограничение длины пароля для Bcrypt

Bcrypt имеет фундаментальное ограничение — он хеширует только первые 72 байта пароля, остальная часть игнорируется. Чтобы избежать этой ситуации, вы можете включить проверку длины:

// В config/hash.php

'bcrypt' => [
    'cost' => 12,
    'limit' => 72, // Максимальная длина пароля
],
PHP

Если пароль превышает указанный лимит, Hash::make(...) выбросит исключение:

use Imhotep\Facades\Hash;
use InvalidArgumentException;

try {
    $hash = Hash::make(str_repeat('a', 100));
} catch (InvalidArgumentException $e) {
    echo "Пароль слишком длинный: " . $e->getMessage();
    // Value is too long to hash. Value must be less than 72 bytes.
}
PHP

Важно: Ограничение в 72 байта — это особенность алгоритма Bcrypt. Проверка длины добавляет дополнительный уровень безопасности, но не обязательна. Вы можете отключить её, установив limit в null.

Настройка сложности Argon2

Argon2 дает больше гибкости — вы управляете тремя параметрами:

$hash = Hash::make('password', [
    'memory' => 2048,  // Сколько памяти (в КБ) использует алгоритм
    'time' => 4,       // Сколько итераций (время)
    'threads' => 2,    // Сколько потоков использовать
]);
PHP
  • memory: Чем больше памяти, тем сложнее взломать хеш с помощью специальных аппаратных ускорителей.
  • time: Чем больше итераций, тем дольше вычисляется хеш (и тем сложнее перебор).
  • threads: Количество потоков — влияет на скорость вычислений.

Хеширование паролей

Самый простой способ захешировать пароль — использовать фасад Hash:

namespace App\Controllers;

use App\Models\User;
use Imhotep\Http\Request;
use Imhotep\Facades\Hash;

class UserController
{
    public function register(Request $request)
    {
        $password = $request->post('password');
        
        // Хешируем пароль — теперь его можно безопасно хранить в базе
        $hashedPassword = Hash::make($password);
        
        // Сохраняем в базу...
        User::create([
            'password' => $hashedPassword
        ]);
        
        return response()->json(['message' => 'Пользователь создан']);
    }
}
PHP

Проверка пароля

Когда пользователь входит в систему, вы не "расшифровываете" хеш — вы проверяете, совпадает ли введенный пароль с сохраненным хешем:

namespace App\Controllers;

use App\Models\User;
use Imhotep\Http\Request;
use Imhotep\Facades\Hash;

class AuthController
{
    public function login(Request $request)
    {
        $email = $request->post('email');
        $password = $request->post('password');
        
        // Ищем пользователя
        $user = User::where('email', $email)->first();
        
        if (!$user) {
            return response()->json(['error' => 'Пользователь не найден'], 404);
        }
        
        // Проверяем пароль
        if (Hash::check($password, $user->password)) {
            // Пароль верный — пускаем пользователя
            return response()->json(['message' => 'Добро пожаловать!']);
        }
        
        return response()->json(['error' => 'Неверный пароль'], 401);
    }
}
PHP

Важно: Hash::check автоматически определяет, каким алгоритмом был создан хеш, и применяет соответствующую проверку. Вам не нужно указывать алгоритм — система сама разберется.

Обновление устаревших хешей

Представьте, вы повысили cost с 10 до 12. Все новые пароли будут хешироваться с cost: 12, но старые остались с cost: 10. Это нормально, но менее безопасно.

Метод needsRehash поможет определить, пора ли обновить хеш:

namespace App\Controllers;

use Imhotep\Facades\Hash;

class AuthController
{
    public function login(Request $request)
    {
        // ... нашли пользователя, проверили пароль ...
        
        if (Hash::check($password, $user->password)) {
            // Проверяем, не устарел ли хеш
            if (Hash::needsRehash($user->password)) {
                // Обновляем хеш на новый (с актуальными настройками)
                $user->password = Hash::make($password);
                User::update($user);
                
                // Пользователь даже не заметит — он просто вошел
            }
            
            // Пускаем пользователя
            return response()->json(['message' => 'Добро пожаловать!']);
        }
    }
}
PHP

Проверка алгоритма хеширования

Imhotep по умолчанию проверяет, что хеш создан с помощью того алгоритма, который указан в вашем приложении. Это дополнительный уровень безопасности.

// Если ваш драйвер — 'bcrypt'
// Но кто-то подсунул хеш, созданный через 'argon'
// Imhotep выбросит исключение

use Imhotep\Facades\Log;
use Imhotep\Facades\Hash;

try {
    $isValid = Hash::check($password, $hashFromDatabase);
} catch (RuntimeException $e) {
    // "This password does not use the bcrypt algorithm."
    // Возможно, атака!
    Log::warning('Подозрительный хеш: алгоритм не соответствует', [
        'hash' => $hashFromDatabase
    ]);
}
PHP

Если вы мигрируете с одного алгоритма на другой (например, с Bcrypt на Argon2id), эта проверка будет мешать. Отключите её в .env:

HASH_VERIFY=false
INI

После завершения миграции не забудьте включить обратно.