Я хочу добавить флажок "запомнить меня" перед входом в систему.
Каков наилучший способ безопасного хранения файла cookie в браузере пользователя?
Например, Facebook имеет свой флажок "помнить меня", чтобы каждый раз, когда вы вводили facebook.com, вы уже вошли в систему.
Мой текущий логин использует простые сеансы.
Ответ 2
Обновление (2017-08-13). Чтобы понять, почему мы разделяем selector
и token
, вместо того, чтобы просто использовать token
, прочитайте в этой статье о разделении токенов для предотвращения атак синхронизации в SELECT-запросах.
Я собираюсь извлечь стратегию, изложенную в этом сообщении в блоге об обеспечении долгосрочной долгосрочной аутентификации, поскольку это охватывает много нас интересует только " запомнить меня.
Преамбула - Структура базы данных
Нам нужна отдельная таблица из таблицы наших пользователей, которая выглядит так (MySQL):
CREATE TABLE `auth_tokens` (
`id` integer(11) not null UNSIGNED AUTO_INCREMENT,
`selector` char(12),
`token` char(64),
`userid` integer(11) not null UNSIGNED,
`expires` datetime,
PRIMARY KEY (`id`)
);
Важно то, что selector
и token
являются отдельными полями.
После входа в систему
Если у вас нет random_bytes()
, просто возьмите копию random_compat.
if ($login->success && $login->rememberMe) { // However you implement it
$selector = base64_encode(random_bytes(9));
$authenticator = random_bytes(33);
setcookie(
'remember',
$selector.':'.base64_encode($authenticator),
time() + 864000,
'/',
'yourdomain.com',
true, // TLS-only
true // http-only
);
$database->exec(
"INSERT INTO auth_tokens (selector, token, userid, expires) VALUES (?, ?, ?, ?)",
[
$selector,
hash('sha256', $authenticator),
$login->userId,
date('Y-m-d\TH:i:s', time() + 864000)
]
);
}
Повторная аутентификация при загрузке страницы
if (empty($_SESSION['userid']) && !empty($_COOKIE['remember'])) {
list($selector, $authenticator) = explode(':', $_COOKIE['remember']);
$row = $database->selectRow(
"SELECT * FROM auth_tokens WHERE selector = ?",
[
$selector
]
);
if (hash_equals($row['token'], hash('sha256', base64_decode($authenticator)))) {
$_SESSION['userid'] = $row['userid'];
// Then regenerate login token as above
}
}
Подробнее
Мы используем 9 байтов случайных данных (base64, закодированных до 12 символов) для нашего селектора. Это обеспечивает 72 бита пространства ключей и, следовательно, 2 36 бит сопротивления столкновению (атаки на день рождения), который больше, чем наша емкость (integer(11) UNSIGNED
), в 16 раз.
Мы используем 33 байта (264 бит) случайности для нашего фактического аутентификатора. Это должно быть непредсказуемым во всех практических сценариях.
Мы храним SHA256 хэш аутентификатора в базе данных. Это уменьшает риск олицетворения пользователя после утечки информации.
Мы пересчитываем хэш SHA256 значения аутентификатора, хранящегося в cookie пользователя, затем сравниваем его с сохраненным хэшем SHA256, используя hash_equals()
для предотвращения временных атак.
Мы отделили селектор от аутентификатора, потому что поиск БД не является постоянным. Это устраняет потенциальное влияние временных утечек на поиск, не вызывая резкого падения производительности.