Cross Origin Resource Sharing
Last updated
Last updated
Herkese selam :)
CORS yani Cross Origin Resource Sharing, web uygulamaları üzerinde güvenlik önlemi açısından yaygın olarak kullanılan bir mekanizmadır. Temel olarak, bir web uygulamasının kaynaklarına başka bir port veya domainden erişilip erişilemeyeceğini kontrol eder. Frontend Development üzerine uğraşmış ancak client-server mimarisine pek hakim olmayan geliştiricilerin en az bir kere karşılaştığı bu hatadan bahsedeceğim bugün. Keyifli okumalar dilerim :)
İnternetin erken dönemlerinde, tarayıcılar herhangi bir güvenlik politikası uygulamıyor ve web sayfaları farklı originlerden gelen kaynaklara erişebiliyordu. Bu durum web güvenliği için ciddi bir tehdit oluşturuyordu.
Özellikle JavaScript gibi dinamik içeriklerin yaygın olarak kullanıldığı modern web uygulamalarında, bir web sayfasının bir kaynağına erişim yetkisi, diğer kaynaklara erişim yetkisi anlamına geliyordu. Bu durum, kötü niyetli aktörlerin kullanıcıların bilgilerine ve tarayıcılarında saklanan çerezlere erişim kazanabilecekleri güvenlik açıklarını ortaya çıkarıyordu.
Bu nedenle, tarayıcı geliştiricileri ve web standartları kuruluşları, bu güvenlik riskini azaltmak için yeni bir mekanizma geliştirmeye başladılar. Bu mekanizma, farklı kökenlerden gelen kaynaklara erişim izni sağlayacak, ancak güvenlik açıklarını kapatmaya yardımcı olacak şekilde tasarlanmalıydı.
2004 yılında, Microsoft, Internet Explorer tarayıcısına özel bir özellik olarak ekledi. Ancak, daha sonraki yıllarda diğer tarayıcılar da bu özelliği benimsedi. 2014 yılında, W3C (World Wide Web Consortium), CORS'u resmi bir standart olarak kabul etti ve bu, tüm modern tarayıcıların CORS'u desteklemesiyle sonuçlandı.
CORS'un ortaya çıkışıyla birlikte, web geliştiricileri farklı originlerden gelen kaynaklara erişim izni vermek ve aynı zamanda güvenlik açıklarını en aza indirmek için standart bir yönteme sahip oldular. Bu, web uygulamalarının daha güvenli ve etkili bir şekilde çalışmasına olanak tanıdı.
CORS üzerine konuşmadan önce SOP'un ne olduğu hakkında biraz konuşalım.
Gelişen web teknolojileri ile birlikte güvenlik açıklarının da artması ile tarayıcılar SOP (Same Origin Policy) adı verdiğimiz bir politikayı da öne sürdü. SOP'a göre tarayıcılar, kullanıcıları korumak üzere, kullanıcı bir sitede gezinti yaparken ilgili uygulamanın başka bir uygulamaya istek atmasına sınırlama getirir. Bu, aynı origin'e sahip olmayan sistemler arasında kaynak paylaşımını kısıtlayan bir yapıdır.
SOP politikasına göre, yüklenen her bir kaynak
Şema/Protokol,
Domain (alan adı),
Port (bağlantı noktası)
birleşimi ile origin olarak tanımlanır ve sadece aynı origin'e sahip olan kaynaklar birbirleri ile paylaşıma geçebilirler.
Örnek olarak https://yavuzlarygb/ornek/deneme.html
şeklinde bir origin'imiz olsun. Bu origin'e göre
https://yavuzlarygb/ornek2/index.html
origininden gelen istekler kabul edilir. Çünkü yalnızca dosya yolu farklıdır.
http://yavuzlarygb/ornek/deneme.html
origininden gelen istekler kabul edilmez çünkü protokol farklıdır.
https://www.yavuzlarygb/ornek/deneme.html
origininden gelen istekler kabul edilmez çünkü domain farklıdır.
https://yavuzlarygb:8080/ornek/deneme.html
origininden gelen istekler kabul edilmez çünkü port numarası farklıdır.
Internet Explorer tarayıcısında SOP için özel bir durum vardır: IE, port (bağlantı noktası) origin kontrolünde politikaya dahil edilmez.
Ek olarak SOP farklı originler arasında istek atılmasını ortadan kaldırmaz, farklı bir originden dönen cevabın okunmasını engeller. SOP kısıtlaması olmasaydı, zararlı bir siteye girdiğimizde, o an oturumumuzun açık olduğu bir uygulamadaki bilgilerimiz kolayca okunabilir halde kalacaktı. SOP mimarisini kafanızda şekillendirebildiğinizi düşünüyorum bu yüzden burada SOP'u noktalıyorum.
SOP nedeniyle dönen cevapların okunamaması geliştiriciler için bir süre sonra sorun olmaya başladı. JSONP (JSON Padding) ile bu sorun ortadan kaldırılabilse de yazma işlemi yapılamadığından tarayıcılar, Cross Origin Resource Sharing kullanmaya başladılar. CORS sayesinde, SOP ile gelen katı kurallar kontrollü bir şekilde esnetilmiş ve SOP'un net kırmızı çizgileri ortadan kalkmış oldu. Kısaca modern web uygulamalarının sıkça ihtiyaç duyduğu kaynak paylaşımı sorununu ortadan kaldırmak için CORS geliştirildi.
Aşağıda CORS tarafından esneklik sağlanmamış bir origin'e istek atılması sonucunda, tarayıcının SOP kısıtlamalarından dolayı isteğe cevap veremediği bir durumun, tarayıcıdaki developer console üzerindeki hata mesajını görüyorsunuz.
Örnek olarak bir React uygulaması, farklı sunucuda bulunan bir API üzerinden veri almak istediğinde, bu sunucu üzerinde CORS ayarlarının yapılandırılması gerekir. Aksi halde API'ımızdan veri okunması pek de olası değildir.
CORS ayarları, sunucu tarafında yapılır. Kontrolü sağlanan veriler ise HTTP Başlıkları tarafından iletilir. Yaygın olarak kontrol edilen başlıklarda ise Access-Control-Allow-Origin, Access-Control-Allow-Methods ve Access-Control-Allow-Headers başlıkları bulunur. Bu başlıklar sırasıyla şu işlevleri yerine getirirler:
Access-Control-Allow-Origin: bu başlık hangi domainlere ve hangi portlara izin verildiğinin kontrolünden sorumludur. Örnek olarak "Access-Control-Allow-Origin: yavuzlar.org" olarak verilen bir başlık, sadece bu domain üzerinden gelen isteklere izin verir. Farklı domainler veya portlar eklemek için virgül ile ayrılmış bir liste verilebilir veya tüm domain ve portlara yıldız (*) karakteri ile izin verilebilir.
Access-Control-Allow-Methods: bu başlıkta hangi HTTP Request Metotları kabul edildiği kontrol edilir. Örnek olarak "Access-Control-Allow-Methods: GET, POST" olarak verilen bir başlık, sadece HTTP Get ve HTTP Post isteklerine yanıt verir.
Access-Control-Allow-Headers: bu başlık ise hangi HTTP Header'larına izin verildiğini kontrol eder. Örnek olarak "Access-Control-Allow-Headers: Content-Type" şeklinde verilen başlık, sadece content-type başlığına izin verir.
Bunlara ek olarak bir de bizim gönderdiğimiz HTTP Request'in header bilgileri kontrol edilir. Tarayıcıdan kaynağa yapılan bir istekte bu header, isteğin origin başlığında hangi kökten geldiğini kontrol eder.
Ön kontrol isteğine gerek kalmadan yapılan isteklerdir. Ön kontrol'ün ne demek olduğunu bir sonraki başlık altında açıklayacağım ancak bu isteklerde tek koşul ön kontrolün olmaması değil. Aynı zamanda metot olarak GET, HEAD ya da POST metotlarından birisi ile yapılması gerekmektedir. Ayrıca yalnızca basit istek başlıkları içermelidir (''Accept', 'Accept-Language', 'Content-Language', 'Content-Type' gibi).
Simple Request'lerin aksine, asıl istek gönderilmeden bir ön kontrol isteği gönderilmesi bu türde temel motivasyondur. Bu istek OPTIONS metodu ile yapılır. Yapılan istek sonucunda alınacak olan aksiyonun izinli olup olmadığı kontrol edilir. İzin verilirse asıl istek gönderilir, izin verilmeyen istekler tarayıcı tarafından engellenir.
Metot olarak GET, HEAD, POST dışında PUT, DELETE gibi metotlara da yer verilebilir. Ek olarak yalnızca basit başlıklar içermek zorunda değildir.
Diğer metotlara nazaran daha az bilinen bir diğer yöntem ise Credentialed Request'lerdir. Bu isteklerde tarayıcı isteği gönderirken, kaynaklara kimlik doğrulaması gönderilip gönderilmeyeceği bilgisini iletir. Ayrıca bu isteklerde "Access-Control-Allow-Credentials: true" başlığı olmalıdır.
Giriş kısmında ve yazının ilk paragraflarında da bahsettiğim SOP politikasını esneten bir yapı olduğundan dolayı CORS, bazı güvenlik açıklarını da beraberinde getirmektedir. CORS'un givensiz yapılandırılması sıkça karşılaşılan durumlar arasındadır.
İlk olarak yapılandırma yapılırken unutulmamalıdır ki, CORS, CSRF (Cross-site Request Forgery) gibi saldırılara karşı koruma sağlayan bir yapı değildir.
Bir diğer konu, gönderilen origin bilgisinin "Access-Control-Allow-Origin" başlık bilgisinin içerisinde yer alması durumudur. Origin'in kontrol edilmemesi ve “Access-Control-Allow-Credentials” ile kimliği doğrulanabilir içeriklerin erişilebilir olması, hedeflenen origin'in rastgele originlere güvenmesi sonucunda response'un kimliği doğrulanmış kişinin oturumunda yorumlanması ile sonuçlanabilir.
Origin bilgisinin hatalı bir şekilde kontrol edilmesi durumunda -ki bu URL öneki, URL soneki ya da regex ile yapılır- farkında olmadan istenilmeyen originlerin erişimine izin verilmiş olabilir.
Sıkça karşılaşılan bir diğer durum, CORS hatası alındıktan sonra origin kontrolüne gerekli hassasiyetin sağlanmayıp wildcard (*) parametresi ile tüm originlere izin verilmesi durumudur. Wildcard kullanılan bir durumda Access-Control-Allow-Credentials: true olsa bile credential'lar requeste eklenmeden gönderilmektedir. Saldırganın doğrudan erişim sağlayamadığı bir senaryoda ise hedef kişinin tarayıcısı bir proxy olarak kullanılabilir.
XSS (Cross Site Scripting) zafiyeti içeren bir site içeren senaryoda, CORS doğru yapılandırılmış olsa bile, saldırgan XSS üzerinden yazdığı payload'ı sisteme enjekte ederek CORS'un güvendiği origin'den hassas bilgileri okuyabilir.
Preflighted istekleri kullanın.
HTTP metotlarını ve başlıkları sınırlandırın. Yalnızca ihtiyacınız dahilinde erişmeniz gereken başlıklara ve metotlara izin verin.
Geliştirdiğiniz uygulama hassas veriler içeriyosa yerel ağlarda da dahil olmak üzere kesinlikle wildcard (*) kullanmaktan kaçının.
Access-Control-Allow-Origin başlığı NULL değerini alabilmektedir ancak whitelist'inizde null ifadesi kullanmayın.
CORS'un tarayıcı tarafında çalışan bir mekanizma olduğunu unutmayın. Bu, CORS sunucu tarafında hassas bilgileri koruyamaz demenin bir başka yolu.
Son olarak sunucularınızdaki CORS politikalarını düzenli olarak izleyin, inceleyin, ve güncelleyin. Yeni güvenlik tehditlerine karşı duyarlı kalın.
Yazının son kısmında neden bu yazıyı seçtiğimden biraz bahsedeyim :)
Frontend alanında neredeyse hiç tecrübem yokken Frontend Developer Intern olarak staja kabul edilmiştim. Stajın daha ilk günlerinde tanıştığım bu hatanın CORS olduğunu saatler sonra anlayacaktım (hatayı okumamıştım ve benden kaynaklandığını düşünüp çözmeye çalışmıştım) :) Bir zamanlar okuma alışkanlığım olduğunu pek söyleyemem ve benim gibi olan dostlarımın aynı hatayı yapmasını istemediğimden dolayı böyle bir konu seçtim.
Yukarda bahsettiğim dostlarım buraya kadar okumayacaktır muhtemelen ancak şans eseri gören "frontendci" arkadaşlar, CORS diye bir hata alırsanız yüksek ihtimalle sizden kaynaklanmıyor :)