Рубрики
Без рубрики

Понимание временных атак с примерами кода

Уязвимый пример входа в систему Следующий фрагмент кода имеет тонкую проблему безопасности с этим …. Теги за безопасностью, WebDev, начинающими, JavaScript.

Уязвимый пример входа в систему

Следующий кодовый фрагмент имеет тонкую проблему безопасности с ним. Можете ли вы сказать, что не так?

// Returns true if the email/password pair is valid
async function isValidCredentials(emailAddress, password) {
    // Fetch the password hash from the DB by email address
    const passwordHashOrNull = await fetchPasswordHash(emailAddress);

    // If there was no match, return false
    if (!passwordHashOrNull) {
        return false;
    }

    // Bcrypt is "a library to help you hash passwords"
    // Here we use the compare function to check that the
    //   provided password matches the hashed password in the DB
    const doesPasswordMatch = await bcrypt.compare(password, passwordHashOrNull);
    return doesPasswordMatch;
}

// Fetches the password hash from the DB
async function fetchPasswordHash(emailAddress) {
    // impl not important
}

Как подсказка, давайте посмотрим, как долго несколько звонков на IsValidCredentials берет:

async function timeIsValidCredentials(emailAddress, password) {
    console.time("Checking " + emailAddress);
    await isValidCredentials(emailAddress, password);
    console.timeEnd("Checking " + emailAddress);
}

await timeIsValidCredentials("test@test.com", "password");
// Checking test@test.com: 63.813ms
await timeIsValidCredentials("test@test.com", "password2");
// Checking test@test.com: 62.867ms
await timeIsValidCredentials("test2@test.com", "password");
// Checking test2@test.com: 4.017ms
await timeIsValidCredentials("test3@test.com", "password");
// Checking test3@test.com: 4.008ms

Есть заметная разница между тем, как долго test@test.com Пишите электронные письма и test2@test.com или test3@test.com Отказ

Оказывается, проблема в этих строках:

  // If there was no match, return false
if (!passwordHashOrNull) {
    return false;
}

Вернувшись рано, если не было совпадения, злоумышленник может легко сказать, что test@test.com есть аккаунт, но test2@test.com и test3@test.com не делать.

Временные атаки

Это распространенный пример атаки времени. Это класс атак, где промежуток времени, когда ваше приложение требует, чтобы выполнить задачу, утечки некоторой информации.

В случае входа в систему, разница во времена добилась довольно очевидной из даже одного запроса. Если разница была более тонкой, злоумышленник может внести много запросов в течение длительного времени и в среднем их вместе, чтобы различать разные случаи.

Это большое дело?

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

Я могу попробовать кучу вариаций FirstName.lastname@gmail.com или Фамилия {3-значный номер }@gmail.com И так далее, пока я не найду действительного.

Кроме того, есть и другие временные атаки, которые протекают еще более конфиденциальную информацию, которую мы увидимся.

Как мы можем исправить это?

Есть несколько стратегий, но самый простой ответ: «Убедитесь, что все кодепаты занимают столько же времени». Вам не нужно делать это везде, просто в чувствительных частях кодовой базы.

Вместо того, чтобы возвращать рано, мы могли бы проверить пароль против некоторых хэш, а затем вернул false:

// If there was no match, waste time and then return false
if (!passwordHashOrNull) {
    await bcrypt.compare(password, RANDOM_PASSWORD_HASH);
    return false;
}

Также полезно добавить ограничение скорости, когда это возможно. Если злоумышленник нуждается в большом количестве запросов, чтобы различать различные случаи, ограничение скорости их может сделать атаку непрактично.

Временные атаки на практике

Недавно умный Атака ГРМ была найдена в пароле Lobste.rs «Сброс пароля Отказ Это использовало тот факт, что базы данных при сравнении двух строк вернутся рано, если строки не совпадают.

Так проверять

"a".repeat(10000) === "b".repeat(10000)

должен занять меньше времени, чем

"a".repeat(10000) === "a".repeat(9999) + "b"

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

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

Оригинал: “https://dev.to/propelauth/understanding-timing-attacks-with-code-examples-32e6”