Lovable ve Bolt ile Yapılan Uygulamalar Neyi Açıkta Bırakıyor
Lovable, Bolt, v0 gibi araçlarla çıkan uygulamaların en sık açık bıraktığı beş nokta ve her birini kendi başına 15 dakikada nasıl test edeceğin.
Lovable veya Bolt ile çıkardığın uygulama demoda kusursuz çalışıyor. Sonra gerçek kullanıcı geliyor ve altındaki delikler açılıyor. Bunlar rastgele değil, neredeyse her projede aynı beş nokta aynı sırayla açık kalıyor. Her biri için nedenini, nasıl fark edeceğini ve nasıl kapatacağını aşağıda buluyorsun.
Özet
LLM demo hızlı çalışsın diye güvenlik katmanlarını genelde kapalı bırakır. En sık görülen beş açık: RLS kapalı tablo, istemciye sızan service role key, herkese açık storage bucket, sunucuda doğrulanmayan yetki, imzasız webhook. Aşağıdaki öz-denetim bölümü kendi projeni 15 dakikada beşine karşı da test etmeni gösteriyor.
pg_tables.rowsecurity = falserole: service_role taşıyan JWTStripe-Signature başlığı olmayan POST kabul ediliyorBu delikler neden hep aynı: demo mantığı, üretim mantığı değil
Lovable, Bolt, v0 gibi araçların arkasındaki model bir görevi çözüyor: ekranda çalışan bir şey üretmek. Giriş formu çalışsın, liste dolsun, ödeme ekranı görünsün. Bunun için en kısa yol genelde güvenlik katmanını atlamak oluyor. Tabloya RLS politikası eklemek bir adım daha demek, service role key'i doğrudan istemciye koymak bir adım eksik demek. Model kısa yolu seçiyor, sen de demoyu üç dakikada görüyorsun.
Sorun, bu kısayolların hiçbirinin yayına çıkarken kendiliğinden kapanmaması. Prototip deploy edildiği anda, demoda sorun çıkarmayan aynı açık bütün internete açılıyor.
Demo (varsayılan çıktı)
RLS kapalı, her satır okunabiliyor
Üretim
service role key burada kalıyor
her kullanıcı yalnız kendi satırını görüyor
RLS kapalı tablo: herkes herkesin satırını okuyor
Supabase ve benzeri Postgres tabanlı yapılarda public şemadaki bir tablo PostgREST üzerinden otomatik olarak API'ye açılıyor. Row Level Security kapalıysa, tabloya erişim yetkisi olan her rol tüm satırları okuyor ve yazıyor. RLS açık ama hiç politika yazılmamışsa tam tersi oluyor: hiçbir satır API üzerinden görünmüyor, ta ki bir politika izin verene kadar. RLS'i açmak güvenli varsayılan, kısıtlayıcı olan değil.
Supabase'in kendi Security Advisor'ı bunu otomatik yakalıyor. 0013_rls_disabled_in_public kuralı, public şemada RLS'siz her tabloyu ERROR seviyesinde işaretliyor; dashboard'da Database → Security Advisor altında görünüyor.
alter table public.tablo_adi enable row level security;
create policy "kullanici_kendi_satirini_gorur"
on public.tablo_adi for select
to authenticated
using (auth.uid() = user_id);
RLS'i tablo tablo aç, sonra gerçek erişim kurallarına uyan politikayı yaz. Politikasız RLS her şeyi kapatır, yanlış politika ise yine her şeyi açık bırakır. İkisini de test et.
İstemciye sızan service role key
Supabase iki anahtar veriyor: anon ve service_role. Anon anahtarı RLS politikalarına uyuyor, tarayıcıda durması güvenli. Service role anahtarı Postgres'in BYPASSRLS yetkisini taşıyor ve bütün RLS politikalarını atlıyor. Resmi dokümantasyon net: bu gizli anahtarı web sayfasına, açık kaynak koduna, mobil ya da masaüstü uygulama paketine asla ekleme.
Bu ayrım Lovable veya Bolt ile çalışırken kolayca kayboluyor. Model "bağlantı çalışsın" derdinde, hangi anahtarın istemciye hangisinin sunucuya ait olduğunu ayırt etmiyor. Sonuç: service role key NEXT_PUBLIC_ önekiyle env dosyasına ya da doğrudan bir client component'e yazılıyor, derlenen JS dosyasının içinde düz metin olarak duruyor.
Kapatmak için anahtarı sadece sunucu tarafı koda taşı: API route, edge function, cron job. Zaten sızmışsa taşımak yetmiyor. Dashboard'dan anahtarı yeniden üret. Eski anahtar geçerli kaldığı sürece sızıntı devam ediyor.
Herkese açık storage bucket
Supabase Storage, politikasız bir bucket'a varsayılan olarak yükleme izni vermiyor. Açılması iki yoldan biriyle oluyor: bucket "public" olarak işaretleniyor, ya da storage.objects üzerinde herkese SELECT izni veren geniş bir politika yazılıyor. İkisi de demoda "dosya hemen görünsün" isteğine hızlı çözüm.
Risk somut: kimlik belgesi, fatura, profil fotoğrafı gibi kullanıcı yüklemeleri tahmin edilebilir bir URL'den herkese açık hale geliyor. URL'yi bilen değil, tahmin eden de erişiyor.
create policy "sahibi_kendi_dosyasini_okur"
on storage.objects for select
to authenticated
using (bucket_id = 'yuklemeler' and (storage.foldername(name))[1] = auth.uid()::text);
Bucket'ı private'a çevir, klasör yapını kullanıcı id'sine göre kur, politikayı buna göre yaz.
Arayüzde gizli, sunucuda serbest: doğrulanmayan yetki
Klasik örnek: admin butonu sadece normal kullanıcıdan CSS ile gizleniyor, ama arkasındaki API endpoint kimin istek attığına bakmıyor. Ya da sipariş /api/orders/123 adresinde duruyor ve numarayı 124 yapan herkes başkasının siparişini görüyor. Bu, ders kitabı örneği bir insecure direct object reference.
Arayüzde gizlemek erişim kontrolü değil, görünürlük kontrolü. Yetki kontrolü her zaman sunucuda, her istekte, kullanıcının o kaynağa gerçekten sahip olup olmadığı sorularak yapılmalı.
Webhook imzası kontrol edilmeyen ödeme akışı
Stripe ve benzeri sağlayıcılar "ödeme başarılı" bilgisini sunucuna bir webhook isteğiyle gönderiyor. Stripe'ın resmi dokümantasyonu açık: imza doğrulaması olmadan bir saldırgan sahte webhook event'leri göndererek sipariş tamamlama, hesap erişimi verme veya kayıt değiştirme gibi eylemleri tetikleyebilir. Her gerçek istek, zaman damgası ve gövdenin HMAC-SHA256 ile imzalanmış halini Stripe-Signature başlığında taşıyor; sunucunun bu imzayı kendi endpoint secret'ıyla yeniden hesaplayıp karşılaştırması gerekiyor.
Lovable ile hızlı kurulan ödeme akışlarında bu doğrulama adımı genelde atlanıyor, çünkü test modunda imzasız da çalışıyormuş gibi görünüyor. İkinci risk aynı ailede: fiyatın istemciden gelmesi. Sepet tutarını tarayıcı hesaplayıp sunucuya "bunu tahsil et" diye gönderiyorsa, tarayıcıyı değiştiren herkes fiyatı da değiştiriyor.
Kapatmak için imzayı resmi kütüphaneyle doğrula, isteğin ham gövdesini middleware'de bozma, tahsil edilecek tutarı her zaman sunucuda ürün fiyatından yeniden hesapla.
Kendi uygulamanı 15 dakikada nasıl denersin
Aşağıdaki dört adım kod yazmadan yapılıyor, sadece tarayıcı ve terminalle. Hepsi kendi projene karşı, kendi anahtarınla çalışıyor.
RLS durumunu sorgula
SQL Editor'da tek sorgu
Security Advisor'a bak
Dashboard, kurulum yok
Yayınlanan JS'te sızan anahtarı ara
curl artı bir grep
Bucket'ı anonim dene
Tek curl, sadece anon key
1. RLS durumunu sorgula. Supabase SQL Editor'da çalıştır:
select schemaname, tablename, rowsecurity
from pg_tables
where schemaname = 'public';
rowsecurity sütunu false olan her tablo RLS'siz demek.
2. Security Advisor'a bak. Dashboard → Database → Security Advisor. rls_disabled_in_public uyarısı çıkan tablo, önce hangisine bakman gerektiğini doğrudan söylüyor.
3. Yayınlanan JS'te sızan anahtarı ara. Canlı sitenin derlenmiş dosyalarını çek, içinde JWT görünümlü bir dize var mı bak:
curl -s https://siten.com | grep -oE '/_next/static/chunks/[^"]+\.js' | \
while read f; do curl -s "https://siten.com$f"; done | \
grep -oE 'eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'
Bulduğun her JWT'nin ortadaki bölümünü base64 -d ile çöz. Payload'da "role":"anon" görüyorsan sorun yok. "role":"service_role" görüyorsan o anahtar herkese açık demektir. Hemen rotate et.
4. Bucket'ı anonim dene. Sadece anon key ile, tarayıcıdaki herhangi bir ziyaretçinin zaten yapabileceğini yap:
curl -s -X GET "https://proje-ref.supabase.co/storage/v1/object/list/bucket-adi" \
-H "apikey: ANON_KEYIN" \
-H "Authorization: Bearer ANON_KEYIN"
200 ve dosya listesi dönerse bucket herkese açık demek. 400 veya 403 dönerse politika işini yapıyor demek.
Ne zaman kendin kapatırsın, ne zaman devralma gerekir
Kendi başına yapabileceklerin: RLS'i açmak, tek bir tabloluk sade politika yazmak, bucket'ı private'a çevirmek, sızmış anahtarı rotate etmek. Her biri neredeyse tek bir komut, yukarıdaki dört adım seni oraya kadar götürüyor.
Yardım gereken kategori farklı: politikaları gerçek iş mantığına göre tasarlamak (yanlış yazılmış bir politika ya her şeyi kapatır ya da yine her şeyi açık bırakır), ödeme akışını sıfırdan güvenli kurmak, tüm codebase'i satır satır taramak. Bunlar tek komutla çözülmüyor, kod okumayı gerektiriyor.
Kodun büyükse ve nereden başlayacağını bilmiyorsan, yapay zeka projesi tamamlama hizmetimiz tam bu noktadan başlıyor: önce kod incelemesi, ne var ne tehlikeli net şekilde ortaya çıkıyor, sonra kapsam çıkarılıyor.
Sık sorulan sorular
Lovable veya Bolt güvensiz mi?
Hayır. Sorun aracın kendisinde değil. Üretilen kod hızlı çalışan bir demoyu hedefliyor, sertleştirilmiş bir üretim sistemini değil. Fark araçta değil, kimsenin RLS'i, anahtarları ve yetkiyi sonradan gözden geçirmemesinde.
RLS'i açmak mevcut kullanıcıları etkiler mi?
Henüz politika yazılmamış bir tabloda RLS açarsan, o tablonun verisi API üzerinden anında kesiliyor ve uygulama göstermeyi durduruyor. RLS açma ve politika yazma aynı anda yapılmalı, önce test ortamında denenerek.
Service role key'i rotate etmek neyi bozar?
O anahtarı kullanan her sunucu tarafı servis (API route, cron, edge function) yeni anahtarla güncellenmeli. Sadece istemciye sızan kopyayı geçersiz kılıyorsun, sunucu tarafı kullanım güncellendikten sonra devam ediyor.
Bu dört kontrolü yaptım, hepsi temiz çıktı. Güvende miyim?
Bu dördü en sık görülen ve en kolay kaçırılan açıklar, kapsamlı bir güvenlik denetimi değil. Ödeme mantığı, yetki hataları ve iş mantığına özgü açıklar kod okunmadan görünmüyor.
Projem hiç yayına girmedi, sadece Lovable'da duruyor. Yine de risk var mı?
Lovable'ın kendi önizleme adresi de canlı bir URL. Paylaştığın her önizleme linki, zaten canlıymış gibi test edilmeli.