JWT-токены: почему в них лежит ваша почта и что с этим делать
Наткнулся на разбор о том, как в JWT-токенах хранят почту, роль и подразделение — и почему это опасно, хотя токен выглядит как шифр. Оказалось, он просто закодирован, а не зашифрован. Это не просто ошибка — это риск для данных. И он часто повторяется у клиентов, даже если сайт работает. Я уже начал проверять это у своих клиентов.
Почему это не просто техническая деталь
Это не про «какой-то баг» в коде. Это про то, что в токенах, которые передаются каждому запросу, лежит конфиденциальная информация — почта, должность, подразделение. И она видна всем, кто знает, как раскодировать base64. Я считаю, что это серьезный риск для бизнеса, потому что такие данные попадают в логи, браузер, прокси — и ни разу не должны там быть. Это не «секрет», а нарушение обработки персональных данных. Даже если не утечка, сам факт хранения лишнего — уже неоправданная уязвимость.
Мне важно, чтобы владелец сайта понимал: это не «проверка уязвимости», а вопрос, как вы защищаете данные своих сотрудников. Если токен — это «секрет», но его можно прочитать открыто — он не работает как секрет. А значит, система не работает.
Как это устроено — и почему кажется безопасным
JWT — это три части, разделённые точками. Первая — заголовок, вторая — полезная нагрузка, третья — подпись. Подпись проверяет целостность, но не конфиденциальность. То есть, кто угодно может прочитать вторую часть, если раскодировать. И это не требует ключа — только base64. Я проверяю это у клиентов, когда есть доступ к токену. Обычно там — email, роль, подразделение. Иногда — даже имя или телефон. Это не нужно в токене. Сервис должен получать это из своей базы по ID.
Важно: подпись не защищает от подделки содержимого. Она только проверяет, что токен не был изменён. Но если кто-то поменяет роль на admin, а подпись оставит старую — сервер её не примет. То есть, подделка блокируется, но только если проверка настроена правильно. А она часто не настроена.
Как понять, что это касается вашего сайта
Я проверяю это так: беру любой токен из системы — например, из браузера или логов. Декодирую среднюю часть. Смотрю, что там лежит. Если вижу email, роль, подразделение — это повод задуматься. Особенно если это не только для себя, а для всех сотрудников. Это не обязательно означает утечку, но означает, что данные не должны быть в токене.
Проверяю ещё три вещи: срок жизни токена (если он больше суток — проблема), есть ли проверка exp, aud, iss, и явно ли указан алгоритм в коде. Если нет — это уязвимость. Я уже вижу, что у многих клиентов это не настроено. И не проверяется.
Что с этим делать — и как я это делаю
Прежде всего — убрать лишнее. В токене остаётся только то, что нужно для аутентификации: ID, срок действия, издатель, аудитория, роль. Всё остальное — из базы. Я начинаю с этого у клиента, если есть доступ. Убираю лишнее, проверяю, где хранится токен — в localStorage или куке. Если в localStorage — риск XSS. Лучше использовать куку с HttpOnly.
Затем — настройка времени жизни. Токен доступа должен быть короткоживущим — минуты, а не дни. Отдельный токен обновления — в куке. И проверка exp, aud, iss — обязательно. Я не доверяю библиотекам, если они не проверяют всё. Проверяю код на наличие явного списка алгоритмов.
Что здесь даёт поддержка
На постоянной поддержке я закрываю такие задачи сразу. Нет сметы, нет переписки. Задача уходит в работу, как только появляется. Это не «попросить» — это «сделать». Мы не ждём, пока у вас случится инцидент. Мы предотвращаем его до того, как вы об этом узнаете.
У нас абонентский договор — первый месяц за наш счёт. На абонентском сроки задач сокращаются примерно на 70%. Это не обещание — это факт. Мы работаем так: предсказуемость вместо авралов. Сайт не падает, вы не думаете о нём, а мы — за вас.
Если хотите проверить, насколько ваш сайт защищён — приглашаю на бесплатный аудит сайта. Проверим, что в токенах, как хранится, и что может быть уязвимо. Без сметы, без давления.