Шерлок Холмс: Справа про перевантаження Redis під час DDoS-атаки

pic

Обкладинка

Цей день почався спокійно, поки не сталося лихо. Ми отримали сповіщення про DDoS-атаки та атаки методом підбору паролів з випадкових IP-адрес ботів. Наша команда швидко мобілізувалася, щоб нейтралізувати атаки. І ось коли ми подумали, що ситуація під контролем, з’явилося ще одне тривожне повідомлення: наша база даних Redis була на 80% заповнена! Це стало шоком, адже зазвичай розмір нашої Redis БД не перевищує 20 МБ.

Фаза розслідування: Містика з Redis

pic

Перед тим, як звернути увагу на проблему з Redis, ми зосередилися на припиненні DDoS-атак та атак методом підбору паролів. Ми реалізували обмеження запитів для певних кінцевих точок за допомогою Cloudflare.

Тепер давайте розглянемо, що ми зберігаємо в Redis. Ми використовуємо його для керування сесіями користувачів у нашому Node.js додатку з Passport.js. Ось приклад того, як виглядає сесія:

{  
 "cookie": {  
 "originalMaxAge": number,  
 "expires": "date",  
 "secure": true,  
 "httpOnly": false,  
 "domain": "domain",  
 "path": "/",  
 "sameSite": false  
 },  
 "passport": {   
 "user": // Actual user data here  
 }  
}

Маючи це на увазі, ми почали розслідування. Я запитав усі дані в Redis, щоб перевірити дійсність сесій. На моє здивування, лише 10% сесій містили валідні дані користувачів. Інші були недійсними, без ключа user.

Код, який розкриває секрет: Чому так багато недійсних сесій?

Я заглибився в те, як генеруються ці недійсні сесії, і виявив, що за замовчуванням express-session створює сесію для кожного запиту, який не має приєднаного cookie (детальніше можна дізнатися тут). Під час DDoS-атаки це призводило до того, що для кожного запиту створювалася нова сесія, яка потім зберігалася в Redis.

Щоб виправити це, ми встановили опцію saveUninitialized: false:

const session = require('express-session');
app.use(session({  
 secret: 'keyboard cat',  
 resave: false,  
 saveUninitialized: false,  
 cookie: { secure: true }  
}));

Виправлення проблеми

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

Розслідування триває

Попри початкове виправлення, ми помітили, що база даних Redis все ще швидко зростала. Щось було не так.

Я заглибився в код і виявив, що ми використовуємо пакет flash, який використовується для передачі повідомлень між серверами. При подальшому вивченні я з’ясував, що при доступі до повідомлень:

const { msg } = req.flash();

Пакет призначає порожній об'єкт для req.session.flash, якщо такого не існує:

var msgs = this.session.flash = this.session.flash || {};

Ця зміна req.session спровокувала express-session на збереження сесії в Redis. Функціональність flash використовувалась на публічних кінцевих точках, що призводило до того, що сесії користувачів залишалися порожніми, і з кожним запитом додавалася нова сесія.

Шлях до вирішення

Щоб вирішити проблему, я змінив код, щоб знищувати сесію після того, як повідомлення буде прочитано, як показано нижче:

const { msg } = req.flash();
req.session.destroy();

І так, подорож детектива завершилася.

Нарешті, якщо стаття була корисною, будь ласка, аплодуйте 👏 та підписуйтеся, дякую!

Перекладено з: Sherlock Holmes: The Case Of Redis Overload During a DDoS Attack

Leave a Reply

Your email address will not be published. Required fields are marked *