사용자가 뉴스 기사를 한국어로 요약해 달라고 요청했습니다. 기사는 GitHub OAuth 인증 구현에서 이메일 기반 인증이 Gmail 리네임으로 인해 발생하는 보안 문제에 대한 기술 분석입니다. 핵심 내용을 파악해야 합니다: 1. 200만 개 이상의 저장소를 스캔 2. Gmail 리네임(2026년 3월 31일 예정)으로 인해 OAuth 인증의 취약점 발견 3. 이메일 기반 인증은 사용자가 Gmail을 변경하면 계정 손실 발생 4. 여러 유명 프로젝트들(PostHog, GitLab, LibreChat 등)이 영향 받음 5. 올바른 패턴: sub/googleId를 기본 키로 사용 (cal.com 사례) 6. Passport.js 기반 저장소 73개, NextAuth 17개 등 영향 80자 이상으로 작성해야 하고, 구체적 사실과 수치를 포함해야 합니다. ## 요약 200만 개 이상의 GitHub 저장소를 스캔한 결과, 다수의 Google OAuth 구현체가 이메일 주소를 기본 식별 키로 사용하고 있어 Gmail 리네임(2026년 3월 31일 예정) 시 사용자가 계정을 잃게 되는 치명적 취약점이 발견되었습니다. PostHog(32K 스타), GitLab(24K), LibreChat(35K), MeshCentral(6.4K) 등 주요 프로젝트들이 영향을 받으며, 특히 이메일만 키로 사용하는 CircuitVerse와 MeshCentral은 리네임 시 즉시 계정 접근이 차단됩니다. Passport.js 생태계(73개 저장소)가 가장 큰 노출 영역이며, 유일하게 안전한 것으로 확인된 41K 스타짜리 cal.com은 token.sub를 기본 키로 사용하고 이메일을 표시용으로만 활용하는 올바른 패턴을 적용하고 있습니다. 개발자들에게는 sub/googleId를 기본 식별 키로 전환하고, authdrift Semgrep 규칙을 CI에 추가하여 취약점을 사전 탐지할 것을 권장합니다.
왜 중요한가
본문
How I measured this Two axes. Popularity on one side. Severity on the other. Popularity is GitHub stars. Not a perfect proxy for real-world deployments but the best public signal available. A project with 30K stars has more live instances than one with 300. Severity is how the email is used in the auth handler. Not all "email in OAuth callback" is the same problem. - Critical: Email is the only identity key. No sub or Google ID stored. Rename guarantees a duplicate user. - High: Email is a fallback lookup key. sub is primary but email fires whensub isn't found. Rename plus revoke creates a duplicate through the fallback path. - Medium: Email is stored and displayed but sub is the actual lookup key. Rename causes stale display data. Not identity fracture. - Safe: sub is primary. Email is cosmetic. The cal.com pattern. The full table | Project | Stars | Ecosystem | Severity | Pattern | |---|---|---|---|---| | prompts.chat | 159K | NextAuth | HIGH | user.email in auth handler | | cal.com | 41K | NextAuth | SAFE | token.sub as primary key | | LibreChat | 35K | Passport.js | HIGH | googleId primary, email fallback | | PostHog | 32K | Python/Django | CRITICAL | email + get_or_create | | GitLab | 24K | Ruby/OmniAuth | HIGH | email + find_by in OmniAuth | | Ubicloud | 12K | Ruby/OmniAuth | HIGH | email + find_or_create | | Gumroad | 8.8K | Ruby/OmniAuth | HIGH | email + find_by | | MeshCentral | 6.4K | Passport.js | CRITICAL | Email only. No sub reference. | | angular-fullstack | 6K | Passport.js | HIGH | Email-keyed scaffold (code generator) | | transfer.zip | 1.5K | Passport.js | HIGH | Email as identity key | | CircuitVerse | 1.2K | Ruby/OmniAuth | CRITICAL | User.where(email:) only | | ztnet | 1.1K | NextAuth | HIGH | Email in sign-in flow | Plus 112 more below 1K stars. The full scan covered Passport.js (73 repos), NextAuth (17), Python/Django (15), Ruby/OmniAuth (19). The danger zone Product analytics platform. Enterprise customers self-host it. The auth handler uses email from Google's userinfo endpoint as a key for get_or_create . Rename creates a new user record. Dashboards, feature flags, analytics data tied to the old account become inaccessible. The irony: an analytics tool built to track users that loses track of its own. Open-source ChatGPT alternative. Widely self-hosted by companies. The code does the right thing first: looks up by googleId . But when that misses it falls back to email. After a rename the fallback fires with the new email. Doesn't match. Creates a new user. Chat history orphaned. I manually verified this. The fallback anti-pattern is exactly the cascade Google's own docs warn about. Second most used Git hosting platform. Self-hosted instances use Google OAuth for SSO. Rename fractures a developer's identity. Code reviews, merge requests, CI/CD permissions all tied to the old email. In regulated environments that's an audit trail break. GitLab.com likely has extra reconciliation logic. Self-hosted instances running the open-source version are the real exposure. Remote device management. IT departments and MSPs use it to manage thousands of machines. Email is the only identity key. No sub reference at all. If an IT admin renames their Gmail they lose access to every managed endpoint until someone manually fixes it. Security-critical context. The propagation risk This is a code generator. Every project scaffolded from it inherits the email-keyed OAuth pattern. The 6K stars massively undercount the blast radius. Hundreds of production apps were generated from this template. The vulnerability lives in the scaffold itself. Propagated silently into projects that never appear in the 124 count. Last commit February 2023. Effectively abandoned. The damage is already done. The high-stakes tier Open-source cloud infrastructure. Users provision VMs and manage resources. Identity fracture here means losing access to running cloud infrastructure. Higher stakes than losing chat history. Creator economy payment platform. Open-sourced in 2024. Email-keyed identity means a creator who renames their Gmail loses connection to payment history, customer list, product listings. Money is on the line. Digital circuit simulator used in education. Students sign in with Google. The from_omniauth method looks up users exclusively by email. The uid is stored on creation but never used for lookup. Rename means a student loses access to their coursework and saved circuits. The one that got it right Scheduling platform. 41K stars. Uses token.sub as the primary identity key (line 581 in their auth config). Email is used for rate limiting only. This is the pattern. This is what correct looks like. If every project in this list adopted the cal.com approach the Gmail rename would be a non-event. Ecosystem breakdown | Ecosystem | Email-keyed repos | Biggest projects | |---|---|---| | Passport.js (Google OAuth) | 73 | LibreChat (35K), MeshCentral (6.4K) | | Ruby OmniAuth | 19 | GitLab (24K), Ubicloud (12K), Gumroad (8.8K) | | NextAuth / Auth.js | 17 | prompts.chat (159K) | | Python (Django/Flask) | 15 | PostHog (32K) | Passport.js dominates the count. Almost 60% of all affected repos use it. The library itself isn't the problem. The problem is the tutorials and Stack Overflow answers that taught a generation of developers to use profile.emails[0].value as the identity key. That was correct advice until 31 March 2026. Methodology Sourcegraph public code search. Non-archived, non-forked repositories only. Searched for library-specific patterns in each ecosystem: - Passport.js: profile.emails[0].value in files also referencingpassport-google-oauth20 - NextAuth: user.email +findUnique /findFirst /where in auth-related files - Python: userinfo +email +objects.get /get_or_create - Ruby: omniauth-google-oauth2 +email +find_by /find_or_create /where Manual verification on a sample of results. False positive rate on sampled files: 0%. One notable false negative: LibreChat's email-fallback pattern was not caught by single-file AST rules. Known limitation of the scanning approach. cal.com was identified as a false positive in the initial Sourcegraph query. Manual inspection confirmed it uses token.sub as the primary key. Removed from the vulnerable set. What you should do now - Switch your primary identity key to sub (orgoogleId /profile.id depending on your library). Email becomes a display field. The cal.com pattern. - Add the three Semgrep rules from authdrift to your CI. Catches the email-keying pattern before it ships. - Audit any code generators or scaffolds you use. If you scaffolded from angular-fullstack or similar templates, the vulnerability is baked into your project already. Check the auth handler. The scanner I built for this audit is open-source: authdrift. Three Semgrep rules covering Passport.js, NextAuth, Python/Django. Run it against your own codebase in CI. It catches the patterns in this table. The full essay explaining the Gmail rename cascade is here.