Skip to content
FeaturesPricingAffiliateBlogHelpAboutContact
Get StartedSign In
Back to Blog
industry2026-05-2413 min read

I found live session tokens in R2 backups — customer_sessions BACKUP_SKIP_DATA (PR #560 SS F1)

Femke Amsterdam Oud-West 37-yo 12-yr cloud security consultancy 3-yr AWS security architect + 6-yr independent specialty object-storage misconfig + data-at-rest exposure European SaaS vendors quiet weekly habit forking interesting open-source SaaS repos walking code paths backup crons monitoring beacons RLS policies. Sunday afternoon thMenu backup cron customer_sessions table dumped R2 all row data not just schema no BACKUP_SKIP_DATA filtering. cloudflare/src/cron-jobs/backup.ts dumpDatabaseToR2 iterate D1 tables INSERT per row gzip + R2 28-day retention classic disaster-recovery. customer_sessions schema id PK + session_token (90-day TTL) + user_id FK + expires_at + created_at + last_seen_at + ip_address + user_agent session_token live session bearer token customer profile dashboard authentication bypass. Mental model (1) Customer A login session_token_A 90-day TTL; (2) Sunday-night backup cron customer_sessions dumped R2 INSERT in backup file; (3) 28 days later Customer A logout DB row deleted but still sitting 28-day backup R2; (4) anyone R2 bucket read access leaked CF API token rogue contractor R2 misconfig public read download backup extract session_token_A; (5) 90-day TTL active impersonation direct access profile. Affected tables (a) customer_sessions 90-day session bearer token CRITICAL impersonation; (b) customer_email_links within-TTL magic-link hash typically 24-hour TTL but 28-day backup window expired DB but R2 lower risk exposure; (c) pin_failures PII staff email ip_hash attempt count GDPR-sensitive; (d) pw_failures same shape PII. Femke writeup security@thmenu.com Cloudflare R2 backup retention 28 days D1 dump entire-row format customer_sessions live session tokens 28-day R2 bucket window CVSS 7.5 HIGH data-at-rest disclosure account takeover potential. Engineering 3 wrong theories (1) backup R2 bucket access-controlled Cloudflare API token + IAM-equivalent permissions correct door locked threat model leaked tokens rogue contractors R2 misconfig defense-in-depth door locked live secrets inside fine wrong; (2) session token TTL short 90 days correct insufficient 28-day backup × multiple backups expiration overlap 60-90 days 90-day TTL active pin_failures/pw_failures TTLless PII; (3) just encrypt backup dump PR #585 XX F2 R2 AES-256-GCM at-rest encryption shipped backup content encrypted key + backup attacker live secrets security smell. Correct pattern don't dump data live-secret tables emit only schema restore sessions empty users re-auth counters reset disaster-recovery intent preserved live exposure 0. PR #560 SS F1 minimal impactful fix cloudflare/src/cron-jobs/backup.ts BACKUP_SKIP_DATA allowlist const BACKUP_SKIP_DATA = new Set(['customer_sessions', 'customer_email_links', 'pin_failures', 'pw_failures']) backup loop if BACKUP_SKIP_DATA.has(tableName) emitSchemaOnly continue CREATE TABLE + sentinel SKIPPED data live-secret table. DR test restore sessions empty users re-authenticate natural session invalidation authentication path sessions yok re-login flow handles. Bonus PR #585 XX F2 AES-256-GCM stays active layered defense live-secret skip + at-rest encryption + bucket IAM 3-layer. Production audit 28-day R2 backup files customer_sessions row count 17238 cumulative %23 TTL still active. R2 bucket access log 0 anomalous reads thMenu cron worker + DR test only no exfiltration. Existing 28-day backup files manual one-off purge customer_sessions data decrypt + INSERT strip + re-encrypt. Femke €2200 Wise bounty CVSS 7.5 + Hall of Fame + security advisory board invite LinkedIn 5.3k Nordic + DACH cloud security professionals. Dogan Istanbul Besiktas Etiler 34-yo 8-yr cloud security consultancy parallel disclosure within same week €1800 bounty Turkish-language blog 4.1k. Pattern D1 / RDS backups must not emit DATA tables holding live secrets or live tokens only SCHEMA enough disaster recovery backup retention exposure window live tokens exposed account-takeover. Sibling sweep customer_sessions + customer_email_links + pin_failures + pw_failures + customer_push_subscriptions (PR #654 VIII F2) + oauth_authorization_codes/refresh_tokens (Phase 2 added after PR #560). Implementation enumerate live-secret tables + BACKUP_SKIP_DATA allowlist + schema-only emit + disaster-recovery test + production audit purge old data + PR template checkbox + quarterly grep audit live-secret × BACKUP_SKIP_DATA membership. PR #560 reference.

th

thMenu Team

thmenu.com

Found this helpful? Share it.