Comments 24
blog.risingstack.com/node-js-interview-questions-and-answers-2017
What's wrong with the following code snippet?
function checkApiKey (apiKeyFromDb, apiKeyReceived) {
if (apiKeyFromDb === apiKeyReceived) {
return true
}
return false
}
The Solution
When you compare security credentials it is crucial that you don't leak any information, so you have to make sure that you compare them in fixed time. If you fail to do so, your application will be vulnerable to timing attacks.
https://jsperf.com/string-comparison-time
Встроенное сравнение строк занимает меньше наносекунды. Даже если допустить, что движок яваскриптовый увидел, что строки константные и заоптимизировал все в ноопы, наивная реализация сравнения через цикл с charAt
занимает меньше микросекунды для строки в 60 символов. Что тут можно через сеть намерять?..
Проблема разобьётся о задержки на разных уровнях, которые как мы знаем не константа. Как правильно заметили, нет смысла сравнивать cleartext пароли. Что-то более длинное не даст вам точного понимания, где именно в строке ошибка, в 18 или 24 символе? Вектор есть, но уж очень лабораторные условия должны быть для его реализации.
Исследования показывают, что злоумышленнику не составляет труда измерять отрезки времени от 15 до 100 микросекунд через Интернет и 100 наносекунд через локальную сеть
А можно ссылочку на эти исследования?
Что-то мне кажется, здесь минимум на 3 порядка улучшили точность, а речь на самом деле идёт про 15-100 миллисекунд (мс) и 100 микросекунд (0,1 мс)
Ну, если у нас проблема в сравнении строк как концепции — значит этой уязвимости должны быть подвержены все языки, рассудил я. Набросал на коленке PHP-скриптик, запустил на домашнем сервачке (гента на целерончике N3150). Запускал из консоли, PHP 7.0.21 (cli).
Результаты, 100 млн (108) сравнений:
Diff at 5th symbol
42.0196 seconds.
Diff at 15th symbol
41.4606 seconds.
То есть разница есть, целых 0,000 000 005 59 секунды (5,6 наносекунды).
Таким образом, даже если злоумышленник сможет замерять отрезки времени с точностью до 100 наносекунд… это ему ничем не поможет.
Резюмируя: делаем из мухи слона?
P.S. Для ноды я не стал делать тест, но, сомневаюсь, что разница больше чем на 3 порядка.
P.P.S. Почему на PHP первый тест занял больше времени чем второй — тоже весьма интересный вопрос, но в данном случае — оффтопик.
P.P.P.S. Ну и конечно же, как выше и ниже заметили — хэши.
(Простите что пример на C++, он мне ближе чем JS)
bool isAuthenticated(const char* token, const char* input, int len) {
int mismatch = 0;
for (int i = 0; i < len; ++i) {
mismatch |= token[i] ^ input[i];
}
return mismatch;
}
Я бы добавил еще один способ защиты. Самый простой и лобовой.
Ставить в конец запроса случайную задержку, например от 1 до 3 мс.
Отличный пример, как взломать приложение, в котором пароли сравниваются прямо в ноде, и к которому имеет доступ только один пользователь. Больше, больше сферических коней в вакууме! И каждый раз помечать их как проблемы в node.js.
Ага, и на любом бэкенде, где серверов больше одного, весь вектор атаки сломается об раунд-робин DNS'а или nginx'а.
Как мы видим – измерение покажет нам, что при установке val0=4 – система ответит нам с небольшой задержкой.Ага, только мы сами ее создали, да еще такую чтоб заметно было.
if (p[i] == correct[i]) { sleepTime += 0.45; } ... sleep(sleepTime * 1000);
Мне кажется, практическое применение такого способа может быть только если есть прямой доступ к проверяющей функции, чтобы измерять только ее работу.
function isAuthenticated(user, token) {
var userToken = getUserToken(user);
return notTimingCheckStrings(userToken, token);
}
function notTimingCheckStrings(str1, str2) {
if(str1.length === str2.length) {
var result = true;
for(var i=0; i<str2.lenght; i++) {
if( str1[i] !== str2[i] ) {
result = false;
}
}
return result;
} else {
//Handle error
return false;
}
}
Весьма интересная статья, спасибо!
Но почему бы было не написать пример с определением mismatch в цикле так, чтобы он исполнялся после копипаста?
Тайминговая атака на Node.js — когда время работает против вас