// docs / baas security / supabase service role exposure
Supabase service role key-ը բացահայտված JavaScript-ում․ ինչ է դա նշանակում, և ինչպես գտնել
Supabase service role key-ն քո տվյալների բազայի վարպետ բանալին է։ Ցանկացած ոք, ով այն ունի, շրջանցում է Row-Level Security-ն, կարող է կարդալ յուրաքանչյուր table-ի յուրաքանչյուր սյունակ և կարող է գրել կամ ջնջել ինչ ուզի։ Այն նախատեսված է ապրելու բացառապես սերվերային կողմի կոդում — երբեք զննարկիչում։ Երբ AI-ի կոդավորման գործիքն այն առաքում է JavaScript bundle-ին, քո տվյալների բազան ըստ էության հանրային է։ Այս հոդվածը բացատրում է JWT ձևը, որ բացահայտում է արտահոսած բանալին, AI-գործիքի երեք նմուշները, որոնք առաջացնում են արտահոսքը, ինչ անել առաջին ժամին հայտնաբերելուց հետո, և ինչպես ավտոմատ սկանավորել այն, քանի դեռ օգտատերերը չեն արել։
Ինչ է service role key-ը
Supabase-ը տալիս է երկու տարբեր բանալի յուրաքանչյուր նախագծի համար․ anon բանալին (նաև կոչվում է publishable key նոր նախագծերում) և service_role բանալին։ Երկուսն էլ JSON Web Token-ներ են, ստորագրված քո նախագծի JWT secret-ով։ Տարբերությունը JWT payload-ի մեջ ներդրված role claim-ն է — anon հանրային բանալու համար, service_role վարպետ բանալու համար։ PostgREST-ը, Supabase Storage-ը և Supabase Auth-ը բոլորն անցնում են շրջանցել-ամեն-ինչ ռեժիմ, երբ տեսնում են service_role claim-ը։
Ապակոդավորիր ցանկացած Supabase բանալի jwt.io-ում և նայիր payload-ին։ Service-role JWT-ի ձևն անշփոթելի է․
Service-role JWT-ի ապակոդավորված payload-ը (ցույց է տրված ստորև որպես շարահյուսությամբ ընդգծված բլոկ)։
{
"iss": "supabase",
"ref": "[project-ref]",
"role": "service_role",
"iat": 1700000000,
"exp": 2000000000
}Նոր Supabase նախագծերը տալիս են secret-տիպի բանալիներ sb_secret_ նախածանցով՝ JWT-ի փոխարեն։ Վարքը նույնական է — ամեն ինչ, որ կրում է sb_secret_ հանրային bundle-ում, հավասարապես աղետալի է։
Ինչպես են AI-ի կոդավորման գործիքները արտահոսում service role key-ը
Մենք տեսել ենք նույն երեք նմուշները հազարավոր vibe-coded հավելվածներում։ Ամեն մեկը սկսվում է ծրագրավորողի կողմից AI գործիքից օգնություն խնդրելով և ավարտվում service key-ի՝ bundle-ի մեջ inline արվելով։
Նմուշ 1․ Մեկ .env ֆայլ NEXT_PUBLIC_ նախածանցով
Ծրագրավորողը խնդրում է AI գործիքին «կարգավորել Supabase» և ընդունում մեկ .env երկու բանալիներով։ AI գործիքը — մարզված մարմնի վրա, որտեղ շրջակա միջավայրի փոփոխականների մեծ մասը բացահայտվում է NEXT_PUBLIC_*-ի միջոցով — երկուսին էլ նախածանցում է NEXT_PUBLIC_-ով։ Next.js-ը այդ նախածանցին համապատասխանող ամեն ինչ inline է անում client bundle-ի մեջ build-ի ժամանակ։ Թողարկիր Vercel, և service key-ն main.[hash].js-ում է։
Նմուշ 2․ Սխալ բանալի createClient կանչում
Ծրագրավորողը տեղադրում է երկու բանալիները AI-ի գեներացրած config.ts ֆայլի մեջ, և AI-ը զննարկչային createClient() կանչը սխալմամբ լրացնում է process.env.SUPABASE_SERVICE_ROLE_KEY-ով։ Build-ը ներքաշում է փոփոխականը, և JWT-ն հայտնվում է bundle-ում։
Նմուշ 3․ Service-role key-ը կոշտ կոդավորված seed սկրիպտներում
Ծրագրավորողը խնդրում է AI գործիքին գրել սկրիպտ, որ լրացնում է տվյալների բազան։ AI-ը service-role բանալին ուղղակիորեն կոշտ-կոդավորում է ֆայլում (շրջակա միջավայրից կարդալու փոխարեն), commit է անում ֆայլը repository-ին, և հանրային GitHub repo-ն կամ թողարկված հավելվածի /scripts/seed.js route-ն այժմ առաքում է բանալին։
Ինչպես է FixVibe bundle scan-ը հայտնաբերում արտահոսքը
FixVibe-ի bundle-secrets ստուգումը ներբեռնում է թողարկված հավելվածի յուրաքանչյուր JavaScript ֆայլ — entry chunk-ներ, lazy-loaded chunk-ներ, web worker-ներ, service worker-ներ — և անցկացնում է դրանք դետեկտորի միջով, որը ապակոդավորում է JWT ձևին համապատասխանող ամեն ինչ (eyJ[base64-header].eyJ[base64-payload].[signature])։ Եթե ապակոդավորված payload-ը պարունակում է "role": "service_role", սկանը հաշվետու է որպես կրիտիկական գտածո՝ ֆայլի ուղով և ճշգրիտ տողով, որտեղ բանալին հայտնվում է։ Նույն ստուգումը նաև համապատասխանում է ավելի նոր sb_secret_* նմուշին նախածանցով։
Սկանը երբեք չի վավերացվում հայտնաբերված բանալիով։ Այն բացահայտում է ձևը և հաշվետու է արտահոսքի մասին — բանալին օգտագործելը՝ շահագործելիությունն ապացուցելու համար, կլիներ չլիազորված մուտք քո տվյալների բազա։ Ապացույցը հենց JWT payload-ում է։
Հայտնաբերվել է — ինչ անել առաջին ժամին
Արտահոսած service role key-ը runtime արտակարգ իրավիճակ է։ Ենթադրիր, որ բանալին scrape արված է — հարձակվողները իրական ժամանակում հետևում են հանրային bundle-ներին։ Տվյալների բազան վերաբերվիր որպես կոմպրոմիսված, քանի դեռ չես պտտել բանալին և աուդիտ արել վերջին գործունեությունը։
- Անհապաղ պտտիր բանալին։ Supabase Dashboard-ում գնա Project Settings → API → Service role key → Reset։ Հին բանալին անվավեր է դառնում վայրկյանների ընթացքում։ Բանալին օգտագործող ցանկացած սերվերային կոդ պետք է թարմացվի և կրկին թողարկվի, քանի դեռ պտտումը չի տեղի ունեցել։
- Աուդիտ արա վերջին տվյալների բազայի գործունեությունը։ Բացիր Database → Logs dashboard-ում։ Զտիր վերջին 7 օրով։ Փնտրիր անսովոր
SELECT *հարցումներ PII պարունակող table-ների դեմ, մեծUPDATEկամDELETEհայտարարություններ, և հարցումներ քո հայտնի ենթակառուցվածքից դուրս IP-ներից։ Supabase-ը գրառում էx-real-ipheader-ը յուրաքանչյուր հարցման համար։ - Ստուգիր storage object-ները։ Այցելիր Storage → Logs և վերանայիր վերջին ֆայլի ներբեռնումները։ Արտահոսած service-role key-ը տալիս է շրջանցել-ամեն-ինչ մուտք նաև մասնավոր bucket-ներին։
- Հեռացրու բանալին source control-ից։ Նույնիսկ պտտումից հետո JWT-ն քո git պատմության մեջ թողնելը նշանակում է, որ այն հայտնաբերելի է հանրային repo-ում։ Օգտագործիր
git filter-repoկամ BFG Repo-Cleaner՝ պատմությունից մաքրելու համար, ապա force-push (նախ զգուշացրու համագործակիցներին)։ - Կրկին սկանավորիր շտկումից հետո։ Գործարկիր թարմ FixVibe սկան կրկին թողարկված հավելվածի դեմ։ Bundle-secrets գտածոն պետք է մաքրվի։ Հաստատիր, որ ոչ մի
service_roleJWT և ոչ միsb_secret_*տող չի մնում որևէ chunk-ում։
Արտահոսքը կանխելը հենց սկզբից
Կառուցվածքային շտկումը անվանման կարգապահությունն է գումարած գործիք-մակարդակի պաշտպանիչները․
- Երբեք service key-ը մի նախածանցիր
NEXT_PUBLIC_*,VITE_*կամ որևէ այլ bundle-inlining նախածանցով։ Անվանման համաձայնությունը սահման է — յուրաքանչյուր ֆրեյմվորք հարգում է այն։ - Պահիր service key-ը ընդհանրապես
.env-ից դուրս ծրագրավորողի մեքենայի վրա։ Կարդա այն 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 արա.next/static/chunks/output-ըservice_roleտողի համար։ Ձախողիր build-ը, եթե ինչ-որ բան համապատասխանում է։
# 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 բանալիները։
Հանրային-bundle սկաներները րոպեների ընթացքում քայլում են նոր deployment-ներով։ Հետազոտողները փաստագրել են գործող շահագործումներ նոր Supabase նախագծերի դեմ՝ առաջին deploy-ից մեկ ժամից պակաս ժամանակում։ Service-role-ի ցանկացած բացահայտում վերաբերվիր որպես 60-րոպեանոց պատուհան, ոչ թե 60-օրյա։
Բավարա՞ր է բանալին պտտելը, թե՞ պետք է ենթադրեմ տվյալների արտահոսք։
Պտտումն անվավեր է դարձնում արտահոսած բանալին, բայց չի չեղարկում արդեն հանված տվյալները։ Եթե քո table-ները պարունակում են PII, վճարային տվյալներ կամ որևէ կարգավորվող տվյալ, դու կարող ես ունենալ ծանուցման պարտավորություն GDPR-ի (72 ժամ), CCPA-ի կամ HIPAA-ի շրջանակում։ Աուդիտ արա log-ները և խորհրդակցիր իրավաբանի հետ, եթե աուդիտը ցույց է տալիս կասկածելի մուտք։
Կարո՞ղ է RLS-ը պաշտպանել ինձ, եթե service-role բանալին արտահոսում է։
Ոչ։ Row-Level Security-ն ամբողջությամբ շրջանցվում է service_role claim-ով։ Դա նախատեսված է — բանալին գոյություն ունի հենց այն բանի համար, որ backend կոդը կարողանա շրջանցել RLS-ը admin գործողությունների համար։ Մեղմացումն այն է, որ բանալին երբեք չհասնի համատեքստի, որտեղ հարձակվողը կարող է կարդալ այն։
Կիրառվու՞մ է սա Supabase-ի նոր publishable / secret բանալու մոդելի վրա (<code>sb_publishable_</code> / <code>sb_secret_</code>)։
Այո — նույնական ռիսկի դաս։ sb_secret_* բանալին նոր secret-key ֆորմատն է, որ փոխարինում է service-role JWT-ին նոր նախագծերի համար։ Ամեն ինչ, որ կրում է sb_secret_* bundle-ում, նույնքան աղետալի է, որքան արտահոսած service-role JWT-ն։ FixVibe-ի bundle-secrets դետեկտորը համապատասխանում է երկու ձևերին։
Իսկ anon / publishable բանալին — այն անվտանգ է bundle-ում։
Այո, ըստ նախագծման։ Anon բանալին նախատեսված է ապրելու զննարկիչում և այն է, որ յուրաքանչյուր Supabase web client-ն օգտագործում է։ Նրա անվտանգությունը կախված է ամբողջությամբ RLS-ի ճիշտ կարգավորումից յուրաքանչյուր հանրային table-ի վրա։ Տես Supabase RLS սկաներ հոդվածը, թե ինչ ստուգել։
Հաջորդ քայլեր
Գործարկիր FixVibe սկան քո production URL-ի դեմ — bundle-secrets ստուգումն անվճար է, առանց գրանցման, և հաշվետու է service_role բացահայտման մասին մեկ րոպեից պակաս ժամանակում։ Զուգակցիր սա Supabase RLS սկաներ հոդվածի հետ՝ ստուգելու, որ RLS շերտն իր գործն է անում, և Supabase storage bucket-ի անվտանգության ստուգաթերթ-ի հետ՝ ֆայլի մուտքը փակելու համար։ AI գործիքների կողմից այս արտահոսքի դասը այսքան հուսալիորեն գեներացնելու ֆոնի համար կարդա Ինչու AI-ի կոդավորման գործիքները թողնում են անվտանգության բացեր։
