{"id":"GHSA-48c2-w96j-mv3m","kind":"vulnerability","aliases":["GHSA-48c2-w96j-mv3m"],"related":[],"title":"@better-auth/kysely-adapter: MySQL serial IDs can return another request's record","description":"## Am I affected?\n\nYou are affected if **all** of these are true:\n\n- Your database is MySQL or MariaDB.\n- You set `advanced.database.generateId: \"serial\"`.\n- You use the default Kysely adapter or the Drizzle adapter with a connection pool.\n\nYou are not affected with other databases or other ID settings.\n\n## Summary\n\nAfter inserting a row, the adapter read the new ID back with a separate query. On a connection pool, that query could run on a different connection and return the ID of a row inserted by another request.\n\n## Impact\n\nUnder ordinary concurrent load, a user who signs in can receive another user's session and act as that user. A sign-up can also return another user's record. Other requests fail with an error. The attacker cannot choose which account they receive.\n\n## Patches\n\nUpgrade `better-auth` and your adapter package (`@better-auth/kysely-adapter` or `@better-auth/drizzle-adapter`) to 1.7.16, or to 1.8.0-beta.5 on the beta line.\n\n## Workarounds\n\nIf you cannot upgrade, stop using `generateId: \"serial\"` on MySQL. A single-connection pool does not prevent this.\n\n## Technical details\n\nThe MySQL branch ran the insert on the pool and then read `LAST_INSERT_ID()` in a separate transaction. `LAST_INSERT_ID()` is scoped to a connection, so the second query could see another request's insert. The adapters now use the ID reported by the insert itself, and fail instead of guessing when no ID is reported.","summary":"On MySQL or MariaDB with generateId set to serial, the adapter read the new row's ID through a separate query that could run on a different pooled connection. A signed-in user can receive another user's session or sign-up record and act as that user.","severity":"medium","cvss":6.8,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"@better-auth/kysely-adapter","fixed":["1.7.16"],"affected":">= 1.5.0-beta.9, < 1.7.16","ecosystem":"npm"},{"name":"@better-auth/kysely-adapter","fixed":["1.8.0-beta.5"],"affected":">= 1.8.0-beta.0, < 1.8.0-beta.5","ecosystem":"npm"},{"name":"@better-auth/drizzle-adapter","fixed":["1.7.16"],"affected":">= 1.5.0-beta.9, < 1.7.16","ecosystem":"npm"},{"name":"@better-auth/drizzle-adapter","fixed":["1.8.0-beta.5"],"affected":">= 1.8.0-beta.0, < 1.8.0-beta.5","ecosystem":"npm"},{"name":"better-auth","fixed":["1.7.16"],"affected":"< 1.7.16","ecosystem":"npm"},{"name":"better-auth","fixed":["1.8.0-beta.5"],"affected":">= 1.8.0-beta.0, < 1.8.0-beta.5","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["better-auth","cwe-362","cwe-488","drizzle","kysely","mariadb","mysql","node.js","package"],"not_affected_if":["default-id-generation","postgresql","sqlite"],"how_to_check":"- Check the installed versions of `better-auth`, `@better-auth/kysely-adapter` and `@better-auth/drizzle-adapter` (for example `npm ls better-auth @better-auth/kysely-adapter @better-auth/drizzle-adapter`).\n- Search your auth configuration for `generateId: \"serial\"` and confirm the database is MySQL or MariaDB.","fix":"- `better-auth` `1.7.16` (or `1.8.0-beta.5` on the beta line)\n- `@better-auth/kysely-adapter` `1.7.16` (or `1.8.0-beta.5` on the beta line)\n- `@better-auth/drizzle-adapter` `1.7.16` (or `1.8.0-beta.5` on the beta line)\n- If you cannot upgrade, stop using `generateId: \"serial\"` on MySQL; a single-connection pool does not prevent this.","status":"open","published_at":"2026-11-03T21:39:08+00:00","first_seen_at":"2026-11-09T22:53:19+00:00","triaged_at":"2026-11-09T22:54:39+00:00","updated_at":"2026-11-09T22:54:39+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:54:39Z","model":"claude-haiku-5-5","output":{"fix":"- `better-auth` `1.7.16` (or `1.8.0-beta.5` on the beta line)\n- `@better-auth/kysely-adapter` `1.7.16` (or `1.8.0-beta.5` on the beta line)\n- `@better-auth/drizzle-adapter` `1.7.16` (or `1.8.0-beta.5` on the beta line)\n- If you cannot upgrade, stop using `generateId: \"serial\"` on MySQL; a single-connection pool does not prevent this.","reach":"internet","title":"better-auth Kysely and Drizzle adapters: MySQL serial IDs can return another user's record","impact":"high","summary":"On MySQL or MariaDB with generateId set to serial, the adapter read the new row's ID through a separate query that could run on a different pooled connection. A signed-in user can receive another user's session or sign-up record and act as that user.","audience":"developers","category":"package","nicknames":[],"who_cares":["better-auth","kysely","drizzle","mysql","mariadb","node.js"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"Signed-in users receive another user's session and act as them.","analyst_note":"","how_to_check":"- Check the installed versions of `better-auth`, `@better-auth/kysely-adapter` and `@better-auth/drizzle-adapter` (for example `npm ls better-auth @better-auth/kysely-adapter @better-auth/drizzle-adapter`).\n- Search your auth configuration for `generateId: \"serial\"` and confirm the database is MySQL or MariaDB.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["postgresql","sqlite","default-id-generation"],"who_is_affected":"Applications that use better-auth with the Kysely or Drizzle adapter, a MySQL or MariaDB database, and `advanced.database.generateId: \"serial\"` are exposed. Deployments on PostgreSQL, SQLite or other ID settings are not affected, and the advisory says a single-connection pool does not prevent the flaw.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.000919,"input_tokens":5296,"output_tokens":778,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-362':50B '-488':52B '/drizzle-adapter':35A,39A,258C '/kysely-adapter':4A,27A,31A,253C '1.7.16':260C '1.8.0':263C '48c2':16A,21A 'account':238C 'act':105B,212C 'adapt':69B,136C,140C,162C,248C,329C 'advanced.database.generateid':129C 'affect':111C,114C,148C 'also':221C 'anoth':10A,95B,193C,207C,223C,324C 'attack':234C 'auth':3A,26A,30A,34A,38A,42A,45A,48B,245C,252C,257C 'back':167C 'beta':267C 'beta.5':264C 'better':2A,25A,29A,33A,37A,41A,44A,47B,244C,251C,256C 'better-auth':1A,24A,28A,32A,36A,40A,43A,46B,243C,250C,255C 'branch':293C 'cannot':235C,272C 'choos':236C 'concurr':198C 'connect':87B,143C,174C,183C,283C,317C 'could':81B,178C,322C 'cwe':49B,51B 'databas':122C,151C 'default':134C 'detail':290C 'differ':85B,182C 'drizzl':53B,139C 'error':232C 'fail':229C,340C 'generateid':64B,276C 'ghsa':15A,20A 'ghsa-48c2-w96j-mv3m':14A,19A 'guess':343C 'id':7A,75B,154C,166C,187C,305C,312C,333C,346C 'impact':195C 'insert':158C,191C,296C,304C,311C,327C,337C 'instead':341C 'kyse':54B,135C 'last':303C,310C 'line':268C 'load':199C 'mariadb':55B,62B,126C 'mv3m':18A,23A 'mysql':5A,56B,60B,124C,279C,292C 'new':72B,165C 'node.js':57B 'ordinari':197C 'packag':58B,249C 'patch':241C 'pool':86B,144C,175C,284C,299C 'prevent':287C 'queri':79B,171C,177C,321C 'ran':294C 'read':70B,163C,302C 'receiv':94B,206C,240C 'record':13A,103B,226C 'report':334C,348C 'request':11A,194C,228C,325C 'return':9A,185C,222C 'row':73B,160C,190C 'run':82B,179C 'scope':314C 'second':320C 'see':323C 'separ':78B,170C,308C 'serial':6A,67B,130C,277C 'session':98B,210C 'set':65B,128C,155C 'sign':90B,101B,203C,218C 'sign-up':100B,217C 'signed-in':89B 'singl':282C 'single-connect':281C 'stop':274C 'summari':156C 'technic':289C 'transact':309C 'true':120C 'upgrad':242C,273C 'use':132C,275C,331C 'user':92B,96B,108B,201C,208C,215C,224C 'w96j':17A,22A 'workaround':269C","nicknames":[],"mitigations":[],"who_affected":"Applications that use better-auth with the Kysely or Drizzle adapter, a MySQL or MariaDB database, and `advanced.database.generateId: \"serial\"` are exposed. Deployments on PostgreSQL, SQLite or other ID settings are not affected, and the advisory says a single-connection pool does not prevent the flaw.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["betterauth","drizzleadapter","kyselyadapter"],"ptokens":["adapter","auth","better","drizzle","kysely"],"labels":["betterauth","cwe362","cwe488","drizzle","kysely","mariadb","medium","mysql","nodejs","npm","package"],"campaign_type":null,"search_a":"'/drizzle-adapter':35A,39A '/kysely-adapter':4A,27A,31A '48c2':16A,21A 'anoth':10A 'auth':3A,26A,30A,34A,38A,42A,45A 'better':2A,25A,29A,33A,37A,41A,44A 'better-auth':1A,24A,28A,32A,36A,40A,43A 'ghsa':15A,20A 'ghsa-48c2-w96j-mv3m':14A,19A 'id':7A 'mv3m':18A,23A 'mysql':5A 'record':13A 'request':11A 'return':9A 'serial':6A 'w96j':17A,22A","search_b":"'-362':50B '-488':52B 'act':105B 'adapt':69B 'anoth':95B 'auth':48B 'better':47B 'better-auth':46B 'connect':87B 'could':81B 'cwe':49B,51B 'differ':85B 'drizzl':53B 'generateid':64B 'id':75B 'kyse':54B 'mariadb':55B,62B 'mysql':56B,60B 'new':72B 'node.js':57B 'packag':58B 'pool':86B 'queri':79B 'read':70B 'receiv':94B 'record':103B 'row':73B 'run':82B 'separ':78B 'serial':67B 'session':98B 'set':65B 'sign':90B,101B 'sign-up':100B 'signed-in':89B 'user':92B,96B,108B","search_c":"'/drizzle-adapter':258C '/kysely-adapter':253C '1.7.16':260C '1.8.0':263C 'account':238C 'act':212C 'adapt':136C,140C,162C,248C,329C 'advanced.database.generateid':129C 'affect':111C,114C,148C 'also':221C 'anoth':193C,207C,223C,324C 'attack':234C 'auth':245C,252C,257C 'back':167C 'beta':267C 'beta.5':264C 'better':244C,251C,256C 'better-auth':243C,250C,255C 'branch':293C 'cannot':235C,272C 'choos':236C 'concurr':198C 'connect':143C,174C,183C,283C,317C 'could':178C,322C 'databas':122C,151C 'default':134C 'detail':290C 'differ':182C 'drizzl':139C 'error':232C 'fail':229C,340C 'generateid':276C 'guess':343C 'id':154C,166C,187C,305C,312C,333C,346C 'impact':195C 'insert':158C,191C,296C,304C,311C,327C,337C 'instead':341C 'kyse':135C 'last':303C,310C 'line':268C 'load':199C 'mariadb':126C 'mysql':124C,279C,292C 'new':165C 'ordinari':197C 'packag':249C 'patch':241C 'pool':144C,175C,284C,299C 'prevent':287C 'queri':171C,177C,321C 'ran':294C 'read':163C,302C 'receiv':206C,240C 'record':226C 'report':334C,348C 'request':194C,228C,325C 'return':185C,222C 'row':160C,190C 'run':179C 'scope':314C 'second':320C 'see':323C 'separ':170C,308C 'serial':130C,277C 'session':210C 'set':128C,155C 'sign':203C,218C 'sign-up':217C 'singl':282C 'single-connect':281C 'stop':274C 'summari':156C 'technic':289C 'transact':309C 'true':120C 'upgrad':242C,273C 'use':132C,275C,331C 'user':201C,208C,215C,224C 'workaround':269C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-03T21:39:08+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-48c2-w96j-mv3m","url":"https://github.com/better-auth/better-auth/security/advisories/GHSA-48c2-w96j-mv3m","severity":"medium","published_at":"2026-11-03T21:39:08+00:00","modified_at":"2026-11-03T22:19:30+00:00","first_seen_at":"2026-11-09T22:53:19+00:00","fetched_at":"2026-11-09T22:53:19+00:00","note":null}],"documents":[{"id":110496,"url":"https://github.com/better-auth/better-auth/security/advisories/GHSA-48c2-w96j-mv3m","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}