// docs / baas security / supabase service role exposure
Supabase service role key ถูกเปิดเผยใน JavaScript: หมายความว่าอย่างไรและจะค้นหาอย่างไร
Supabase service role key คือ key หลักของฐานข้อมูลคุณ ใครก็ตามที่ถือมันจะข้าม Row-Level Security, สามารถอ่านทุกคอลัมน์ของทุกตาราง และสามารถเขียนหรือลบอะไรก็ได้ที่ต้องการ มันถูกออกแบบให้อยู่เฉพาะในโค้ดฝั่งเซิร์ฟเวอร์เท่านั้น — ไม่เคยอยู่ในเบราว์เซอร์ เมื่อเครื่องมือ AI Coding ส่งมันไปยัง JavaScript bundle, ฐานข้อมูลของคุณก็เป็นสาธารณะอย่างมีประสิทธิภาพ บทความนี้อธิบายรูปร่าง JWT ที่ระบุ key ที่รั่วไหล, รูปแบบสามแบบของเครื่องมือ AI ที่สร้างการรั่วไหลนี้, สิ่งที่ต้องทำในชั่วโมงแรกหลังจากตรวจพบ และวิธีสแกนหามันโดยอัตโนมัติก่อนที่ผู้ใช้จะพบ
service role key คืออะไร
Supabase ออก key สองตัวที่แตกต่างกันสำหรับทุกโปรเจกต์: anon key (เรียกอีกอย่างว่า publishable key ในโปรเจกต์ที่ใหม่กว่า) และ service_role key ทั้งคู่เป็น JSON Web Token ที่เซ็นด้วย JWT secret ของโปรเจกต์ ความแตกต่างคือ role claim ที่ฝังอยู่ใน JWT payload — anon สำหรับ key สาธารณะ, service_role สำหรับ key หลัก PostgREST, Supabase Storage และ Supabase Auth ทั้งหมดสลับเข้าสู่โหมดข้ามทุกอย่างเมื่อพบ claim service_role
ถอดรหัส Supabase key ใดๆ ที่ jwt.io และดู payload รูปร่างของ JWT แบบ service-role นั้นเห็นได้ชัด:
Payload ที่ถอดรหัสแล้วของ service-role JWT (แสดงเป็น block ที่มี syntax highlight ด้านล่าง)
{
"iss": "supabase",
"ref": "[project-ref]",
"role": "service_role",
"iat": 1700000000,
"exp": 2000000000
}โปรเจกต์ Supabase ที่ใหม่กว่าออก key แบบ secret พร้อม prefix sb_secret_ แทน JWT พฤติกรรมเหมือนกัน — อะไรก็ตามที่ถือ sb_secret_ ใน bundle สาธารณะนั้นหายนะเท่าๆ กัน
เครื่องมือ AI Coding รั่วไหล service role key อย่างไร
เราเห็นรูปแบบสามแบบเดียวกันในแอป vibe-coded นับพัน แต่ละแบบเริ่มต้นด้วย developer ขอความช่วยเหลือจากเครื่องมือ AI และจบลงด้วย service key inline เข้าไปใน bundle
รูปแบบที่ 1: ไฟล์ .env เดียวพร้อม prefix NEXT_PUBLIC_
developer ขอเครื่องมือ AI ให้ "ตั้งค่า Supabase" และยอมรับ .env เดียวที่มี key ทั้งสองตัว เครื่องมือ AI — ฝึกบน corpus ที่ environment variable ส่วนใหญ่ถูกเปิดเผยผ่าน NEXT_PUBLIC_* — ใส่ prefix NEXT_PUBLIC_ ทั้งสองตัว Next.js inline ทุกอย่างที่ตรงกับ prefix นี้เข้าไปใน client bundle ตอน build time deploy ไปที่ Vercel และ service key จะอยู่ใน main.[hash].js
รูปแบบที่ 2: key ผิดในการเรียก createClient
developer วาง key ทั้งสองตัวลงในไฟล์ config.ts ที่ AI สร้างขึ้น และ AI เติมการเรียก createClient() ฝั่งเบราว์เซอร์ด้วย process.env.SUPABASE_SERVICE_ROLE_KEY โดยไม่ตั้งใจ build ดึงตัวแปรเข้าไป และ JWT ตกไปใน bundle
รูปแบบที่ 3: service role key hardcode ใน seed script
developer ขอเครื่องมือ AI ให้เขียน script ที่ seed ฐานข้อมูล AI hardcode service role key ลงในไฟล์โดยตรง (แทนที่จะอ่านจาก environment), commit ไฟล์ลงใน repository และ public GitHub repo หรือ route /scripts/seed.js ของแอปที่ deploy แล้วก็ให้บริการ key ออกไป
FixVibe bundle scan ตรวจจับการรั่วไหลอย่างไร
Bundle-secrets check ของ FixVibe ดาวน์โหลดทุกไฟล์ JavaScript ที่แอปที่ deploy แล้วอ้างอิง — entry chunk, lazy-loaded chunk, web worker, service worker — และรันพวกมันผ่านตัวตรวจจับที่ถอดรหัสอะไรก็ตามที่ตรงกับรูปร่าง JWT (eyJ[base64-header].eyJ[base64-payload].[signature]) ถ้า payload ที่ถอดรหัสแล้วมี "role": "service_role", scan จะรายงานเป็น finding ระดับ critical พร้อมเส้นทางไฟล์และบรรทัดที่แน่นอนซึ่ง key ปรากฏ check เดียวกันยังจับคู่ pattern sb_secret_* ที่ใหม่กว่าด้วย prefix
scan ไม่เคย authenticate ด้วย key ที่พบ มันระบุรูปร่างและรายงานการรั่วไหล — การใช้ key เพื่อพิสูจน์ความเป็นไปได้ในการ exploit จะถือว่าเป็นการเข้าถึงโดยไม่ได้รับอนุญาตสู่ฐานข้อมูลของคุณ หลักฐานอยู่ใน JWT payload เอง
ตรวจพบแล้ว — สิ่งที่ต้องทำในชั่วโมงแรก
service role key ที่รั่วไหลคือเหตุฉุกเฉิน runtime สมมติว่า key ถูกขโมยไปแล้ว — ผู้โจมตีติดตาม bundle สาธารณะแบบ real-time ปฏิบัติต่อฐานข้อมูลว่าถูกบุกรุกจนกว่าคุณจะหมุน key และ audit กิจกรรมล่าสุด
- หมุน key ทันที ใน Supabase Dashboard ไปที่ Project Settings → API → Service role key → Reset key เก่าถูก invalidate ภายในไม่กี่วินาที โค้ดฝั่งเซิร์ฟเวอร์ใดๆ ที่ใช้ key นั้นต้องถูก update และ redeploy ก่อนที่การหมุน key จะมีผล
- Audit กิจกรรมฐานข้อมูลล่าสุด เปิด Database → Logs ใน dashboard กรองตาม 7 วันที่ผ่านมา ค้นหา query
SELECT *ที่ผิดปกติกับตารางที่มี PII, statementUPDATEหรือDELETEขนาดใหญ่ และ request จาก IP ที่อยู่นอกโครงสร้างพื้นฐานที่คุณรู้จัก Supabase logx-real-ipheader ในทุก request - ตรวจสอบ storage object เยี่ยมชม Storage → Logs และตรวจสอบการดาวน์โหลดไฟล์ล่าสุด service role key ที่รั่วไหลให้สิทธิ์ข้ามทุกอย่างไปยัง private bucket ด้วย
- ลบ key ออกจาก source control แม้หลังจากการหมุน key, การปล่อย JWT ไว้ใน git history หมายความว่ามันค้นพบได้ใน public repo ใช้
git filter-repoหรือ BFG Repo-Cleaner เพื่อขูดออกจาก history แล้ว force-push (เตือนผู้ร่วมงานก่อน) - สแกนใหม่หลังการแก้ไข รัน FixVibe scan ใหม่กับแอปที่ redeploy แล้ว Bundle-secrets finding ควรหายไป ยืนยันว่าไม่มี
service_roleJWT และไม่มี stringsb_secret_*หลงเหลืออยู่ใน chunk ใดๆ
ป้องกันการรั่วไหลตั้งแต่แรก
การแก้ไขเชิงโครงสร้างคือวินัยในการตั้งชื่อบวกกับ guardrail ระดับเครื่องมือ:
- ห้ามใส่ prefix
NEXT_PUBLIC_*,VITE_*หรือ prefix อื่นใดที่ inline เข้า bundle กับ service key แบบแผนการตั้งชื่อคือขอบเขต — ทุก framework เคารพมัน - เก็บ service key ออกจาก
.envทั้งหมดบนเครื่อง developer อ่านจาก secret manager (Doppler, Infisical, Vercel encrypted env vars) ตอน deploy ไม่เคย commit มันในเครื่อง - <strong>Mark every Supabase client construction with explicit context.</strong> Files named <code>supabase/browser.ts</code> use the anon key; files named <code>supabase/server.ts</code> use the service-role key with <code>import 'server-only'</code> at the top. The <code>server-only</code> import causes a build error if a client component tries to consume the module.
- <strong>Add a pre-commit hook that greps for JWT-shaped strings.</strong> <code>git diff --staged | grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'</code> catches both anon and service tokens before they leave your machine.
- เพิ่ม CI gate ที่สแกน build output หลังจาก
next build, grep output.next/static/chunks/หา stringservice_roleทำให้ build fail หากมีอะไรตรง
# Pre-commit hook: refuse any staged JWT-shaped string.
git diff --staged \
| grep -E 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' \
&& echo "JWT detected in staged changes — refusing commit" \
&& exit 1
# CI gate: fail the build if "service_role" shipped to the static bundle.
grep -RE 'service_role|sb_secret_' .next/static/chunks/ \
&& echo "Service-role credential leaked into bundle" \
&& exit 1คำถามที่พบบ่อย
ผู้โจมตีค้นหา Supabase service role key ที่รั่วไหลได้เร็วแค่ไหน?
สแกนเนอร์ bundle สาธารณะกวาดล้าง deployment ใหม่ภายในไม่กี่นาที นักวิจัยได้บันทึก exploit ที่ใช้งานได้กับโปรเจกต์ Supabase ใหม่ในเวลาไม่ถึงหนึ่งชั่วโมงตั้งแต่ deploy ครั้งแรก ปฏิบัติต่อการเปิดเผย service-role ใดๆ ว่าเป็นช่วงเวลา 60 นาที ไม่ใช่ 60 วัน
การหมุน key เพียงพอหรือไม่ หรือต้องสมมติว่าข้อมูลถูก exfiltrate?
การหมุน key ทำให้ key ที่รั่วไหลใช้ไม่ได้ แต่ไม่สามารถยกเลิกข้อมูลที่ถูกดึงไปแล้วได้ ถ้าตารางของคุณมี PII, ข้อมูลการชำระเงิน หรือข้อมูลที่อยู่ภายใต้กฎระเบียบใดๆ คุณอาจมีภาระต้องแจ้งภายใต้ GDPR (72 ชั่วโมง), CCPA หรือ HIPAA Audit log และปรึกษาที่ปรึกษากฎหมายหาก audit แสดงการเข้าถึงที่น่าสงสัย
RLS จะปกป้องฉันได้ไหมถ้า service role key รั่วไหล?
ไม่ Row-Level Security ถูกข้ามทั้งหมดโดย claim service_role นั่นเป็นไปตามการออกแบบ — key มีอยู่เพื่อให้โค้ด backend ข้าม RLS สำหรับการดำเนินการ admin การบรรเทาคือการทำให้แน่ใจว่า key ไม่เคยเข้าถึง context ที่ผู้โจมตีอ่านได้
นี่ใช้กับโมเดล Supabase publishable / secret key ใหม่ (<code>sb_publishable_</code> / <code>sb_secret_</code>) ด้วยหรือไม่?
ใช่ — class ความเสี่ยงเหมือนกัน sb_secret_* key คือรูปแบบ secret key ใหม่ที่แทนที่ service-role JWT สำหรับโปรเจกต์ที่ใหม่กว่า อะไรก็ตามที่ถือ sb_secret_* ใน bundle ก็หายนะเท่ากับ service-role JWT ที่รั่วไหล ตัวตรวจจับ bundle-secrets ของ FixVibe จับคู่ทั้งสองรูปร่าง
แล้ว anon / publishable key — มันปลอดภัยใน bundle หรือไม่?
ใช่ ตามการออกแบบ anon key ตั้งใจให้อยู่ในเบราว์เซอร์และเป็นสิ่งที่ทุก Supabase web client ใช้ ความปลอดภัยของมันขึ้นอยู่กับ RLS ที่ถูกตั้งค่าอย่างถูกต้องในทุกตารางสาธารณะ ดูบทความ Supabase RLS scanner สำหรับสิ่งที่ต้องตรวจสอบ
ขั้นตอนถัดไป
รัน FixVibe scan กับ URL production ของคุณ — bundle-secrets check ฟรี ไม่ต้องสมัคร และรายงานการเปิดเผย service_role ในเวลาไม่ถึงหนึ่งนาที จับคู่กับบทความ Supabase RLS scanner เพื่อตรวจสอบว่าชั้น RLS ทำงานของมัน และ Supabase storage bucket checklist ความปลอดภัย เพื่อล็อกการเข้าถึงไฟล์ สำหรับเบื้องหลังว่าทำไมเครื่องมือ AI จึงสร้าง class การรั่วไหลนี้อย่างแน่นอน อ่าน ทำไมเครื่องมือ AI Coding ทิ้งช่องโหว่ด้านความปลอดภัย
