Skip to content

Security: секреты без fallback — добавить правило в CLAUDE.md (обход авторизации по умолчанию) #5

Description

@merc1305

Разбирал форки стартера и наткнулся на класс дыр, от которого методология сейчас не страхует. Кладу сюда с разбором — фикс уже готов отдельным PR.

Суть

Claude, когда пишет код, по умолчанию любит «удобные» fallback-значения для секретов, чтобы всё сразу запускалось локально:

const secret = process.env.JWT_SECRET ?? 'dev-secret';        // подпись токенов авторизации
const key = Buffer.concat([Buffer.from(process.env.ENCRYPTION_KEY ?? ''), Buffer.alloc(32)]).subarray(0, 32); // пустой ключ → предсказуемые нули
// docker-compose: MYSQL_PASSWORD: ${DB_PASS:-apppass}

Локально удобно. Но если задеплоить, забыв выставить переменную окружения, приложение не падает — оно молча стартует с секретом, который лежит открытым текстом в публичном репозитории. А 'dev-secret' видит любой, кто открыл репо.

Чем это грозит

JWT_SECRET — это чем подписываются токены сессий. Знаешь секрет → подделываешь любой токен и входишь под кем угодно без пароля. Полный обход авторизации. То же с ключом шифрования: предсказуемый ключ = данные, которые ты «зашифровал», расшифровываются кем угодно. Пароль БД по умолчанию — открытая дверь в базу, если порт где-то торчит.

Коварство в том, что всё выглядит рабочим: приложение поднялось, логин проходит, тесты зелёные. Дыра невидима, пока её не поюзают.

Это не гипотетика

Ровно этот footgun воспроизвёлся в одном из форков стартера — реальный человек прошёл онбординг, построил продукт, и в публичный форк уехали и JWT_SECRET ?? 'dev-secret' (web/src/lib/auth.ts), и ключ шифрования с добивкой нулями (web/src/lib/crypto.ts), и дефолтные пароли БД. Человек не виноват — Claude так написал, а правила «так нельзя» в стартере не было.

Аудитория стартера — вайб-кодеры без security-бэкграунда. Они получат это по умолчанию и не заметят.

Фикс

Дешёвый — одно правило в раздел «Безопасность» CLAUDE.md: у секретов не должно быть fallback; нет обязательной env-переменной → падай при старте (throw), а не подставляй заглушку. Тогда «забыл переменную на проде» превращается из тихой дыры в громкую ошибку запуска, которую невозможно не заметить.

Готовый PR: #4

Если формулировка не по тону — перепиши, суть в том, чтобы правило вообще появилось.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions