IDOR Açığı Neden Hâlâ Bu Kadar Yaygın?
IDOR, identifier tahmin edilebildiği için değil, server her object access sırasında doğru authorization kararını veremediği için oluşur. Yaygınlığın mimari ve süreç nedenlerini inceliyoruz.
SECNODEX Güvenlik Ekibi
Ofansif Güvenlik
Bir kullanıcı kendi faturasını indirmek için şu endpoint'e request gönderiyor:
GET /api/invoices/8f24.../download
Uygulama kullanıcıyı başarıyla authenticate ediyor. Invoice UUID format olarak geçerli. Kayıt database'de bulunuyor. File storage'dan PDF alınıyor ve response dönüyor.
Akış teknik olarak sorunsuz çalışıyor. Tek eksik, server'ın şu soruyu hiç sormamış olmasıdır:
Kısa cevap
“Bu kullanıcı, tam olarak bu invoice üzerinde download action'ını gerçekleştirmeye yetkili mi?”
Kullanıcı başka bir invoice UUID'si elde ettiğinde aynı endpoint ikinci faturayı da döndürüyorsa ortaya çıkan problem IDOR veya API terminolojisinde Broken Object Level Authorization olur.
IDOR yıllardır bilinen, anlatması kolay ve ilk bakışta düzeltmesi basit görünen bir vulnerability'dir. Buna rağmen OWASP Top 10:2025 içinde Broken Access Control yine ilk sıradadır. OWASP API Security Top 10:2023 ise Broken Object Level Authorization'ı API riskleri arasında birinci sıraya yerleştirir.
Bu durum şu soruyu doğurur:
Kısa cevap
Authentication framework'leri, API gateway'ler, UUID'ler, policy engine'ler ve modern SAST araçları bu kadar gelişmişken IDOR açığı neden hâlâ bu kadar yaygın?
Çünkü IDOR esas olarak bir identifier problemi değildir. Bir authorization consistency problemidir.
Kısa cevap
Saldırganın başarılı olması için yüzlerce object access path'i içinden yalnızca birinde eksik kontrol bulması yeterlidir. Uygulamanın güvenli olması içinse her object, action, role, tenant, API version ve execution path üzerinde doğru object-level authorization kararı verilmesi gerekir.
Bir endpoint'in login istemesi yeterli değildir. User role'ün genel olarak o fonksiyona erişebilmesi de yeterli değildir. Server her request'te subject, object, action ve context ilişkisini değerlendirmelidir.
Bu yazıda IDOR'un neden hâlâ yaygın olduğunu yalnızca “developer authorization kontrolünü unutuyor” cümlesiyle açıklamayacağız. Multi-tenant architecture, ORM kullanımı, UUID yanılgısı, GraphQL resolver'ları, cache, file storage, background job, bulk endpoint, microservice ve legacy API gibi gerçek implementation noktalarını ayrı ayrı inceleyeceğiz. Ardından IDOR'un nasıl test edilmesi ve kalıcı biçimde nasıl önlenmesi gerektiğini ele alacağız.
IDOR nedir?
IDOR, Insecure Direct Object Reference ifadesinin kısaltmasıdır. Uygulama, kullanıcı tarafından gönderilen bir object reference üzerinden kayda veya kaynağa ulaşır, fakat request'i yapan subject'in o object üzerinde ilgili action için yetkili olup olmadığını yeterli biçimde doğrulamaz.
Object reference yalnızca sıralı bir database ID'si değildir. Şunların tamamı doğrudan veya dolaylı reference olabilir:
- Numeric primary key
- UUID veya GUID
- Username, e-mail ya da account number
- Invoice number
- Order code
- Support ticket key
- File name veya storage key
- Document slug
- Vehicle Identification Number
- Message ID
- Export job ID
- Backup ID
- GraphQL global node ID
- WebSocket channel veya topic adı
- Cloud resource identifier
- Encoded veya encrypted object token
Object de yalnızca bir database row değildir. File, report, message, image, invoice, medical record, payment instruction, API key, device, vehicle, backup, project, source code artifact veya administrative task bir object olabilir.
IDOR şu action'ların herhangi birinde ortaya çıkabilir:
- Read
- List
- Download
- Create child object
- Update
- Delete
- Approve
- Reject
- Cancel
- Share
- Export
- Restore
- Transfer ownership
- Trigger background process
- Generate signed URL
- Execute business action
Bu nedenle “GET request'te başka kaydı okuyabiliyor mu?” testi IDOR coverage'ının yalnızca küçük bir bölümüdür.
IDOR, BOLA ve Broken Access Control aynı şey mi?
Terimler birbiriyle ilişkilidir, fakat aynı kapsamı ifade etmez.
Broken Access Control
En geniş kategoridir. Bir kullanıcının, service'in veya başka bir subject'in izin verilen sınırların dışında action gerçekleştirebilmesini kapsar.
IDOR
User-controlled object reference üzerinden başka bir kaynağa unauthorized access sağlanmasıdır. Terim web application güvenliğinde uzun süredir kullanılır.
Broken Object Level Authorization
API dünyasında IDOR'a çok yakın ve daha açıklayıcı kullanılan ifadedir. Server, request'teki object identifier ile işlem yaparken subject'in o object üzerindeki yetkisini doğrulamaz.
Broken Object Property Level Authorization
Object'in tamamına değil belirli property'lerine ilişkin authorization problemidir. Kullanıcı object'i görmeye yetkilidir, fakat salary, internalNotes, role veya creditLimit gibi property'leri okumaya ya da değiştirmeye yetkili değildir.
Broken Function Level Authorization
Kullanıcı, object'ten bağımsız olarak erişmemesi gereken bir function veya endpoint'e ulaşır. Standard user'ın administrative endpoint'i çağırması buna örnektir.
| Problem | Yetki hangi seviyede bozulur? | Örnek |
|---|---|---|
| IDOR / BOLA | Belirli object | User A, User B'nin invoice'unu indirir |
| Broken Object Property Level Authorization | Object property | User kendi profile'ında role=admin yazar |
| Broken Function Level Authorization | Function | Standard user admin export endpoint'ini çağırır |
| Authentication failure | Subject identity | Saldırgan başka kullanıcı olarak login olur |
Bu sınıflar aynı attack path içinde birleşebilir. Örneğin standard user önce administrative bulk export function'ına erişir, ardından request içindeki tenant ID'yi değiştirerek başka kurumun verisini indirir. Burada function-level ve object-level authorization birlikte bozulmuştur.
IDOR neden identifier problemi değildir?
Sıralı 12345 yerine UUID kullanmak object enumeration'ı zorlaştırabilir. Ancak UUID'yi bilen kişinin yetkili olduğu varsayımını güvenli hale getirmez.
Bir identifier şu yollardan sızabilir:
- Başka API response'u
- Search sonucu
- Notification veya e-mail linki
- Browser history
- Application log'u
- Analytics event'i
- Referer header
- Shared URL
- Screenshot
- Mobile application local storage
- Client-side JavaScript state
- Export file
- Support konuşması
- Public profile veya attachment
- Error response
- GraphQL relationship
- WebSocket event
- Third-party integration
OWASP IDOR Prevention Cheat Sheet, GUID gibi karmaşık identifier'ların guess etmeyi zorlaştırabileceğini ancak access control check'in yine de zorunlu olduğunu açıkça vurgular.
Doğru güvenlik özelliği şudur:
Kısa cevap
Object identifier saldırgan tarafından tamamen bilinse bile server unauthorized action'ı reddetmelidir.
Identifier entropy defense in depth sağlayabilir. Authorization control'ün yerine geçmez.
IDOR neden hâlâ bu kadar yaygın?
Yaygınlığın tek bir nedeni yoktur. IDOR, software architecture, development hızı, test yaklaşımı ve authorization modelindeki küçük boşlukların birleşiminden doğar.
1. Authentication ile authorization karıştırılıyor
Uygulama request başında JWT doğruluyor veya session kontrolü yapıyor. Developer bundan sonra gelen request'in güvenilir olduğunu düşünüyor.
Authentication yalnızca request'i kimin yaptığını söyler. Authorization ise bu subject'in belirli bir resource üzerinde belirli bir action'a izinli olup olmadığını söyler.
Şu kontrol eksiktir:
JWT geçerli mi? → Evet
User role endpoint'e erişebilir mi? → Evet
Invoice var mı? → Evet
Invoice bu user veya tenant için erişilebilir mi? → Kontrol edilmediIDOR çoğunlukla ilk üç adım doğru olduğu için gözden kaçar.
2. Function-level kontrol object-level kontrol sanılıyor
Route üzerinde authenticated, customer veya canDownloadInvoice benzeri bir middleware bulunabilir. Bu, user'ın genel olarak invoice download function'ını kullanabileceğini gösterir. Hangi invoice'u indirebileceğini göstermez.
Object-level karar şu bileşenleri birlikte değerlendirmelidir:
- Subject
- Object
- Action
- Tenant
- Relationship
- Object state
- Request context
User invoice okuyabilir, fakat yalnızca kendi organization'ına ait invoice'ları. Finance role okuyabilir, fakat delete edemeyebilir. Support role masked view görebilir, raw PDF alamayabilir. Approver aynı object'i belirli state'te onaylayabilir, ancak kendi oluşturduğu kaydı onaylayamayabilir.
Tek role check bu nüansları taşıyamaz.
3. ORM kullanımı unsafe erişimi fazla kolaylaştırıyor
Modern framework ve ORM'lerde object retrieval son derece kolaydır:
invoice = Invoice.find(request.invoiceId)Kod kısa, okunabilir ve functional olarak doğrudur. Fakat global dataset üzerinde arama yapar. Authorization daha sonra ayrıca yazılmak zorundadır.
Güvenli pattern object'i authorized scope içinden aramalıdır:
invoice = currentTenant.invoices
.where(id = request.invoiceId)
.firstOrDeny()Bu örnek tek başına bütün authorization problemlerini çözmez. User'ın tenant içinde o invoice'a erişim hakkı ayrıca değerlendirilebilir. Ancak global findById yerine tenant-scoped repository kullanmak cross-tenant riskini yapısal olarak azaltır.
4. Authorization farklı katmanlara dağılıyor
Bir endpoint controller'da check yapıyor. Başka endpoint service layer'a güveniyor. Background job repository'ye doğrudan gidiyor. GraphQL resolver policy helper kullanıyor. Admin panel ise legacy middleware ile korunuyor.
Kontrol dağıldıkça şu soruya cevap vermek zorlaşır:
Kısa cevap
“Bu object'e ulaşan bütün entry point'lerde aynı policy gerçekten uygulanıyor mu?”
Saldırganın yalnızca unutulmuş tek bir path bulması yeterlidir.
5. Multi-tenant context request'ten alınıyor
Multi-tenant application'larda tenantId, organizationId veya workspaceId request içinde taşınabilir. Backend bu değeri trusted kabul ederse saldırgan hem resource ID'yi hem tenant boundary'yi kontrol eder.
Riskli akış şöyledir:
tenantId = request.header["X-Tenant-ID"]
documentId = request.path.id
document = repository.find(tenantId, documentId)Burada query iki alanla sınırlandırılmış görünür, fakat tenant context authenticated identity'den türetilmemiştir.
Doğru yaklaşım current tenant bilgisini verified session, token claim, membership veya server-side context üzerinden çıkarmaktır. Request'teki tenant seçimi varsa bile user'ın o tenant membership'i ayrıca doğrulanmalıdır.
6. UUID güvenlik kontrolü zannediliyor
Numeric ID UUID'ye çevrildiğinde basic enumeration testi başarısız olur. Takım vulnerability'nin kapandığını düşünebilir.
Oysa UUID şu yollardan bir kez sızdığında access control problemi tekrar görünür:
- List endpoint başka tenant object'ini dönüyor
- Notification yanlış kullanıcıya gidiyor
- GraphQL node relationship ID'yi açığa çıkarıyor
- Support log'unda UUID bulunuyor
- Signed URL başka kullanıcıya forward ediliyor
- Bulk operation response'u farklı object ID'lerini içeriyor
UUID exploitability'yi azaltabilir. Root cause'u düzeltmez.
7. Frontend'de görünmeyen action güvenli sanılıyor
UI başka kullanıcının object ID'sini göstermeyebilir. Delete button yalnızca owner için render edilebilir. Mobile application ilgili action'ı hiç sunmayabilir.
Saldırgan frontend kurallarına bağlı değildir. Request'i doğrudan gönderebilir, eski mobile client'ı kullanabilir veya browser'da yakaladığı request'i değiştirebilir.
Authorization trusted server-side code içinde uygulanmalıdır. Button gizlemek access control değildir.
8. Her CRUD action ayrı ayrı korunmuyor
Read endpoint doğru policy kullanırken update veya delete endpoint'i yalnızca object'in varlığını kontrol edebilir. Export, preview, history, restore ve attachment path'leri daha da sık unutulur.
OWASP IDOR Prevention Cheat Sheet de verification'ın read, create, update, delete, export ve administrative action'lar dahil object reference kullanan bütün operasyonlarda yapılması gerektiğini belirtir.
| Object | Read | Update | Delete | Share | Export | Approve |
|---|---|---|---|---|---|---|
| Invoice | Kontrol var | Kontrol var | N/A | N/A | Eksik | Finance only |
| Document | Kontrol var | Owner only | Eksik | Kontrol var | Eksik | N/A |
| Ticket | Participant | Agent only | N/A | N/A | Admin only | N/A |
Bir object için tek “erişebilir” kararı çoğu zaman yetersizdir. Action bazlı policy gerekir.
9. Alternative endpoint ve legacy API'ler unutuluyor
Yeni /api/v3/documents/{id} endpoint'i merkezi policy kullanabilir. Eski /api/v1/file?id= endpoint'i hâlâ mobile client uyumluluğu için açık olabilir. Admin API, batch API veya partner integration aynı object'i farklı path'ten getiriyor olabilir.
Common risk noktaları şunlardır:
- Eski API version
- Mobile-only endpoint
- Internal API
- Partner API
- Admin ve support endpoint
- GraphQL ile REST'in paralel kullanımı
- WebSocket subscription
- Bulk ve batch operation
- Export ve reporting service
- Preview ve thumbnail endpoint
Authorization inventory endpoint inventory ile birlikte tutulmadığında coverage boşluğu oluşur.
10. Microservice geçişinde subject context kayboluyor
API gateway user'ı authenticate ediyor ve request'i service'e iletiyor. Service başka bir internal service'i çağırırken yalnızca object ID gönderiyor. Downstream service çağrının internal network'ten geldiğini görüp trusted kabul ediyor.
Akış şöyle olabilir:
Client → API Gateway → Order Service → Document Service → StorageDocument Service yalnızca documentId alıyor, subject ve tenant context'i almıyorsa object-level karar veremez. Upstream service'in check yaptığı varsayılır. Başka bir caller aynı internal API'yi doğrudan kullandığında control bypass edilir.
Service-to-service trust, end-user authorization'ın yerine geçmez. Policy kararı güvenli biçimde iletilmeli veya downstream service gerekli context ile yeniden authorization yapmalıdır.
11. Cache tenant ve user boundary'yi taşımıyor
Application database query'sini doğru scope ile yapabilir, fakat cache key yalnızca object ID içeriyor olabilir:
cacheKey = "invoice:" + invoiceIdİlk request Tenant A için authorized invoice'u cache'e koyar. İkinci request Tenant B'den aynı veya collision üreten reference ile geldiğinde cache layer authorization query'sini bypass edip cached object'i döndürebilir.
Cache key design, tenant isolation'ın parçasıdır. Bazı durumlarda tenant ID yeterlidir. Bazı object'lerde user, role, data projection veya policy version da cache context'ine dahil olmalıdır.
12. File access database authorization'dan ayrılıyor
Application metadata için authorization yapar, fakat file download doğrudan storage key ile çalışabilir. Ya da file server yalnızca signed URL'nin geçerliliğini kontrol eder.
Şu hatalar görülebilir:
- Storage key kullanıcıdan doğrudan alınır
- Signed URL başka user'a forward edilebilir
- URL object ownership değiştikten sonra geçerli kalır
- Thumbnail endpoint ana file policy'sini kullanmaz
- Export archive içindeki file'lar ayrı ayrı scope edilmez
- Deleted record'ın blob'u erişilebilir kalır
- Public bucket path UUID içerdiği için güvenli sanılır
File identifier'ın random olması file authorization sağlamaz.
13. Background job request anındaki policy'yi yeniden doğrulamıyor
User export başlatıyor. Job queue'ya yalnızca exportId ve tenantId yazılıyor. Worker daha sonra object'leri topluyor ve download linki üretiyor.
Aradaki sürede şu değişiklikler olabilir:
- User tenant'tan çıkarılmıştır
- Role düşürülmüştür
- Object ownership değişmiştir
- Export iptal edilmiştir
- Data classification yükselmiştir
- Share permission revoke edilmiştir
Job, creation anındaki authorization'ı sonsuza kadar geçerli sayarsa stale authorization oluşur. Queue message subject, action ve tenant context'i taşımalı, execution anında gerekli policy yeniden değerlendirilmelidir.
14. GraphQL'de edge korunuyor, node korunmuyor
GraphQL schema içinde user'ın projects edge'i authorization ile korunabilir. Fakat global node(id:) resolver aynı Project object'ini doğrudan döndürebilir. Mutation, nested resolver veya alias kullanımı farklı path açabilir.
OWASP GraphQL Cheat Sheet, authorization'ın hem edge hem node seviyesinde uygulanmasını önerir. Bir relationship üzerinden object'in görünmemesi, direct node access'in güvenli olduğu anlamına gelmez.
15. Bulk endpoint tek object policy'sini atlıyor
Tekil delete endpoint şu kontrolü yapıyor:
policy.authorize(user, document, DELETE)Bulk endpoint performans için ID listesini doğrudan repository'ye veriyor:
repository.deleteAll(request.documentIds)Listedeki object'lerden yalnızca bazılarının user'a ait olması, bütün listeyi güvenli hale getirmez. Her item authorization scope içinde filtrelenmeli ve partial failure davranışı açıkça tasarlanmalıdır.
Bulk endpoint'ler IDOR etkisini tek kayıttan toplu data destruction'a büyütebilir.
16. Object lifecycle policy'den daha karmaşık
Bir object yalnızca “owner” veya “not owner” durumunda değildir. Lifecycle boyunca şu state'lerden geçebilir:
- Draft
- Submitted
- Approved
- Shared
- Transferred
- Archived
- Soft deleted
- Restored
- Legal hold
- Closed
User draft'ı update edebilir, submitted object'i yalnızca görüntüleyebilir, approved object'i hiç değiştiremez. Shared object read-only olabilir. Ownership transfer sonrası eski owner'ın cached veya signed access'i sonlanmalıdır.
Authorization object state ile birlikte modellenmezse aynı endpoint farklı state'te IDOR veya unauthorized action üretir.
17. Authorization negative testleri eksik kalıyor
Functional test şunu doğrular:
Kısa cevap
User A kendi document'ını okuyabiliyor.
Security açısından asıl gerekli testlerden biri şudur:
Kısa cevap
User A, User B'nin document'ını okuyamıyor.
İkincisi yazılmadığında endpoint functional olarak yeşil görünür. Cross-tenant, cross-role, inactive membership ve revoked share scenario'ları test matrix'e eklenmezse authorization regression kolayca production'a çıkar.
18. Otomatik scanner'lar business ownership'i bilmiyor
Bir DAST scanner valid account ile object ID değiştirip response farkı arayabilir. SAST missing annotation veya unsafe repository method'u bulabilir. Ancak tool çoğu zaman şu bilgileri kendiliğinden bilemez:
- Hangi object kime ait?
- User A ile User B aynı tenant'ta mı?
- Bu role hangi action izinli?
200 OKresponse içindeki data gerçekten unauthorized mı?404olması gerekirken403dönmesi yalnızca bilgi sızıntısı mı?- Shared object için hangi relationship geçerli?
- Support role masked property mi görmeli?
Bu nedenle IDOR detection kaliteli test data, role matrix ve manual reasoning gerektirir.
19. Authorization sorumluluğunun sahibi belirsiz kalıyor
Controller developer service'in check yaptığını düşünüyor. Service developer API gateway'in role kontrolüne güveniyor. Repository developer'ın işi yalnızca data access. Security team ise framework middleware'in yeterli olduğunu varsayıyor.
Sonuçta herkes authorization'ın başka bir katmanda uygulandığını düşünüyor.
Her object type için şu soruların net owner'ı olmalıdır:
- Policy kim tarafından tanımlanır?
- Hangi layer enforce eder?
- Bypass gerektiren administrative flow nasıl yönetilir?
- Exception kim tarafından onaylanır?
- Regression test'i kim yazar?
- Policy değişikliği kim tarafından review edilir?
20. Performans gerekçesiyle authorization query'den ayrılıyor
Developer önce object'i global query ile fetch eder, sonra ayrı query ile membership kontrol eder. Bulk işlemlerde policy check pahalı olduğu için atlanabilir. Cache hit oranı düşmesin diye tenant key kaldırılabilir.
Security ve performance birbirine karşıt hedefler değildir. Authorization-aware data access, batched policy evaluation, indexed composite key ve precomputed relationship gibi pattern'lerle ikisi birlikte tasarlanmalıdır.
Performance optimization, object-level authorization'ı devre dışı bırakmak için geçerli gerekçe değildir.
IDOR yalnızca URL parametresinde mi bulunur?
Hayır. Object reference request'in herhangi bir bölümünde olabilir.
| Konum | Örnek reference |
|---|---|
| URL path | /documents/{documentId} |
| Query string | ?invoice=... |
| JSON body | {"accountId":"..."} |
| Form field | Hidden project_id |
| Header | X-Workspace-ID |
| Cookie | Selected organization key |
| GraphQL variable | nodeId, reportKeys |
| WebSocket message | subscribe.channelId |
| gRPC message | Resource name veya numeric key |
| Queue message | tenantId, jobId, objectKey |
| File path | Storage key veya filename |
| Signed URL | Object path ve expiry claim |
IDOR testi yalnızca Burp Repeater'da URL sonundaki sayıyı artırmak değildir. Request'in object seçimini etkileyen bütün veri noktaları incelenmelidir.
Horizontal, vertical ve cross-tenant etkiler
Horizontal authorization bypass
Aynı privilege seviyesindeki User A, User B'nin resource'una erişir. Klasik IDOR örneğidir.
Cross-tenant access
User A, başka bir customer veya organization'a ait object'e erişir. Multi-tenant SaaS için etkisi çoğunlukla daha yüksektir. Tek bulgu customer isolation iddiasını zedeleyebilir.
Vertical etki
Standard user administrative owner'a ait object'i değiştiriyor veya yalnızca privileged role'ün yapabileceği action'ı object reference üzerinden tetikliyorsa vertical privilege etkisi oluşabilir. Burada BOLA ile BFLA birlikte değerlendirilebilir.
Cross-context access
User aynı object'e bir context'te yetkilidir, başka context'te değildir. Örneğin employee kendi department'ında document okuyabilir, project'ten çıkarıldıktan sonra okuyamaz. Signed URL veya cached permission eski context'i taşımaya devam ederse authorization bozulur.
Multi-tenant sistemlerde IDOR neden daha kritik?
Single-tenant application'da IDOR iki user arasında data leakage oluşturabilir. Multi-tenant SaaS'ta aynı hata kurumlar arası data isolation'ı kırar.
Tenant boundary şu katmanların tamamında korunmalıdır:
- Authentication context
- API ve resolver
- Service method
- Repository query
- Database policy
- Cache
- Search index
- File storage
- Message queue
- Export ve reporting
- Observability data
- Backup ve restore
Yalnızca application query'sine tenant_id eklemek diğer katmanları otomatik güvenli hale getirmez.
Güvenli tenant context nasıl kurulmalı?
Tenant context mümkün olduğunca server tarafından verified identity ve membership üzerinden üretilmelidir.
authenticatedSubject = session.requireUser()
requestedTenant = request.workspaceId
membership = membershipService.requireActive(
authenticatedSubject.id,
requestedTenant
)
tenantContext = TenantContext.from(membership)Sonraki layer'lar raw request tenant ID yerine doğrulanmış TenantContext kullanmalıdır.
Composite lookup neden değerlidir?
Object global ID ile değil tenant scope içinde aranır:
document = documents.findOne(
tenantId = tenantContext.id,
documentId = request.documentId
)Bu pattern cross-tenant retrieval'ı zorlaştırır. Ancak aynı tenant içindeki user-level relationship ve action policy'si yine ayrıca gerekebilir.
Database Row-Level Security çözüm mü?
RLS güçlü bir defense in depth katmanı olabilir. Query application katmanında tenant filter unutsa bile database current tenant policy'si ile row erişimini sınırlayabilir.
Fakat RLS şu riskleri tek başına çözmez:
- Yanlış tenant context set edilmesi
- Shared high-privilege database account
- Policy bypass yetkisi
- Cache ve search index leakage
- File storage access
- Same-tenant user ownership
- Background worker context'i
- Object action ve state rule'ları
RLS tek güvenlik kontrolü değil, katmanlı modelin parçası olmalıdır.
UUID kullanmak IDOR'u çözer mi?
Hayır. UUID yalnızca identifier discovery maliyetini artırabilir.
UUID'nin gerçek faydaları şunlardır:
- Sequential enumeration'ı zorlaştırır
- Object count hakkında bilgi sızıntısını azaltabilir
- Dağıtık sistemde collision riski düşük identifier sağlar
- Accidental reference karışmasını azaltabilir
Çözmediği problemler şunlardır:
- Sızmış UUID ile unauthorized access
- Cross-tenant lookup
- Update ve delete policy eksikliği
- Signed URL forwarding
- GraphQL node access
- Cache isolation
- Background job authorization
- Bulk operation
- Share revoke sonrası stale access
UUID kullanılabilir, fakat raporda IDOR remediation olarak yalnızca “ID yapısı UUID'ye çevrildi” yazılması yeterli değildir.
Base64, encryption veya signed identifier çözüm mü?
Base64
Encoding'dir, access control değildir. Kolayca decode edilebilir.
Encrypted identifier
Identifier'ı gizleyebilir, fakat key management, mode, integrity ve replay sorunları ekler. Decrypt edilen object için yine authorization gerekir.
Signed identifier
Tampering'i önleyebilir. Token içindeki object ID değiştirilirse signature bozulur. Ancak token başka kullanıcıya aktarılabiliyorsa ve subject, tenant, action, expiry gibi context'e bind edilmemişse replay ile unauthorized access devam edebilir.
Signed URL
Kısa ömür ve narrow action ile faydalıdır. Buna rağmen generation anında authorization yapılmalı, URL scope'u object ve action'a bağlanmalı, hassas object'lerde revocation ihtiyacı düşünülmelidir.
Her durumda temel soru değişmez:
Kısa cevap
Request'i yapan subject bu object üzerinde bu action için şu anda yetkili mi?
Güvenli object-level authorization nasıl tasarlanır?
Tek bir annotation veya middleware bütün application'ı otomatik güvenli hale getirmez. Sağlam model birkaç katmandan oluşur.
1. Deny by default
Policy açıkça izin vermiyorsa access reddedilmelidir. Yeni endpoint, action veya object state varsayılan olarak açık olmamalıdır.
OWASP Authorization Cheat Sheet, unmatched request için default kararın deny olması gerektiğini vurgular.
2. Her request'te permission validation
Frontend daha önce aynı object'i listelemiş olabilir. User bir dakika önce yetkili olabilir. Client object ID'yi güvenilir kaynaktan almış olabilir. Bunların hiçbiri yeni request'te authorization check'i kaldırmaz.
Permission her request'te doğrulanmalıdır.
3. Subject, object, action ve context birlikte değerlendirilir
Authorization kararı yalnızca role'e bakmamalıdır.
decision = policy.evaluate(
subject = currentUser,
object = invoice,
action = DOWNLOAD,
context = {
tenant,
objectState,
relationship,
authenticationStrength,
deviceTrust
}
)Her uygulamanın bütün context alanlarına ihtiyacı yoktur. Önemli olan business requirement'ın policy modeline açıkça çevrilmesidir.
4. Authorized dataset üzerinden lookup
Mümkün olduğunda global object retrieval yerine current subject veya tenant scope'u içinde arama yapılmalıdır.
Riskli pattern:
document = repository.findById(request.documentId)
policy.check(currentUser, document, READ)Daha güvenli başlangıç:
document = repository.findReadableBy(
subject = currentUser,
tenant = currentTenant,
id = request.documentId
)İkinci model authorization logic'in tamamını repository'ye gömmek zorunda değildir. Fakat unsafe global access method'larının application code içinde yayılmasını önler.
5. Tenant-scoped repository
Repository current tenant olmadan çalışmamalıdır. Tenant ID raw function argument olarak her caller'dan alınmak yerine verified context'ten gelmelidir.
class TenantScopedDocumentRepository:
context = requireTenantContext()
get(id):
return query(
document.id == id,
document.tenantId == context.tenantId
)Missing tenant context fail-open değil fail-closed sonuç üretmelidir.
6. Centralized policy, domain-specific karar
Authorization'ın bütün endpoint'lerde tekrar yazılması inconsistency üretir. Merkezi policy framework veya reusable enforcement noktası faydalıdır.
Ancak “tek global middleware” de bütün domain rule'larını anlayamaz. Sağlıklı modelde enforcement merkezi, policy domain-aware olabilir.
Örneğin:
- Invoice policy ownership ve finance role'e bakar
- Medical record policy care relationship ve consent'e bakar
- Document policy workspace membership ve share state'e bakar
- Refund policy amount limit ve separation of duties kuralına bakar
7. RBAC tek başına yeterli kabul edilmemeli
RBAC, role'ün hangi function'ları kullanabildiğini yönetmek için faydalıdır. IDOR ise çoğu zaman aynı role içindeki iki user'ın object relationship'inden doğar.
ABAC ve ReBAC şu soruları daha iyi modelleyebilir:
- Subject hangi tenant'ta?
- Object'in owner'ı kim?
- User project member mı?
- Object subject ile paylaşılmış mı?
- Department ilişkisi var mı?
- Object state action'a izin veriyor mu?
Yine de policy model adı güvenliği garanti etmez. Implementation, policy coverage ve test gerekir.
8. Authorization check tek katmana bırakılmamalı
API layer hızlı deny sağlayabilir. Service layer business action'ı koruyabilir. Repository tenant scope'u enforce edebilir. Database RLS ek boundary sağlayabilir.
Katmanların hepsinde aynı logic'i kopyalamak yerine farklı failure mode'ları kesecek defense in depth tasarlanmalıdır.
9. Cache authorization-aware olmalı
Cache key ve cache value şu sorulara göre tasarlanmalıdır:
- Object tenant-specific mi?
- Response user'a göre property filtreliyor mu?
- Role'e göre farklı projection var mı?
- Permission revoke olduğunda cache invalid oluyor mu?
- Shared object policy değiştiğinde stale response kalıyor mu?
- Negative authorization response cache'leniyor mu?
objectId tek başına cache identity olmaya yetmeyebilir.
10. Background job subject context taşımalı
Queue message içinde yalnızca object ID değil, doğrulanabilir subject ve tenant context'i bulunmalıdır. Worker kendi service identity'sinin geniş yetkisini end-user yetkisi gibi kullanmamalıdır.
Critical job'larda execution anında reauthorization, idempotency ve policy version kontrolü gerekebilir.
11. File ve blob access mapping üzerinden yapılmalı
User-controlled storage key doğrudan file service'e verilmemelidir. Application object ile blob arasındaki mapping'i authorized scope içinde bulmalı ve kısa ömürlü access üretmelidir.
File metadata, thumbnail, preview, original ve export copy aynı policy ailesi içinde değerlendirilmelidir.
12. Error response güvenli olmalı
Unauthorized object için 403 veya 404 seçimi product ve enumeration riskine göre yapılabilir. Resource existence bilgisini saklamak için 404 tercih edilebilir.
Ancak status code değiştirmek IDOR'u düzeltmez. Server önce authorization kararı vermelidir. Unauthorized data response body, timing, size veya header içinde sızmamalıdır.
Authorization decision modelinde en sık yapılan hatalar
IDOR remediation sırasında geliştiricinin ilk refleksi çoğu zaman eksik endpoint'e bir if koşulu eklemektir. Bu değişiklik görünen bulguyu kapatabilir. Fakat authorization kararı yanlış modellenmişse aynı açık başka bir action, endpoint veya object relationship üzerinden tekrar ortaya çıkar.
Kalıcı çözüm için önce sistemin hangi soruya cevap verdiği netleşmelidir. Güvenli soru yalnızca “Bu user bu object'e erişebilir mi?” değildir. Doğru karar çoğu zaman şuna benzer:
Kısa cevap
Bu subject, bu tenant context'i ve mevcut object state'i içinde, bu object üzerinde istenen action'ı şimdi gerçekleştirebilir mi?
Bu cümledeki her parça önemlidir. Bunlardan biri kaybolduğunda authorization modeli gereğinden geniş bir allow kararı üretebilir.
Tek bir canAccess boolean'ına fazla anlam yüklemek
canAccess(document) gibi bir helper başlangıçta pratik görünebilir. Ancak read, update, share, download, approve ve delete aynı yetki değildir. User ekranda document başlığını görebilir fakat original file'ı indiremeyebilir. Shared user comment ekleyebilir fakat ownership transfer edemeyebilir.
Bir action için verilen true sonucu bütün action'lara taşındığında function-level ve object-level authorization birbirine karışır. Policy kararının en az subject, object ve action'ı açık biçimde alması gerekir. Tenant, object state, authentication strength veya request channel gibi context alanları riskli işlemlerde bu modele eklenir.
Önce object'i getirip sonra kontrol etmek
findById(id) çağrısından sonra ownership kontrolü yapmak her zaman yanlış değildir. Sorun, kontrol gerçekleşmeden önce data'nın serialize edilmesi, cache'e yazılması, audit event üretilmesi veya başka bir service'e gönderilmesidir. Lazy loading ve debug logging de beklenmeyen sızıntılar oluşturabilir.
Daha güvenli yaklaşım object'i baştan authorized scope içinde bulmaktır. Bunun mümkün olmadığı mimarilerde authorization check herhangi bir observable side effect'ten önce çalışmalıdır. “En sonunda deny ettik” ifadesi, arada hassas veri başka bir katmana taşındıysa yeterli değildir.
Parent kontrolünü child object için yeterli saymak
User'ın bir project'i görmesi, project altındaki her secret, attachment, invoice veya audit record'u görebileceği anlamına gelmez. Child object farklı classification, owner, sharing rule veya retention state taşıyabilir.
Tersi de görülebilir. User'a tek bir file paylaşılmıştır fakat uygulama önce parent folder membership aradığı için meşru erişimi reddeder. Yani relationship zinciri hem gereğinden geniş allow hem de hatalı deny üretebilir. Policy, business modelde gerçekten var olan ilişkiyi kontrol etmelidir.
Ownership eşitliğini bütün yetki modelinin yerine koymak
object.ownerId == currentUser.id bazı personal object'ler için doğru kontroldür. Team-owned, delegated, jointly managed veya tenant-owned object'lerde ise eksik kalır. Bir support agent belirli koşullarda masked view yetkisine sahip olabilir. Finance team invoice approve edebilir fakat beneficiary bilgisini değiştiremez. Eski owner şirketten ayrıldığında object yeni owner'a devredilmiş olabilir.
Ownership önemli bir attribute'tur, fakat tek olası relationship değildir. Shared access, team membership, delegation, object state ve separation of duties gibi kurallar açıkça modellenmelidir.
Policy Enforcement Point ile Policy Decision Point'in ayrışması
Central authorization service doğru karar verse bile endpoint bu kararı çağırmıyorsa koruma yoktur. Diğer yönde, gateway genel bir role kontrolü yaparken domain service daha ayrıntılı object relationship'ini bilmiyor olabilir.
Mimaride kararın nerede üretildiği ve nerede enforce edildiği ayrı ayrı tanımlanmalıdır. Yeni entry point açıldığında hangi enforcement point'in zorunlu olduğu code review checklist'inde görünmelidir. REST endpoint, GraphQL resolver, background worker ve admin tool aynı policy'yi farklı adapter'lar üzerinden kullanabilmelidir.
Internal service'i otomatik olarak güvenilir saymak
Bir service account geniş database erişimine sahip olabilir. Bu, o service'e user adına gelen her talebin yetkili olduğu anlamına gelmez. Downstream service yalnızca caller service'in identity'sini görür ve original subject, tenant veya action context'ini doğrulayamazsa confused deputy problemi doğar.
User context'inin services arasında taşınması imzalı ve doğrulanabilir olmalıdır. Downstream taraf yalnızca gelen tenantId alanına güvenmemeli, caller'ın bu context'i temsil etmeye yetkili olup olmadığını da kontrol etmelidir. Service-to-service authentication, end-user authorization'ın yerine geçmez.
Eski authorization kararını süresiz kullanmak
Membership revoke edilmiş, object başka tenant'a taşınmış veya sharing link kapatılmış olabilir. Buna rağmen long-running job, session claim ya da cache eski allow kararını kullanıyorsa authorization state güncel değildir.
Kararın ne kadar süreyle geçerli olduğu riske göre belirlenmelidir. Para transferi, secret görüntüleme ve ownership transfer gibi action'larda execution anında yeniden değerlendirme gerekebilir. Policy change ile cache invalidation arasındaki pencere de threat model içinde ele alınmalıdır.
Support ve superuser bypass'larını görünmez bırakmak
isAdmin veya isSupport kontrolüyle bütün policy'leri bypass etmek kolaydır. Bu ayrıcalık production troubleshooting sırasında eklenir ve zamanla kalıcı bir arka kapıya dönüşebilir.
Support access için amaç, kapsam, süre, approval, masking ve audit koşulları belirlenmelidir. Break-glass akışı normal authorization modelinden ayrı olsa bile ölçülebilir ve sonradan incelenebilir olmalıdır. Bir role'ün teknik olarak her object'i açabilmesi, business olarak bunu yapmaya yetkili olduğu anlamına gelmez.
Yalnızca allow ve deny sonucunu kaydetmek
Bir authorization kararı incelenirken “neden allow oldu?” sorusu en az kararın kendisi kadar değerlidir. Policy ID, rule version, subject relationship ve context bilgisi tutulmadığında incident sırasında karar yeniden üretilemez.
Log hassas veriyi çoğaltmamalıdır. Yine de correlation ID, object type, güvenli object reference, action, decision, policy version ve reason category gibi alanlar bulunmalıdır. Böylece yanlış policy, eksik enforcement ve kötüye kullanım birbirinden ayrılabilir.
IDOR test metodolojisi nasıl olmalı?
Kaliteli test, tek account ve rastgele ID değişikliğinden daha fazlasını gerektirir.
1. Authorization matrix hazırlanır
En az şu boyutlar belirlenir:
- Subject veya role
- Tenant
- Object type
- Object owner
- Object state
- Action
- Entry point
- Expected decision
Örnek:
| Subject | Object | İlişki | Action | Beklenen sonuç |
|---|---|---|---|---|
| User A | Document A | Owner | Read | Allow |
| User A | Document B | Same tenant, not shared | Read | Deny |
| User A | Document C | Other tenant | Read | Deny |
| User A | Document D | Shared read-only | Read | Allow |
| User A | Document D | Shared read-only | Update | Deny |
| Support | Document A | Support access | Masked preview | Allow |
| Support | Document A | Support access | Raw download | Deny |
Matrix olmadan tester response'un doğru mu yanlış mı olduğunu tahmin eder.
2. En az iki user ve iki tenant kullanılır
OWASP IDOR Prevention Cheat Sheet farklı authorization scope'lara sahip birden fazla account ve object ile test yapılmasını önerir.
Minimum test data seti şu profilleri içerebilir:
- User A, Tenant A
- User B, Tenant A
- User C, Tenant B
- Privileged User, Tenant A
- Disabled veya membership'i kaldırılmış user
- Shared object ve unshared object
- Archived veya soft-deleted object
Tek admin account ile object-level authorization test edilemez.
3. Object reference inventory çıkarılır
Tester yalnızca visible URL'lere bakmaz. Request path, query, body, header, GraphQL variable, WebSocket message ve background operation reference'ları kaydeder.
Object ID'nin başka endpoint'lerden nasıl elde edilebildiği de incelenir. Bir UUID guess edilemiyor olabilir, fakat list, search veya relationship response'unda sızıyor olabilir.
4. Her action ayrı test edilir
Şunlar ayrı ayrı değerlendirilmelidir:
- Read
- Update
- Delete
- Export
- Download
- Share
- Approve
- Cancel
- Restore
- Generate link
- Create child resource
- Bulk action
Read korunuyor diye delete'in korunduğu varsayılmaz.
5. HTTP method ve content type varyasyonları incelenir
Authorization filter yalnızca GET veya POST için bağlanmış olabilir. Aynı route PUT, PATCH, DELETE, HEAD veya alternative content type ile farklı handler'a düşebilir.
Test scope içinde framework routing ve method override davranışı da değerlendirilmelidir.
6. Alternative channel'lar test edilir
- REST
- GraphQL
- Mobile API
- WebSocket
- gRPC
- Admin panel
- Partner API
- Legacy API version
- Import ve export
- Background job sonucu
Bir channel'da doğru policy uygulanması diğerini güvenli yapmaz.
7. Negative authorization testleri otomatikleştirilir
Manual pentest discovery için güçlüdür. Bulunan policy expectation regression test'e çevrilmelidir.
Örnek test isimleri:
user_cannot_read_another_users_document
user_cannot_update_read_only_shared_document
tenant_a_cannot_export_tenant_b_invoice
revoked_member_cannot_reuse_signed_download
support_cannot_access_unmasked_customer_recordBu testler business language ile yazıldığında authorization requirement görünür hale gelir.
8. Gerçek customer data'sı gereksiz yere açılmaz
Production testinde object enumeration ve bulk download ciddi privacy etkisi oluşturabilir. Synthetic data, canary object ve controlled account kullanılmalıdır.
Unauthorized gerçek data görülürse minimum evidence alınmalı, bulk access durdurulmalı ve acil bildirim süreci işletilmelidir. Bir kaydın doğrulanması için yüzlerce kaydın indirilmesi gerekmez.
SAST, Secure Code Review ve pentest IDOR'u nasıl görür?
IDOR için tek test tekniği yeterli değildir.
SAST
Şunları bulabilir:
- Missing authorization annotation
- Global
findByIdkullanımı - User-controlled ID'nin repository lookup'a gitmesi
- Policy helper çağrısının eksikliği
- Unsafe direct storage access
- Kuruma özgü custom rule ile tenant filter eksikliği
Şunlarda zorlanabilir:
- Object ownership requirement
- Shared relationship
- Tenant membership
- Action ve object state ilişkisi
- Runtime policy configuration
- Cross-service context
- Business-specific exception
SAST'ın kapsamını ve sınırlarını SAST nedir, hangi açıkları bulur, hangilerini kaçırır? yazımızda ayrıntılı ele aldık.
Secure Code Review
Reviewer object retrieval, policy enforcement, tenant context, alternate entry point, cache ve background job boyunca authorization flow'u izleyebilir. Root cause ve aynı pattern'in diğer örneklerini bulabilir.
Manual review ile otomasyon arasındaki fark için Secure Code Review ile otomatik SAST taraması farkı rehberimize bakabilirsiniz.
Sızma testi
İki veya daha fazla account ve object kullanarak runtime'da unauthorized action'ı doğrular. API gateway, deployed configuration, cache, identity provider ve gerçek workflow birlikte test edilir.
Source review ile runtime exploitation'ın neden farklı evidence ürettiğini Kaynak kod analizi ile sızma testi aynı problemi çözmez: nerede ayrışır? yazımızda açıklıyoruz.
Birleşik model
Pentest bir endpoint'te cross-tenant document download bulur. Secure Code Review bunun unscoped repository method'undan kaynaklandığını ve yedi endpoint'i etkilediğini gösterir. Ardından custom SAST rule aynı unsafe method'un yeni kullanımını engeller. Negative integration test ise regression'ı yakalar.
Bu, tek bulguyu kalıcı security control'e dönüştüren modeldir.
IDOR raporu nasıl yazılmalı?
“ID değiştirildi, başka veri geldi” açıklaması teknik ve business karar için yetersizdir.
İyi bir rapor şu alanları içermelidir:
- Etkilenen endpoint ve method
- Object type
- Test edilen subject ve role
- Tenant context
- Object ownership veya relationship
- Authorized request
- Unauthorized request
- Maskelenmiş response evidence
- Read, write, delete veya action etkisi
- UUID'nin nasıl elde edildiği
- Bulk veya automation potansiyeli
- Cross-tenant olup olmadığı
- Root cause
- Etkilenen benzer endpoint'ler
- Business impact
- Remediation pattern
- Regression test önerisi
- Retest sonucu
Örnek attack path kaydı
| Alan | Değer |
|---|---|
| Subject | Tenant A içinde standard user |
| Hedef object | Tenant B'ye ait invoice |
| Entry point | GET /api/invoices/{invoiceId}/download |
| Identifier kaynağı | Search response içindeki leaked UUID |
| Eksik kontrol | Repository query yalnızca invoiceId kullanıyor |
| Sonuç | Cross-tenant PDF disclosure |
| Yayılım | Preview ve export endpoint'leri aynı helper'ı kullanıyor |
| Root cause | Tenant context repository contract'ında zorunlu değil |
| Systemic fix | Tenant-scoped repository ve action policy |
| Regression | İki tenant ile read, preview, export negative testleri |
IDOR severity nasıl belirlenmeli?
Her IDOR aynı riskte değildir. Sequential ID veya UUID kullanımı tek başına severity belirlemez.
Değerlendirme faktörleri şunlardır:
- Authentication gerekiyor mu?
- Hangi role gerekiyor?
- Cross-user mı, cross-tenant mı?
- Data ne kadar hassas?
- Read, update, delete veya approve mümkün mü?
- Object ID nasıl elde ediliyor?
- Bulk operation var mı?
- Enumeration ne kadar kolay?
- Rate limit var mı?
- Tek object mi, bütün dataset mi etkileniyor?
- Action financial veya physical impact üretiyor mu?
- Account takeover zinciri oluşuyor mu?
- Audit log ve detection var mı?
- Share, revoke ve lifecycle davranışı nasıl?
- Aynı root cause kaç endpoint'i etkiliyor?
Örnekler:
- Public profile picture üzerinde düşük etkili cross-user read, düşük veya orta risk olabilir.
- Cross-tenant invoice ve contract download yüksek risk taşır.
- Başka user'ın MFA device'ını silme veya e-mail değişikliğini approve etme account takeover'a dönüşebilir.
- Başka tenant'ın backup'ını delete etme critical business impact oluşturabilir.
Risk yalnızca dönen byte sayısıyla değil object'in işleviyle belirlenir.
Production'da IDOR bulunduğunda ne yapılmalı?
Production'da doğrulanan bir IDOR yalnızca backlog'a eklenecek application bug'ı değildir. Başka user veya tenant object'lerine izinsiz erişim mümkünse olay, potansiyel bir security incident olarak ele alınmalıdır. Bunun anlamı her bulgunun veri ihlali ilan edilmesi değildir. Önce exposure, exploitation ve etki kontrollü biçimde araştırılır.
İyi bir müdahale akışı üç soruyu paralel ele alır:
- 1Yetkisiz erişim ihtimali şimdi nasıl durdurulur?
- 2Geçmişte ne olmuş olabilir?
- 3Aynı root cause başka nerelerde bulunuyor?
Önce erişim yolu sınırlandırılır
Kalıcı code fix hazırlanırken savunmasız endpoint açık bırakılmamalıdır. Uygun containment seçeneği sistemin işlevine göre değişir:
- Riskli endpoint'i veya yalnızca problemli action'ı geçici olarak kapatmak
- Export, bulk download veya sharing özelliğini feature flag ile durdurmak
- Gateway üzerinde dar bir allowlist veya ek policy uygulamak
- İlgili role'ün hassas action yetkisini geçici olarak azaltmak
- Signed URL ve access token'ları revoke etmek
- Object enumeration hızını düşürmek için geçici rate limit eklemek
Geçici kontrol, kalıcı authorization fix'i yerine geçmez. WAF üzerinde ID formatı engellemek veya response'u 404 yapmak tek başına containment sayılmaz. Seçilen kontrolün gerçek unauthorized action'ı durdurduğu iki farklı user ve mümkünse iki farklı tenant ile doğrulanmalıdır.
Etkilenen object ve zaman aralığı belirlenir
İnceleme yalnızca pentest sırasında kullanılan object'e odaklanmamalıdır. Savunmasız code ne zaman production'a çıktı, hangi endpoint ve method'lar aynı helper'ı kullanıyor, hangi object type'lar erişilebildi, identifier nerelerden elde edilebildi gibi sorular cevaplanmalıdır.
Access log tek başına yeterli olmayabilir. API gateway log'ları, application audit trail, object storage access, CDN, cache, background job ve database audit kayıtları correlation ID ve zaman bilgisiyle birleştirilebilir. Özellikle aşağıdaki örüntüler incelenebilir:
- Aynı subject'in kısa sürede çok sayıda foreign object istemesi
- User'ın normalde ilişkili olmadığı tenant'lara ait object erişimleri
- Peş peşe değişen numeric ID veya farklı UUID denemeleri
- Listing izni olmadan direct download çağrıları
- Read sonrasında export, share, update veya delete action'ı
- Aynı foreign object'in birden çok entry point üzerinden denenmesi
Loglarda object ownership veya authorization decision bulunmuyorsa geçmişe dönük kesin hüküm vermek zorlaşır. “Logda hata yok, exploitation yoktur” sonucu çıkarılamaz. Bulguların güven seviyesi ve telemetry boşlukları incident kaydında açıkça belirtilmelidir.
Evidence korunur, fakat yeni bir veri riski yaratılmaz
İnceleme amacıyla bütün customer verisini toplu biçimde export etmek doğru değildir. Gerekli minimum kayıt korunmalı, access sınırlandırılmalı ve chain of custody ihtiyacı varsa belgelenmelidir. Request ve response örneklerinde token, personal data ve secret değerler maskelenmelidir.
Privacy, legal ve ilgili business owner'lar olayın kapsamına göre sürece dahil edilir. Bildirim yükümlülüğü teknik ekibin tek başına varsayacağı bir konu değildir. Değerlendirme doğrulanmış erişim, etkilenmiş veri kategorisi, jurisdiction ve sözleşmesel yükümlülükler üzerinden yapılır.
Credential rotation otomatik refleks olmamalı
IDOR çoğu zaman geçerli bir session'ın yanlış object'e erişmesidir. Bu nedenle bütün user password'lerini değiştirmek root cause'u ortadan kaldırmaz. Ancak açığa çıkan object içinde API key, reset token, private key, session secret veya reusable signed URL varsa ilgili secret'lar hızla revoke edilmeli ve rotate edilmelidir.
Başka user'ın e-mail adresini değiştirme, MFA device silme veya token üretme gibi write etkileri varsa account takeover olasılığı ayrıca araştırılmalıdır. Unauthorized state change geri alınırken meşru user işlemlerinin üzerine yazılmamasına dikkat edilmelidir.
Tek endpoint değil, root cause kapatılır
Savunmasız handler'a ownership check eklemek ilk adımdır. Aynı unscoped repository method'unu, resolver pattern'ini, file mapping'ini veya service client'ını kullanan bütün entry point'ler aranmalıdır. Fix sonrasında read, create, update, delete, download, share, approve ve export action'ları negative testlerle doğrulanmalıdır.
Incident ancak containment, systemic remediation, retrospective analysis, regression test ve monitoring birlikte tamamlandığında kapatılmalıdır. Retest yalnızca ilk proof of concept'i değil olası bypass ve sibling endpoint'leri de kapsamalıdır.
IDOR remediation güvenle nasıl production'a alınır?
Authorization fix'leri yüksek etkiye sahiptir. Çok dar bir policy meşru user'ları engelleyebilir. Çok geniş bir compatibility istisnası ise açığı yeniden üretir. Bu yüzden rollout, hem security hem de availability açısından planlanmalıdır.
Policy değişikliği gözlemlenebilir olmalı
Yeni policy önce production dışı ortamda gerçek ilişki çeşitlerini temsil eden test data ile doğrulanmalıdır. Gerekirse kısa süreli comparison mode kullanılabilir. Eski ve yeni kararlar aynı request için hesaplanır, fakat yalnızca mevcut güvenli karar enforce edilir. Farklar telemetry'ye yazılır ve beklenmeyen allow ile deny sonuçları incelenir.
Shadow mode riskli bir allow kararını production'da uygulamak için gerekçe olamaz. Açık aktifse containment devam eder. Shadow sonuçları yalnızca yeni policy'nin coverage ve compatibility etkisini görmek için kullanılır.
Rollout küçük ve geri dönüşü güvenli olmalı
Değişiklik önce internal account, synthetic canary veya düşük riskli tenant grubunda gözlemlenebilir. Deny rate, error rate, latency, support ticket ve kritik business action başarı oranı izlenir. Sonra kapsam kontrollü olarak genişletilir.
Rollback planı eski savunmasız davranışı geri getirmemelidir. Sorun çıkarsa feature'ı kapatmak, read-only moda almak veya daha dar bir geçici allowlist kullanmak mümkün olmalıdır. “Authorization hata verirse herkese izin ver” yaklaşımı recovery planı değildir.
Policy service outage durumunda fail-open yapılmamalı
Central policy engine erişilemez olduğunda hassas action'ların varsayılan sonucu deny olmalıdır. Availability gereksinimi yüksek sistemlerde local verified policy bundle, kısa süreli güvenli cache veya degraded read-only mode tasarlanabilir. Fakat timeout'u otomatik allow sonucuna çevirmek saldırgana policy service'i devre dışı bırakarak erişim kazanma yolu açar.
Cache ve asynchronous işler rollout kapsamına alınmalı
Code deploy edilse bile eski authorized response cache'te kalabilir. CDN, application cache, signed URL ve permission decision cache invalidation planı fix ile birlikte çalışmalıdır.
Queue'da bekleyen job'lar eski subject context'i veya policy version'ı taşıyabilir. Riskli action execution anında yeniden authorize edilmeli ya da eski policy version'lı mesajlar kontrollü biçimde durdurulmalıdır. Retry mekanizması unauthorized action'ı tekrar tekrar denememelidir.
Legacy client uyumluluğu authorization istisnasına dönüşmemeli
Eski mobile client tenant context göndermiyor veya deprecated endpoint kullanıyor olabilir. Uyumluluk için server'ın object'i global ID ile bulup erişime açması kabul edilemez. Gerekirse migration window, minimum client version ve endpoint deprecation planı uygulanır.
Policy ve API contract version'ları ilişkilendirildiğinde hangi client'ın hangi kurala tabi olduğu izlenebilir. Geçici istisnaların owner'ı, bitiş tarihi ve telemetry'si olmalıdır.
Deployment sonrası negative canary çalıştırılmalı
Health check yalnızca 200 OK aramamalıdır. Synthetic User A'nın User B object'ini okuyamadığını, Tenant A'nın Tenant B export'unu başlatamadığını ve revoked user'ın cached URL'yi kullanamadığını düzenli olarak kontrol eden negative canary daha değerlidir.
İyi rollout'un başarı ölçütü yalnızca hata oranının artmaması değildir. Beklenen allow akışları çalışırken beklenen deny kararlarının gerçekten enforce edildiği de kanıtlanmalıdır.
IDOR için monitoring ve detection nasıl olmalı?
Prevention asıl kontroldür. Monitoring, başarısız ve şüpheli denemeleri görünür hale getirir.
Authorization decision log'u şu alanları taşıyabilir:
- Subject ID
- Tenant ID
- Object type
- Object ID'nin güvenli temsil biçimi
- Action
- Decision
- Policy veya rule ID
- Deny reason category
- Entry point
- Correlation ID
- Authentication strength
- Timestamp
Sensitive object ID ve personal data log'a gereksiz yere yazılmamalıdır. Hash veya internal surrogate kullanılabilir.
Şüpheli sinyaller şunlardır:
- Kısa sürede çok sayıda farklı object ID denemesi
- Tek user'ın çok sayıda deny alması
- Cross-tenant reference denemeleri
- Sequential ID pattern'i
- Read sonrası hızlı bulk export
- Birden fazla endpoint'te aynı foreign object'in denenmesi
- Revoked user'ın signed URL kullanımı
- Support role'ün olağan dışı raw data erişimi
- Service account'ın beklenmeyen tenant'lara erişmesi
Rate limit ve alert IDOR'u önlemez. Access control başarısız olduğunda blast radius'u ve dwell time'ı azaltabilir.
Yanlış düzeltmeler neden işe yaramaz?
“ID'leri UUID yaptık”
Guessing zorlaşır. Authorization eksikliği kalır.
“ID'yi Base64 encode ettik”
Encoding geri çevrilebilir. Access control sağlamaz.
“URL'yi frontend'den kaldırdık”
Endpoint hâlâ çağrılabilir. Server-side enforcement gerekir.
“Endpoint login istiyor”
Authentication subject'i belirler. Object permission'ını belirlemez.
“Role kontrolü var”
Role function'a erişimi gösterebilir. Object ownership ve relationship ayrıca gerekir.
“WAF engeller”
IDOR request'i syntactically normal ve authenticated olabilir. WAF business ownership'i bilemez.
“Rate limit ekledik”
Bulk abuse'u yavaşlatabilir. Tek kritik object access'i yine başarılı olur.
“Unauthorized object için 404 dönüyoruz”
Resource existence sızıntısını azaltabilir. Data dönmeye devam ediyorsa status code çözüm değildir.
“Signed URL kullanıyoruz”
Generation, scope, subject binding, expiry ve revocation doğru değilse URL paylaşımı unauthorized access üretir.
“Controller'da check var”
GraphQL, background job, admin path veya başka service repository'ye doğrudan erişebilir. Bütün entry point'ler kapsanmalıdır.
IDOR prevention için development standard nasıl kurulmalı?
Security requirement'ı açık yazın
“Endpoint authorize edilmeli” yerine ölçülebilir requirement kullanın:
Kısa cevap
Tenant içindeki standard user yalnızca owner olduğu veya aktif read share aldığı document'ları görüntüleyebilir. Başka tenant object'leri identifier bilinse dahi görünmemelidir.
Authorization matrix'i code'dan önce oluşturun
Role × object × action × state × relationship matrisi design aşamasında belirlenmelidir. Matrix policy ve test case'lere kaynak olur.
Safe-by-default abstraction üretin
- Tenant-scoped repository
requireAuthorizedObject()helper- Policy middleware
- Resolver authorization wrapper
- Authorized file access service
- Queue subject context envelope
- Cache namespace standardı
Developer'ın her endpoint'te aynı kontrolü sıfırdan yazması beklenmemelidir.
Unsafe method'ları görünür veya yasak hale getirin
Global findById, direct storage get veya bypass policy method'ları restricted API olabilir. Kullanım code owner review'i gerektirebilir.
Custom SAST rule bu method'ların application layer'da doğrudan kullanılmasını engelleyebilir.
Negative test template sağlayın
Her object endpoint'i için en az şu testler üretilebilir:
- Other user object
- Other tenant object
- Revoked membership
- Unauthorized action
- Wrong object state
- Bulk list içinde foreign object
- Alternate API version
- Background result access
Policy change'i security-sensitive kabul edin
Permission schema, membership, share, role ve object state değişiklikleri security review trigger'ı olmalıdır.
Retest'i yalnızca tek endpoint'te bırakmayın
Root cause ortak helper veya repository ise bütün caller'lar tekrar değerlendirilmelidir. Fix bir endpoint'e eklenen if ile sınırlı kalmamalıdır.
IDOR testinde satın alma kapsamı nasıl yazılmalı?
“OWASP Top 10 test edilecek” ifadesi object-level authorization coverage'ını garanti etmez.
Scope'ta şu bilgiler bulunmalıdır:
- User role listesi
- En az iki same-role account
- Birden fazla tenant account'ı
- Test edilecek object type'lar
- Object lifecycle state'leri
- Read, update, delete, export ve business action'lar
- REST, GraphQL, mobile, WebSocket ve partner API kapsamı
- Legacy API version'lar
- Bulk endpoint'ler
- File ve signed URL akışları
- Background job ve export sonucu
- Admin ve support panel
- Test data yöntemi
- Production data'ya dokunma sınırı
- Source code review dahil olup olmadığı
- Retest kapsamı
Yalnızca bir standard user account verilirse cross-user test yapılabilir, fakat cross-tenant ve role comparison eksik kalabilir. Yalnızca admin account verilmesi ise IDOR testini ciddi biçimde sınırlar.
IDOR neden scanner ile tamamen otomatik bulunamıyor?
Scanner object pair ve account pair bilgisine ihtiyaç duyar. Response farkını görmek yeterli değildir. İki response'un business açısından farklı permission'a tabi olduğunu bilmelidir.
Automation şu işlerde faydalıdır:
- Object reference toplama
- Account A request'ini Account B context'inde replay etme
- Response status, length ve schema karşılaştırma
- Method ve endpoint varyasyonu üretme
- Bulk list içinde foreign object deneme
- Regression suite çalıştırma
İnsan doğrulaması şu noktalarda gereklidir:
- Object ownership'i belirleme
- Expected policy'yi anlama
- Shared relationship'i yorumlama
- Sensitive data'yı tanıma
- Partial response'u değerlendirme
- Business action impact'ini ölçme
- Safe proof sınırını koruma
Otomasyon test coverage'ını artırır. Authorization oracle yoksa nihai kararı tek başına veremez.
Başarı nasıl ölçülmeli?
“Bu yıl kaç IDOR bulundu?” tek başına olgunluk göstergesi değildir.
Anlamlı metrikler şunlardır:
- Object type'ların authorization matrix coverage'ı
- Negative authorization test coverage'ı
- Cross-tenant test edilen endpoint oranı
- Central policy kullanan entry point oranı
- Unsafe global repository method kullanımı
- Authorization regression sayısı
- Aynı root cause'a bağlı tekrar sayısı
- Policy bypass exception sayısı ve yaşı
- Revoked access propagation süresi
- Signed URL maksimum lifetime
- Deny decision alert coverage'ı
- IDOR remediation lead time
- Pentest bulgusundan custom rule veya regression test'e dönüşüm oranı
- Production'a kaçan authorization finding sayısı
Amaç deny sayısını artırmak değil, policy expectation ile runtime decision arasındaki farkı azaltmaktır.
Sonuç: IDOR basit bir bug değil, authorization garantisinin kırılmasıdır
IDOR'un açıklaması basittir: User object ID'yi değiştirir ve başka kaynağa ulaşır.
Onu kalıcı biçimde önlemek ise basit değildir. Çünkü modern application tek controller ve tek database query'den oluşmaz. Aynı object REST API, GraphQL, mobile client, admin panel, cache, file storage, background worker, export service ve third-party integration üzerinden erişilebilir.
Bu nedenle IDOR hâlâ yaygındır.
Saldırganın bir eksik check bulması yeterlidir. Güvenli application'ın bütün access path'lerde doğru karar vermesi gerekir.
Kalıcı çözüm identifier'ı gizlemek değil authorization'ı architecture özelliği haline getirmektir:
- 1Authentication ile authorization'ı ayrı kontrol olarak ele alın.
- 2Subject, object, action ve context'i her request'te değerlendirin.
- 3Deny by default uygulayın.
- 4Tenant context'i request'ten değil verified identity ve membership'ten üretin.
- 5Global
findByIdyerine authorized dataset ve tenant-scoped repository kullanın. - 6REST, GraphQL, cache, file, queue ve background job'larda aynı policy garantisini koruyun.
- 7UUID ve signed reference'ları yalnızca defense in depth olarak değerlendirin.
- 8İki user ve iki tenant ile negative authorization testleri yazın.
- 9Pentest bulgusunu Secure Code Review ile root cause'a bağlayın.
- 10Tekrarlanan unsafe pattern'i custom SAST rule ve regression test'e dönüştürün.
Secnodex, IDOR testini yalnızca URL'deki sayıyı değiştiren bir kontrol olarak ele almaz. Role, tenant, object, action ve lifecycle matrix'i üzerinden REST, GraphQL, mobile API, file access, export ve background process path'lerini inceler. Bulguyu runtime evidence ile doğrular, source code erişimi varsa root cause ve etkilenen diğer endpoint'leri ortaya çıkarır. Uygulamanızın object-level authorization modelini değerlendirmek için Secnodex ile iletişime geçebilirsiniz.
Kaynaklar
- OWASP Top 10:2025 – A01 Broken Access Control
- OWASP API Security Top 10:2023 – API1 Broken Object Level Authorization
- OWASP Insecure Direct Object Reference Prevention Cheat Sheet
- OWASP Authorization Cheat Sheet
- OWASP Multi-Tenant Security Cheat Sheet
- OWASP GraphQL Cheat Sheet
- OWASP WSTG v4.2 – Testing for Insecure Direct Object References
- OWASP Application Security Verification Standard 5.0.0
- MITRE CWE-639: Authorization Bypass Through User-Controlled Key
- MITRE CWE-862: Missing Authorization
- MITRE CWE-863: Incorrect Authorization
Sık sorulan sorular
IDOR açığı nedir?
IDOR, application'ın user-controlled object reference ile bir resource'a ulaşırken request'i yapan subject'in o object üzerindeki action'a yetkili olup olmadığını yeterli biçimde kontrol etmemesidir.
IDOR açığı neden hâlâ bu kadar yaygın?
Çünkü object-level authorization'ın her object, action, role, tenant, state ve entry point'te tutarlı uygulanması gerekir. Tek bir unutulmuş endpoint, background job, cache veya legacy API bütün modeli bozabilir.
IDOR ile BOLA aynı mı?
Çok büyük ölçüde örtüşür. BOLA, API'lerde object-level authorization eksikliğini daha doğrudan tanımlar. IDOR daha eski ve web security içinde yaygın kullanılan terimdir.
UUID kullanmak IDOR'u önler mi?
Hayır. UUID guess etmeyi zorlaştırabilir, fakat identifier başka yoldan sızdığında unauthorized access devam eder. Server her request'te authorization yapmalıdır.
IDOR için kullanıcının login olması gerekir mi?
Çoğu IDOR authenticated user context'inde görülür, çünkü problem bir user'ın başka user veya tenant object'ine erişmesidir. Unauthenticated object access de Broken Access Control kapsamında olabilir.
IDOR yalnızca data okuma açığı mıdır?
Hayır. Update, delete, approve, cancel, export, share, restore ve signed URL üretme gibi action'lar da etkilenebilir. Integrity ve availability etkisi data disclosure'dan daha ağır olabilir.
IDOR horizontal privilege escalation mıdır?
Klasik IDOR çoğunlukla aynı role seviyesindeki user'lar arasında horizontal authorization bypass üretir. Cross-tenant ve bazı vertical etkiler de oluşabilir.
WAF IDOR'u engeller mi?
Genellikle hayır. Request geçerli syntax, valid session ve normal object ID içerir. Object ownership gibi business context WAF tarafından güvenilir biçimde bilinmez.
SAST IDOR bulabilir mi?
Missing annotation, unsafe findById, user-controlled lookup ve eksik custom policy pattern'lerini bulabilir. Fakat ownership, tenant relationship ve action context için manual validation gerekir.
IDOR pentestte nasıl doğrulanır?
En az iki farklı authorization scope'a sahip account ve bunlara ait synthetic object'ler kullanılır. Account A context'inde Account B veya başka tenant object'i üzerinde read, update, delete, export ve ilgili business action'lar kontrollü biçimde denenir.
`403` yerine `404` dönmek IDOR'u çözer mi?
Hayır. 404 resource existence bilgisini saklamak için tercih edilebilir. Temel çözüm unauthorized object'e data veya action erişimi vermemektir.
Signed URL IDOR riskini kaldırır mı?
Hayır. URL generation anında authorization, kısa expiry, narrow action, subject veya tenant binding ve gerekirse revocation tasarlanmalıdır. URL'nin ele geçirilmesi ya da paylaşılması senaryosu ayrıca değerlendirilir.
RBAC IDOR'u önlemek için yeterli mi?
Tek başına çoğu zaman yeterli değildir. RBAC function-level permission için yararlıdır. Object ownership, tenant membership, share ve state gibi ilişkiler ABAC, ReBAC veya domain-specific policy gerektirebilir.
Multi-tenant application'da en önemli kontrol nedir?
Verified tenant context ve her data access path'inde tenant-scoped authorization temel gereksinimdir. Query, cache, search, file storage, queue ve export katmanları aynı boundary'yi korumalıdır.
IDOR düzeltildikten sonra ne test edilmeli?
Yalnızca bulunan endpoint değil, aynı repository, helper ve object type'ı kullanan bütün read, update, delete, export ve background path'ler test edilmelidir. Negative regression test eklenmelidir.
IDOR bulgusu her zaman Critical mıdır?
Hayır. Data sensitivity, action, cross-tenant etki, scale, identifier discovery, required privilege ve chained impact birlikte değerlendirilir. Başka user'ın public olmayan verisini okumak ile backup delete etmek aynı riskte değildir.
Okumaya devam et
Kaynak Kod Analizi
SAST Nedir, Hangi Açıkları Bulur, Hangilerini Kaçırır?
SAST, uygulama çalıştırılmadan source code, bytecode veya binary üzerinde güvenlik analizi yapar. Injection ve riskli API kullanımında güçlüdür; ancak business logic, IDOR ve runtime configuration gibi kritik riskleri tek başına kapsayamaz.
Yazıyı okuKaynak Kod Analizi
Kaynak Kod Analizi ile Sızma Testi Aynı Problemi Çözmez: Nerede Ayrışır?
Kaynak kod analizi uygulamanın nasıl yazıldığını, sızma testi ise çalışan sistemde neyin gerçekten istismar edilebildiğini inceler. Aralarındaki farkı teknik örneklerle ele alıyoruz.
Yazıyı oku