diff --git a/src/pentesting-ci-cd/supabase-security.md b/src/pentesting-ci-cd/supabase-security.md index b39cbf1dc..8d9f9c552 100644 --- a/src/pentesting-ci-cd/supabase-security.md +++ b/src/pentesting-ci-cd/supabase-security.md @@ -1,49 +1,48 @@ -# Supabase Güvenliği +# Supabase Güvenlik {{#include ../banners/hacktricks-training.md}} ## Temel Bilgiler -[**Ana sayfalarına**](https://supabase.com/) göre: Supabase, açık kaynaklı bir Firebase alternatifidir. Projenize bir Postgres veritabanı, Kimlik Doğrulama, anlık API'ler, Edge Fonksiyonları, Gerçek Zamanlı abonelikler, Depolama ve Vektör gömme ile başlayın. +As per their [**landing page**](https://supabase.com/): Supabase is an open source Firebase alternative. Start your project with a Postgres database, Authentication, instant APIs, Edge Functions, Realtime subscriptions, Storage, and Vector embeddings. -### Alt Alan Adı +### Alt alan adı -Temelde bir proje oluşturulduğunda, kullanıcı **`jnanozjdybtpqgcwhdiz.supabase.co`** gibi bir supabase.co alt alan adı alacaktır. +Genel olarak bir proje oluşturulduğunda kullanıcı şu formatta bir supabase.co alt alan adı alır: **`jnanozjdybtpqgcwhdiz.supabase.co`** ## **Veritabanı yapılandırması** > [!TIP] -> **Bu verilere `https://supabase.com/dashboard/project//settings/database` gibi bir bağlantıdan erişilebilir.** +> **Bu verilere şu tarz bir linkten erişilebilir: `https://supabase.com/dashboard/project//settings/database`** -Bu **veritabanı**, bazı AWS bölgelerinde dağıtılacak ve ona bağlanmak için şu şekilde bağlanmak mümkün olacaktır: `postgres://postgres.jnanozjdybtpqgcwhdiz:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres` (bu us-west-1'de oluşturuldu).\ -Şifre, kullanıcının daha önce koyduğu bir **şifredir**. +Bu **veritabanı** belirli bir AWS bölgesinde dağıtılacaktır ve bağlanmak için şu bağlantı kullanılabilir: `postgres://postgres.jnanozjdybtpqgcwhdiz:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres` (bu örnek us-west-1'de oluşturuldu). Parola, kullanıcının önceden belirlediği **parola**dır. -Bu nedenle, alt alan adı bilinen bir alan adı olduğundan ve kullanıcı adı olarak kullanıldığından ve AWS bölgeleri sınırlı olduğundan, **şifreyi brute force denemek** mümkün olabilir. +Bu nedenle, alt alan adı biliniyor, kullanıcı adı olarak kullanılıyor ve AWS bölgeleri sınırlı olduğundan, **brute force the password** denenebilir. -Bu bölüm ayrıca şunları içermektedir: +Bu bölüm ayrıca şu seçenekleri içerir: -- Veritabanı şifresini sıfırlama -- Bağlantı havuzlamasını yapılandırma -- SSL yapılandırma: Düz metin bağlantılarını reddetme (varsayılan olarak etkin) +- Veritabanı parolasını sıfırlama +- Connection pooling'i yapılandırma +- SSL'i yapılandırma: plan-text bağlantılarını reddet (varsayılan olarak etkinler) - Disk boyutunu yapılandırma -- Ağ kısıtlamaları ve yasaklar uygulama +- Ağ kısıtlamaları ve yasakları uygulama ## API Yapılandırması > [!TIP] -> **Bu verilere `https://supabase.com/dashboard/project//settings/api` gibi bir bağlantıdan erişilebilir.** +> **Bu verilere şu tarz bir linkten erişilebilir: `https://supabase.com/dashboard/project//settings/api`** -Projenizdeki supabase API'sine erişim URL'si şöyle olacak: `https://jnanozjdybtpqgcwhdiz.supabase.co`. +Projenizdeki supabase API'sine erişim URL'si şu şekilde olacaktır: `https://jnanozjdybtpqgcwhdiz.supabase.co`. -### anon api anahtarları +### anon api keys -Ayrıca bir **anon API anahtarı** (`role: "anon"`) oluşturacaktır, örneğin: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk` uygulamanın API anahtarını kullanabilmesi için gereklidir. +Ayrıca `role: "anon"` olan bir **anon API key** üretilir, örneğin: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk` — uygulamanın API ile iletişim kurmak için kullanması gerekir. -Bu API ile iletişim kurmak için API REST'ini [**belgelerde**](https://supabase.com/docs/reference/self-hosting-auth/returns-the-configuration-settings-for-the-gotrue-server) bulmak mümkündür, ancak en ilginç uç noktalar şunlar olacaktır: +It's possible to find the API REST to contact this API in the [**docs**](https://supabase.com/docs/reference/self-hosting-auth/returns-the-configuration-settings-for-the-gotrue-server), but the most interesting endpoints would be:
-Kaydol (/auth/v1/signup) +Kayıt (/auth/v1/signup) ``` POST /auth/v1/signup HTTP/2 Host: id.io.net @@ -99,61 +98,170 @@ Priority: u=1, i ```
-Bu nedenle, bir müşterinin kendilerine verilen alt alan adıyla supabase kullandığını keşfettiğinizde (şirketin bir alt alan adının supabase alt alan adlarına CNAME yönlendirmesi yapması mümkündür), **supabase API'sini kullanarak platformda yeni bir hesap oluşturmayı** deneyebilirsiniz. +Yani, bir müşterinin kendisine verilen alt alan adını kullanan bir supabase istemcisi keşfettiğinizde (şirketin bir alt alanının supabase alt alanlarına CNAME koymuş olması mümkün), **supabase API**'yi kullanarak platformda yeni bir hesap oluşturmayı deneyebilirsiniz. -### gizli / hizmet_rolü api anahtarları +### secret / service_role api keys -**`role: "service_role"`** ile bir gizli API anahtarı da oluşturulacaktır. Bu API anahtarı gizli olmalıdır çünkü **Satır Seviyesi Güvenliği**'ni atlayabilecektir. +Bir gizli API anahtarı ayrıca **`role: "service_role"`** ile oluşturulacaktır. Bu API anahtarı gizli olmalıdır çünkü **Row Level Security**'yi atlayabilecektir. -API anahtarı şu şekilde görünür: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxNDk5MjcxOSwiZXhwIjoyMDMwNTY4NzE5fQ.0a8fHGp3N_GiPq0y0dwfs06ywd-zhTwsm486Tha7354` +API anahtarı şöyle görünür: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxNDk5MjcxOSwiZXhwIjoyMDMwNTY4NzE5fQ.0a8fHGp3N_GiPq0y0dwfs06ywd-zhTwsm486Tha7354` -### JWT Gizli Anahtarı +### JWT Secret -Uygulamanın **özel JWT jetonları oluşturup imzalayabilmesi** için bir **JWT Gizli Anahtarı** da oluşturulacaktır. +Bir **JWT Secret** de oluşturulacak, böylece uygulama **özel JWT tokenları oluşturup imzalayabilir**. -## Kimlik Doğrulama +## Authentication -### Kayıtlar +### Signups > [!TIP] -> **Varsayılan olarak** supabase, daha önce bahsedilen API uç noktalarını kullanarak projenizde **yeni kullanıcıların hesap oluşturmasına** izin verecektir. +> Varsayılan olarak supabase, önce bahsedilen API uç noktalarını kullanarak projenizde yeni kullanıcıların hesap oluşturmasına izin verir. -Ancak, bu yeni hesaplar varsayılan olarak, **giriş yapabilmek için e-posta adreslerini doğrulamaları** gerekecektir. İnsanların e-posta adreslerini doğrulamadan giriş yapabilmesi için **"Anonim girişlere izin ver"** seçeneğini etkinleştirmek mümkündür. Bu, **beklenmedik verilere** erişim sağlayabilir (kullanıcılar `public` ve `authenticated` rollerini alır).\ -Bu çok kötü bir fikirdir çünkü supabase, aktif kullanıcı başına ücret alır, bu nedenle insanlar kullanıcı oluşturup giriş yapabilir ve supabase bu kullanıcılar için ücret alır: +Bununla birlikte, bu yeni hesaplar varsayılan olarak **oturum açabilmek için e-posta adreslerini doğrulamak zorunda olacaklardır**. İnsanların e-posta adreslerini doğrulamadan giriş yapmalarına izin vermek için **"Allow anonymous sign-ins"** etkinleştirmek mümkündür. Bu, **beklenmeyen verilere** erişim sağlayabilir (altyapı kullanıcıları `public` ve `authenticated` rollerini alır).\ Bu çok kötü bir fikirdir çünkü supabase aktif kullanıcı başına ücretlendirir; insanlar kullanıcı oluşturup giriş yaparsa supabase bunlar için ücret alacaktır:
-### Şifreler ve oturumlar +#### Auth: Server-side signup enforcement -Minimum şifre uzunluğunu belirtmek mümkündür (varsayılan olarak), gereksinimleri (varsayılan olarak yok) ve sızdırılmış şifrelerin kullanılmasını engellemek mümkündür.\ -**Varsayılan gereksinimler zayıf olduğu için gereksinimleri geliştirmek** önerilir. +Frontend'de kayıt butonunu gizlemek yeterli değildir. Eğer **Auth server hala kayıtlara izin veriyorsa**, bir saldırgan public `anon` anahtarıyla doğrudan API'yi çağırıp rastgele kullanıcılar oluşturabilir. -- Kullanıcı Oturumları: Kullanıcı oturumlarının nasıl çalıştığını yapılandırmak mümkündür (zaman aşımı, kullanıcı başına 1 oturum...) -- Bot ve Kötüye Kullanım Koruması: Captcha etkinleştirmek mümkündür. +Hızlı test (kimliği doğrulanmamış bir istemciden): +```bash +curl -X POST \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-d '{"email":"attacker@example.com","password":"Sup3rStr0ng!"}' \ +https://.supabase.co/auth/v1/signup +``` +Expected hardening: +- Dashboard'ta email/password kayıtlarını devre dışı bırakın: Authentication → Providers → Email → Disable sign ups (invite-only), veya eşdeğer GoTrue ayarını yapın. +- API'nin önceki çağrıya artık 4xx döndürdüğünü ve yeni bir kullanıcı oluşturulmadığını doğrulayın. +- Eğer davetler veya SSO'ya güveniyorsanız, açıkça gerekmedikçe diğer tüm providers'ın devre dışı olduğundan emin olun. -### SMTP Ayarları +## RLS and Views: Write bypass via PostgREST -E-posta göndermek için bir SMTP ayarlamak mümkündür. +Duyarlı sütunları “gizlemek” için bir Postgres VIEW kullanmak ve bunu PostgREST üzerinden açığa çıkarmak, ayrıcalıkların nasıl değerlendirildiğini değiştirebilir. In PostgreSQL: +- Ordinary views varsayılan olarak view sahibinin ayrıcalıklarıyla çalışır (definer semantics). PG ≥15'te `security_invoker`'a geçiş yapabilirsiniz. +- Row Level Security (RLS) temel tablolarda uygulanır. Tablo sahipleri, tabloya `FORCE ROW LEVEL SECURITY` ayarı yapılmadıkça RLS'yi atlar. +- Updatable views INSERT/UPDATE/DELETE kabul edebilir ve bunlar daha sonra base tabloya uygulanır. `WITH CHECK OPTION` yoksa, view predicate'ine uymayan yazma işlemleri yine de başarılı olabilir. -### Gelişmiş Ayarlar +Risk pattern observed in the wild: +- Azaltılmış sütunlu bir view Supabase REST üzerinden açığa çıkarılır ve `anon`/`authenticated`'a yetki verilir. +- PostgREST, updatable view üzerinde DML'e izin verir ve işlem view sahibinin ayrıcalıklarıyla değerlendirilir; bu da base tablodaki amaçlanan RLS politikalarını fiilen atlatır. +- Sonuç: düşük ayrıcalıklı istemciler, değiştirmemeleri gereken satırları (ör. profile bios/avatars) topluca düzenleyebilir. -- Erişim jetonları için sonlanma süresi ayarlayın (varsayılan 3600) -- Potansiyel olarak tehlikeye atılmış yenileme jetonlarını tespit etmek ve iptal etmek için ayarlayın -- MFA: Kullanıcı başına bir kerede kaç MFA faktörünün kaydedilebileceğini belirtin (varsayılan 10) -- Maksimum Doğrudan Veritabanı Bağlantıları: Kimlik doğrulama için kullanılan maksimum bağlantı sayısı (varsayılan 10) -- Maksimum İstek Süresi: Bir Kimlik Doğrulama isteğinin sürebileceği maksimum süre (varsayılan 10s) +Illustrative write via view (attempted from a public client): +```bash +curl -X PATCH \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-H "Prefer: return=representation" \ +-d '{"bio":"pwned","avatar_url":"https://i.example/pwn.png"}' \ +"https://.supabase.co/rest/v1/users_view?id=eq." +``` +Hardening checklist for views and RLS: +- Tabloları doğrudan, açıkça belirtilmiş asgari ayrıcalık izinleri ve hassas RLS politikalarıyla açığa çıkarmayı tercih edin. +- Bir görünümü açığa çıkarmanız gerekiyorsa: +- Güncellenemez yapın (ör. expressions/joins içerecek şekilde) veya tüm güvensiz rollere görünümde `INSERT/UPDATE/DELETE`'i reddedin. +- `ALTER VIEW SET (security_invoker = on)` uygulanmasını sağlayın; böylece invoker’ın ayrıcalıkları owner’ınkiler yerine kullanılır. +- Temel tablolarda `ALTER TABLE FORCE ROW LEVEL SECURITY;` kullanın; böylece sahipler bile RLS'e tabi olur. +- Eğer updatable bir görünüm aracılığıyla yazılara izin veriyorsanız, `WITH [LOCAL|CASCADED] CHECK OPTION` ekleyin ve temel tablolarda tamamlayıcı RLS ile yalnızca izin verilen satırların yazılabileceğinden/değiştirilebileceğinden emin olun. +- Supabase'de, end-to-end davranışı testlerle doğrulamamışsanız, view'lar üzerinde `anon`/`authenticated` rollerine herhangi bir yazma ayrıcalığı vermekten kaçının. -## Depolama +Detection tip: +- `anon` ve bir `authenticated` test kullanıcısı ile her açığa çıkarılmış tablo/görünüm üzerinde tüm CRUD işlemlerini deneyin. Reddedilmesini beklediğiniz halde başarılı olan herhangi bir yazma, bir yanlış yapılandırmaya işaret eder. + +### OpenAPI-driven CRUD probing from anon/auth roles + +PostgREST, tüm REST kaynaklarını numaralandırmak ve ardından düşük ayrıcalıklı rollerden izin verilen işlemleri otomatik olarak probe etmek için kullanabileceğiniz bir OpenAPI belgesi sunar. + +Fetch the OpenAPI (works with the public anon key): +```bash +curl -s https://.supabase.co/rest/v1/ \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +-H "Accept: application/openapi+json" | jq '.paths | keys[]' +``` +Probe deseni (örnekler): +- Tek bir satırı oku (beklenen 401/403/200, RLS'e bağlı olarak): +```bash +curl -s "https://.supabase.co/rest/v1/?select=*&limit=1" \ +-H "apikey: " \ +-H "Authorization: Bearer " +``` +- UPDATE testi engellenmiş (test sırasında verileri değiştirmemek için var olmayan bir filtre kullanın): +```bash +curl -i -X PATCH \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-H "Prefer: return=minimal" \ +-d '{"__probe":true}' \ +"https://.supabase.co/rest/v1/?id=eq.00000000-0000-0000-0000-000000000000" +``` +- Test INSERT engellendi: +```bash +curl -i -X POST \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-H "Prefer: return=minimal" \ +-d '{"__probe":true}' \ +"https://.supabase.co/rest/v1/" +``` +- Test DELETE engellenmiş: +```bash +curl -i -X DELETE \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +"https://.supabase.co/rest/v1/?id=eq.00000000-0000-0000-0000-000000000000" +``` +Öneriler: +- Önceki taramaları hem `anon` hem de minimum düzeyde `authenticated` kullanıcı için otomatikleştir ve regresyonları yakalamak için CI'ye entegre et. +- Her açık tablo/görünüm/fonksiyonu birinci sınıf bir yüzey olarak ele al. Bir görünümün “inherits” ile base tablolarıyla aynı RLS duruşunu varsayma. + +### Parolalar & oturumlar + +It's possible to indicate the minimum password length (by default), requirements (no by default) and disallow to use leaked passwords.\ +Varsayılan gereksinimler zayıf olduğu için gereksinimleri **geliştirmeniz önerilir**. + +- User Sessions: Kullanıcı oturumlarının nasıl çalışacağını yapılandırmak mümkün (zaman aşımı, kullanıcı başına 1 oturum...) +- Bot and Abuse Protection: Captcha'yı etkinleştirmek mümkün. + +### SMTP Settings + +E-posta göndermek için bir SMTP ayarlamak mümkün. + +### Advanced Settings + +- Set expire time to access tokens (3600 by default) +- Potansiyel olarak ele geçirilmiş refresh token'ları tespit edip iptal etmek ve timeout ayarlamak mümkün +- MFA: Bir kullanıcının aynı anda kaç MFA faktörü kaydedebileceğini belirt (varsayılan 10) +- Max Direct Database Connections: Kimlik doğrulama için kullanılacak maksimum bağlantı sayısı (varsayılan 10) +- Max Request Duration: Bir Auth isteğinin sürebileceği maksimum süre (varsayılan 10s) + +## Storage > [!TIP] -> Supabase, **dosyaları depolamaya** ve bunları bir URL üzerinden erişilebilir hale getirmeye izin verir (S3 bucket'ları kullanır). +> Supabase allows **to store files** and make them accesible over a URL (it uses S3 buckets). -- Yükleme dosyası boyutu sınırını ayarlayın (varsayılan 50MB) -- S3 bağlantısı şu şekilde bir URL ile verilir: `https://jnanozjdybtpqgcwhdiz.supabase.co/storage/v1/s3` -- `access key ID` (örneğin, `a37d96544d82ba90057e0e06131d0a7b`) ve `secret access key` (örneğin, `58420818223133077c2cec6712a4f909aec93b4daeedae205aa8e30d5a860628`) ile oluşturulan **S3 erişim anahtarı** talep etmek mümkündür. +- Yükleme dosya boyutu limitini ayarla (varsayılan 50MB) +- The S3 connection is given with a URL like: `https://jnanozjdybtpqgcwhdiz.supabase.co/storage/v1/s3` +- S3 access key talep etmek mümkün; bunlar bir `access key ID` (e.g. `a37d96544d82ba90057e0e06131d0a7b`) ve bir `secret access key` (e.g. `58420818223133077c2cec6712a4f909aec93b4daeedae205aa8e30d5a860628`) şeklinde oluşur -## Edge Fonksiyonları +## Edge Functions -Supabase'te **gizli anahtarları** depolamak da mümkündür ve bunlar **edge fonksiyonları tarafından erişilebilir** (web üzerinden oluşturulup silinebilir, ancak değerlerine doğrudan erişmek mümkün değildir). +Supabase içinde **gizli bilgileri saklamak** de mümkün olup bunlar **edge functions** tarafından erişilebilir (web üzerinden oluşturulup silinebilirler, ancak değerlerine doğrudan erişim mümkün değildir). + +## References + +- [Building Hacker Communities: Bug Bounty Village, getDisclosed’s Supabase Misconfig, and the LHE Squad (Ep. 133) – YouTube](https://youtu.be/NI-eXMlXma4) +- [Critical Thinking Podcast – Episode 133 page](https://www.criticalthinkingpodcast.io/episode-133-building-hacker-communities-bug-bounty-village-getdisclosed-and-the-lhe-squad/) +- [Supabase: Row Level Security (RLS)](https://supabase.com/docs/guides/auth/row-level-security) +- [PostgreSQL: Row Security Policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) +- [PostgreSQL: CREATE VIEW (security_invoker, check option)](https://www.postgresql.org/docs/current/sql-createview.html) +- [PostgREST: OpenAPI documentation](https://postgrest.org/en/stable/references/api.html#openapi-documentation) {{#include ../banners/hacktricks-training.md}}