Оновлення хешування паролів без примушування нового пароля для існуючих користувачів


32

Ви підтримуєте наявну програму зі встановленою базою користувачів. З часом вирішено, що поточна техніка введення пароля застаріла і потребує вдосконалення. Крім того, з причин UX ви не хочете, щоб існуючі користувачі змушували оновлювати свій пароль. Ціле оновлення хешування паролів має відбуватися за екраном.

Припустимо, модель "спрощеної" бази даних для користувачів, яка містить:

  1. Посвідчення особи
  2. Електронна пошта
  3. Пароль

Як можна вирішити таку вимогу?


Мої поточні думки:

  • створити новий метод хешування у відповідному класі
  • оновіть таблицю користувачів у базі даних, щоб містити додаткове поле пароля
  • Після того, як користувач успішно ввійде в систему, використовуючи застарілий хеш пароля, заповніть друге поле пароля оновленим хешем

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

Крім того, це в основному означає, що стару техніку хешування можна змусити залишатись на невизначений термін, поки кожен користувач не оновить свій пароль. Тільки в цей момент я міг почати видаляти старий хеш-чек і видалити зайве поле бази даних.

Я в основному шукаю поради щодо дизайну, оскільки моє поточне «рішення» є брудним, неповним, а що ні, але якщо для опису можливого рішення потрібен фактичний код, сміливо використовуйте будь-яку мову.


4
Чому б вам не вдалося розрізнити набір і невстановлений вторинний хеш? Просто зробіть стовпчик бази даних нульовим і перевіряйте на null.
Кіліан Фот

6
Перейдіть із новим стовпчиком типу хеш, а не полем для нового хеша. Оновлюючи хеш, змініть поле типу. Таким чином, коли це повториться в майбутньому, ви вже зможете розібратися, і немає шансів на те, що ви тримаєтесь на старому (імовірно, менш захищеному) хеші.
Майкл Коне

1
Я думаю, ви на правильному шляху. Через 6 місяців або рік ви можете змусити будь-яких інших користувачів змінити свій пароль. Через рік чи що завгодно можна позбутися старого поля.
GlenPeterson

3
Чому б просто не закинути старий хеш?
Siyuan Ren

тому що ви хочете, щоб пароль був хешований (а не старий хеш), щоб видалити його (тобто порівнювати його з хешованим, наданим користувачем), ви отримуєте ... пароль - не інше значення, яке потім потрібно "видалити" за старим методом. Можливо, але не чистіше.
Майкл Дюрант

Відповіді:


25

Я б запропонував додати нове поле "hash_method", можливо, 1 означатиме старий метод і 2 означає новий метод.

Розумно кажучи, якщо ви дбаєте про подібні речі і ваша програма є відносно довговічною (що, мабуть, уже є), це, мабуть, повториться, оскільки криптовалюта та інформаційна безпека є таким розвиваючим, досить непередбачуваним полем. Був час, коли простий пробіг через MD5 був стандартним, якщо хеширование взагалі використовувалося! Тоді можна подумати, що вони повинні використовувати SHA1, а тепер є засолювання, глобальна сіль + індивідуальна випадкова сіль, SHA3, різні методи генерації крипто-готових випадкових чисел ... це не просто «зупиниться», так що ви можете добре виправити це розширюваним, повторюваним способом.

Отже, скажімо, тепер у вас є щось на кшталт (я сподіваюся, що у простоті псевдо-JavaScript для простоти):

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword());


if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword)
{
    // TODO: Learn what "hash" means
    return clearPassword + "H@$I-I";
}

Тепер зрозумівши, що існує кращий метод, вам просто потрібно надати незначний рефакторинг:

var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword(), user.getHashingMethod());

if (user.getPasswordHash() == tryPassword)
{
    // Authenticated!
}

function hashPassword(clearPassword, hashMethod)
{
    // Note: Hash doesn't mean what we thought it did. Oops...

    var hash;
    if (hashMethod == 1)
    {
        hash = clearPassword + "H@$I-I";
    }
    else if (hashMethod == 2)
    {
        // Totally gonna get it right this time.
        hash = SuperMethodTheNSASaidWasAwesome(clearPassword);
    }
    return hash;
}

Жодним секретним агентам чи програмістам не було завдано шкоди при створенні цієї відповіді.


+1 Це здається досить розумним рефератом, дякую. Хоча я можу в кінцевому підсумку перемістити поля хеш-хешметодів до окремої таблиці, коли я це реалізую.
Віллем

1
Проблема цієї методики полягає в тому, що для облікових записів не входять, ви не можете оновлювати хеші. На мій досвід, більшість користувачів не буде входити протягом багатьох років (якщо ви не видаляєте неактивних користувачів). Ваша абстракція теж не працює, оскільки вона не враховує солі. Стандартна абстракція має дві функції: одну для перевірки та одну для створення.
CodesInChaos

Хешування паролів ледве розвивалося протягом останніх 15 років. Bcrypt був опублікований у 1999 році і досі є рекомендованим хешем. Відтоді відбулося лише одне суттєве поліпшення: послідовно жорсткі хеші пам'яті, що були першопрохідними скріптом.
CodesInChaos

Це залишає старі хеші вразливими (наприклад, якщо хтось викрадає БД). Хешування кожного старого хешу новим більш захищеним методом певною мірою пом'якшує це (вам все одно знадобиться тип хеша, щоб розрізняти newHash(oldHash, salt)абоnewHash(password, salt)
dbkk

42

Якщо ви достатньо піклуєтеся про те, щоб якомога швидше розгорнути нову схему хешування для всіх користувачів (наприклад, тому, що стара дійсно незахищена), насправді існує спосіб миттєвої «міграції» кожного пароля.

Ідея в основному хеш-хеш . Замість того, щоб чекати, коли користувачі нададуть свій існуючий пароль ( p) при наступному вході, ви негайно використовуєте новий алгоритм хешування ( H2) на існуючому хеші, вже створеному за старим алгоритмом ( H1):

hash = H2(hash)  # where hash was previously equal to H1(p) 

Зробивши цю конверсію, ви все ще можете повністю перевірити пароль; вам потрібно просто обчислити H2(H1(p'))замість попереднього, H1(p')коли користувач намагається увійти за допомогою пароля p'.

У теорії, ця техніка може бути застосована до кількох міграцій ( H3, H4і т.д.). На практиці ви хочете позбутися старої хеш-функції, як з точки зору продуктивності, так і з читабельності. На щастя, це досить просто: при наступному успішному вході просто обчисліть новий хеш пароля користувача та замініть на нього наявний хеш-код-хеш:

hash = H2(p)

Вам також знадобиться додатковий стовпець, щоб запам'ятати, який хеш ви зберігаєте: пароль пароля або старий хеш. У випадку нещасного витоку бази даних, цей стовпець не повинен полегшити роботу злому; незалежно від його значення, зловмиснику все одно доведеться змінювати захищений H2алгоритм, а не старий H1.


3
Гігантський +1: H2 (H1 (p)), як правило, настільки ж безпечний, як використання H2 (p) безпосередньо, навіть якщо H1 страшний, тому що (1) сіль і розтягнення опікуються H2, і (2) проблеми з "зламаними" хешами, як MD5, не впливають на хешування паролів. Пов’язано: crypto.stackexchange.com/questions/2945/…
orip

Цікаво, що я вважав би, що хешування старого хешу створить якусь проблему безпеки. Здається, я помилився.
Віллем

4
якщо H1 дійсно жахливий, це не буде безпечним. Бути жахливим у цьому контексті - це здебільшого про втрату великої кількості ентропії від вхідних даних. Навіть MD4 та MD5 майже ідеальні в цьому плані, тому такий підхід є незахищеним лише для деяких хешей або хешей домашньої версії з дуже коротким виходом (нижче 80 біт).
CodesInChaos

1
У цьому випадку, напевно, єдиним варіантом є визнати, що ви сильно накрутили і просто продовжуєте та скидаєте пароль для всіх користувачів. Тут менше про міграцію і більше про контроль за пошкодженнями.
Xion

8

Ваше рішення (додаткова колонка в базі даних) цілком прийнятна. Єдине питання з ним - це те, про яке ви вже згадували: те, що старе хешування все ще використовується для користувачів, які не отримали автентифікацію після зміни.

Щоб уникнути цієї ситуації, ви можете зачекати, коли ваші найактивніші користувачі перейдуть на новий алгоритм хешування, а потім:

  1. Видаліть акаунти користувачів, які занадто довго не здійснювали автентифікацію. Немає нічого поганого у видаленні облікового запису користувача, який ніколи не відвідував веб-сайт протягом трьох років.

  2. Надішліть електронний лист іншим користувачам серед тих, хто деякий час не пройшов автентифікацію, повідомивши, що їх може зацікавити щось нове. Це допоможе зменшити кількість облікових записів, які використовують старіші хешування.

    Будьте уважні: якщо у вас опція без спаму користувачі можуть перевірити на вашому веб-сайті, не надсилайте електронний лист тим користувачам, які перевіряли його.

  3. Нарешті, через кілька тижнів після кроку 2 знищіть пароль для користувачів, які ще використовують стару техніку. Коли і якщо вони спробують перевірити автентифікацію, вони побачать, що їх пароль недійсний; якщо вони все ще зацікавлені у вашій службі, вони можуть її скинути.


1

Напевно, у вас ніколи не буде більше декількох типів хешів, тому ви можете вирішити це, не розширюючи базу даних.

Якщо методи мають різну довжину хешу, і довжина хеша кожного методу є постійною (скажімо, перехід від md5 до hmac-sha1), ви можете вказати метод з довжини хеша. Якщо вони мають однакову довжину, ви можете обчислити хеш спочатку за допомогою нового методу, а потім старого методу, якщо перший тест не вдався. Якщо у вас є поле (не показане), яке повідомляє про те, коли користувач востаннє оновлений / створений, ви можете використовувати це як індикатор, який метод використовувати.


1

Я зробив щось подібне, і є спосіб сказати, чи це новий хеш, і зробити його ще більш безпечним. Я здогадуюсь, що більш безпечно - це гарна річ для вас, якщо ви знайдете час, щоб оновити хеш ;-)

Додайте в свою таблицю БД новий стовпець під назвою "сіль" і добре використовуйте його як довільно створену сіль на кожного користувача. (в значній мірі запобігає атакам веселкового столу)

Таким чином, якщо мій пароль "pass123", а випадкова сіль - 3333, мій новий пароль був би хеш "pass1233333".

Якщо у користувача є сіль, ви знаєте, що це новий хеш. Якщо сіль користувача недійсна, ви знаєте, що це старий хеш.


0

Незалежно від того, наскільки хороша ваша міграційна стратегія, якщо база даних вже була викрадена (і ви не можете негативно довести, що цього ніколи не було), паролі, що зберігаються з незахищеними хеш-функціями, вже можуть бути порушені. Єдине послаблення для цього - вимагати від користувачів змінити свої паролі.

Це не проблема, яку можна вирішити (лише), написавши код. Рішення полягає в інформуванні користувачів та клієнтів про ризики, яким вони були схильні. Після того, як ви будете готові бути відкритими щодо цього, ви можете зробити міграцію, просто скинувши пароль кожного користувача, оскільки це найпростіше і безпечне рішення. Усі альтернативи насправді полягають у тому, щоб приховувати той факт, що ви накрутили.


1
Один компроміс , який я зробив в минулому, щоб зберегти старі паролі в протягом періоду часу, перефразовуючи їх як увійти люди в, але після того, що минув час ви потім викликати скидання пароля на тих , хто не увійти в протягом того часу. Таким чином, у вас є період, коли у вас все ще є менш захищені паролі, але час обмежений, і ви також мінімізуєте збої для користувачів, які регулярно входять у систему.
Шон Бертон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.