Günümüzün web'inde zengin bir deneyim oluşturmak, neredeyse kaçınılmaz olarak gerçek bir kontrolünüz olmayan bileşenleri ve içerikleri yerleştirmeyi gerektirir. Üçüncü taraf widget'lar etkileşimi artırabilir ve genel kullanıcı deneyiminde kritik bir rol oynayabilir. Kullanıcı tarafından oluşturulan içerik bazen sitenin yerel içeriğinden bile daha önemlidir. İkisinden de kaçınmak pek mümkün değildir ancak her ikisi de sitenizde Kötü Bir Şey™ olma riskini artırır. Yerleştirdiğiniz her widget (her reklam, her sosyal medya widget'ı), kötü amaçlı kişiler için potansiyel bir saldırı vektörüdür:
İçerik Güvenliği Politikası (İGP), komut dosyası ve diğer içeriklerin özellikle güvenilir kaynaklarını izin verilenler listesine eklemenize olanak tanıyarak bu iki içerik türüyle ilişkili riskleri azaltabilir. Bu, doğru yönde atılmış önemli bir adımdır ancak çoğu CSP yönergesinin sunduğu korumanın ikili olduğunu belirtmekte fayda vardır: Kaynağa izin verilir veya verilmez. Bazen "Bu içerik kaynağına gerçekten güvendiğimden emin değilim ama çok güzel! Lütfen yerleştir Tarayıcı ama sitemi bozmasına izin verme."
En az ayrıcalık
Temel olarak, yerleştirdiğimiz içeriğe yalnızca işini yapması için gereken minimum düzeyde yetenek vermemizi sağlayacak bir mekanizma arıyoruz. Bir widget'ın yeni bir pencere açması gerekmiyorsa window.open erişimini kaldırmanın bir zararı olmaz. Flash gerektirmiyorsa eklenti desteğini kapatmak sorun olmayacaktır. En az ayrıcalık ilkesine uyup kullanmak istediğimiz işlevle doğrudan alakalı olmayan tüm özellikleri engellersek mümkün olduğunca güvende oluruz. Sonuç olarak, yerleştirilmiş bir içeriğin kullanmaması gereken ayrıcalıklardan yararlanmayacağına artık körü körüne güvenmek zorunda kalmıyoruz. Uygulama, işlevlere erişemez.
iframe öğeleri, bu tür bir çözüm için iyi bir çerçeve oluşturmanın ilk adımıdır.
Güvenilmeyen bir bileşeni iframe içine yüklemek, uygulamanız ile yüklemek istediğiniz içerik arasında bir miktar ayrım sağlar. Çerçeveli içerik, sayfanızın DOM'una veya yerel olarak depoladığınız verilere erişemez ve sayfada rastgele konumlara çizim yapamaz. Kapsamı, çerçevenin ana hatlarıyla sınırlıdır. Ancak bu ayrım gerçekten sağlam değildir. Kapsanan sayfa, rahatsız edici veya kötü amaçlı davranışlar için hâlâ bir dizi seçeneğe sahiptir: otomatik oynatılan video, eklentiler ve pop-up'lar buzdağının görünen kısmıdır.
iframe öğesinin sandbox özelliği
, çerçeveli içeriklerle ilgili kısıtlamaları sıkılaştırmak için tam olarak ihtiyacımız olan şeyi sağlar. Tarayıcıya, belirli bir çerçevenin içeriğini düşük ayrıcalıklı bir ortamda yüklemesi talimatını verebiliriz. Böylece, yalnızca yapılması gereken iş için gerekli olan özelliklerin alt kümesine izin verilir.
Güvenin ama doğrulayın
Twitter'ın "Tweet" düğmesi, korumalı alan aracılığıyla sitenize daha güvenli bir şekilde yerleştirilebilecek işlevlere iyi bir örnektir. Twitter, aşağıdaki kodla düğmeyi bir iframe aracılığıyla yerleştirmenize olanak tanır:
<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
Neleri kilitleyebileceğimizi anlamak için düğmenin hangi özelliklere ihtiyaç duyduğunu dikkatlice inceleyelim. Çerçeveye yüklenen HTML, Twitter'ın sunucularından bir miktar JavaScript yürütür ve tıklandığında tweet atma arayüzüyle doldurulmuş bir pop-up oluşturur. Bu arayüzün, tweet'i doğru hesaba bağlamak için Twitter'ın çerezlerine erişmesi ve tweet gönderme formunu gönderebilmesi gerekir. Hepsi bu kadar. Çerçevenin herhangi bir eklenti yüklemesi, üst düzey pencerede gezinmesi veya diğer işlevleri kullanması gerekmez. Bu ayrıcalıklara ihtiyacı olmadığı için çerçeve içeriğini korumalı alana alarak bunları kaldıralım.
Korumalı alan, izin verilenler listesi temelinde çalışır. Mümkün olan tüm izinleri kaldırarak başlarız ve ardından korumalı alanın yapılandırmasına belirli işaretler ekleyerek her bir özelliği tekrar etkinleştiririz. Twitter widget'ı için JavaScript, pop-up'lar, form gönderme ve twitter.com'un çerezlerini etkinleştirmeye karar verdik. Bunu yapmak için iframe öğesine aşağıdaki değere sahip bir sandbox özelliği ekleyebiliriz:
<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
src="https://platform.twitter.com/widgets/tweet_button.html"
style="border: 0; width:130px; height:20px;"></iframe>
Bu kadar basit. Çerçeveye ihtiyaç duyduğu tüm özellikleri verdik. Tarayıcı, sandbox özelliğinin değeri aracılığıyla açıkça vermediğimiz tüm ayrıcalıklara erişimi reddedecektir.
Yetenekler üzerinde ayrıntılı kontrol
Yukarıdaki örnekte olası korumalı alan işaretlerinden birkaçını gördük. Şimdi özelliğin iç işleyişini biraz daha ayrıntılı bir şekilde inceleyelim.
Boş bir korumalı alan özelliğine sahip bir iframe verildiğinde, çerçeveli doküman tamamen korumalı alana alınır ve aşağıdaki kısıtlamalara tabi olur:
- JavaScript, çerçeveli dokümanda yürütülmez. Bu, yalnızca komut dosyası etiketleri aracılığıyla açıkça yüklenen JavaScript'i değil, aynı zamanda satır içi etkinlik işleyicileri ve javascript: URL'lerini de içerir. Bu aynı zamanda noscript etiketlerinde yer alan içeriğin, kullanıcı komut dosyasını kendisi devre dışı bırakmış gibi görüntüleneceği anlamına da gelir.
- Çerçeveli doküman benzersiz bir kaynağa yüklenir. Bu, aynı kaynak kontrollerinin tümünün başarısız olacağı anlamına gelir. Benzersiz kaynaklar, kendileri de dahil olmak üzere başka hiçbir kaynakla eşleşmez. Bu durum, diğer etkilerin yanı sıra dokümanın herhangi bir kaynağın çerezlerinde veya diğer depolama mekanizmalarında (DOM depolama alanı, Indexed DB vb.) depolanan verilere erişemeyeceği anlamına gelir.
- Çerçeveli doküman yeni pencereler veya iletişim kutuları oluşturamaz (örneğin,
window.openveyatarget="_blank"aracılığıyla). - Formlar gönderilemez.
- Eklentiler yüklenmez.
- Çerçeveli doküman yalnızca kendi içinde gezinebilir, üst düzey üst öğesinde gezinemez.
window.top.locationayarı bir istisna oluşturur vetarget="_top"içeren bağlantıyı tıklamanın bir etkisi olmaz. - Otomatik olarak tetiklenen özellikler (otomatik odaklanan form öğeleri, otomatik oynatılan videolar vb.) engellenir.
- İşaretçi kilidi alınamıyor.
seamlessözelliği, çerçeveli dokümanın içerdiğiiframesüzerinde yoksayılır.
Bu, oldukça katı bir önlemdir ve tam korumalı alana yüklenen bir doküman iframe
gerçekten çok az risk oluşturur. Elbette bu durumda çok fazla değerli işlem de yapamaz: Bazı statik içerikler için tam korumalı alan kullanabilirsiniz ancak çoğu zaman kısıtlamaları biraz gevşetmek istersiniz.
Eklentiler hariç olmak üzere, bu kısıtlamaların her biri, korumalı alan özelliğinin değerine bir işaret eklenerek kaldırılabilir. Korumalı alandaki dokümanlar hiçbir zaman eklenti çalıştıramaz. Çünkü eklentiler, korumalı alanda olmayan yerel kodlardır. Ancak diğer her şey kullanılabilir:
allow-forms, form gönderimine izin verir.allow-popups, pop-up'lara izin verir.allow-pointer-lock, işaretçi kilidine izin verir.allow-same-origin, dokümanın kaynağını korumasına olanak tanır.https://example.com/kaynağından yüklenen sayfalar, bu kaynağın verilerine erişmeye devam eder.allow-scripts, JavaScript'in yürütülmesine ve özelliklerin otomatik olarak tetiklenmesine izin verir (JavaScript aracılığıyla kolayca uygulanabilirler).allow-top-navigation, üst düzey pencerede gezinerek dokümanın çerçeveden çıkmasına olanak tanır.
Bu bilgileri göz önünde bulundurarak, yukarıdaki Twitter örneğinde neden belirli bir korumalı alan işaretleri kümesi kullandığımızı tam olarak değerlendirebiliriz:
- Çerçeveye yüklenen sayfa, kullanıcı etkileşimini işlemek için JavaScript çalıştırdığından
allow-scriptsgereklidir. - Düğme, yeni bir pencerede tweet gönderme formu açtığı için
allow-popupsgereklidir. - Tweet gönderme formunun gönderilebilir olması gerektiğinden
allow-formsgereklidir. allow-same-origingereklidir. Aksi takdirde twitter.com'un çerezlerine erişilemez ve kullanıcı, formu göndermek için giriş yapamaz.
Bir çerçeveye uygulanan korumalı alan işaretlerinin, korumalı alanda oluşturulan tüm pencereler veya çerçeveler için de geçerli olduğunu unutmayın. Bu, form yalnızca çerçevenin açıldığı pencerede mevcut olsa bile çerçevenin korumalı alanına allow-forms eklememiz gerektiği anlamına gelir.
sandbox özelliği uygulandığında widget yalnızca gerekli izinleri alır ve eklentiler, üst gezinme ve işaretçi kilidi gibi özellikler engellenmeye devam eder. Widget'ı yerleştirme riskini azalttık ve herhangi bir olumsuz etki yaşanmadı.
Bu, ilgili herkes için bir kazançtır.
Ayrıcalık ayırma
Güvenilmeyen kodlarını düşük ayrıcalıklı bir ortamda çalıştırmak için üçüncü taraf içeriği korumalı alana almak oldukça faydalıdır. Peki kendi kodunuzu nasıl koruyacaksınız? Kendinize güveniyorsunuz, değil mi? Peki, korumalı alan neden önemlidir?
Bu soruyu tersine çevirelim: Kodunuzun eklentilere ihtiyacı yoksa neden eklentilere erişim izni verilsin? En iyi ihtimalle hiç kullanmadığınız bir ayrıcalık olur, en kötü ihtimalle ise saldırganların içeri sızmak için kullanabileceği potansiyel bir vektör haline gelir. Herkesin kodunda hatalar vardır ve neredeyse her uygulama bir şekilde kötüye kullanıma açıktır. Kendi kodunuzu korumalı alana almak, bir saldırgan uygulamanızı başarıyla alt etse bile uygulamanın kaynağına tam erişim izni verilmeyeceği anlamına gelir. Saldırgan yalnızca uygulamanın yapabileceği şeyleri yapabilir. Bu yine de kötü bir durumdur ancak olabileceğinden daha kötü değildir.
Uygulamanızı mantıksal parçalara bölerek ve her parçayı mümkün olan en az ayrıcalıkla korumalı alana alarak riski daha da azaltabilirsiniz. Bu teknik, yerel kodda çok yaygındır: Örneğin Chrome, kendisini yerel sabit diske erişebilen ve ağ bağlantıları kurabilen yüksek ayrıcalıklı bir tarayıcı işlemine ve güvenilmeyen içeriği ayrıştırma gibi ağır işleri yapan birçok düşük ayrıcalıklı oluşturucu işlemine böler. Oluşturucuların diske erişmesi gerekmez. Tarayıcı, sayfa oluşturmak için gereken tüm bilgileri oluşturuculara sağlar. Zeki bir bilgisayar korsanı, oluşturucuyu bozmanın bir yolunu bulsa bile çok fazla ilerleyemez. Çünkü oluşturucu tek başına çok fazla şey yapamaz: Tüm yüksek ayrıcalıklı erişimler tarayıcının işlemi üzerinden yönlendirilmelidir. Saldırganların herhangi bir zarar verebilmek için sistemin farklı parçalarında birkaç açık bulması gerekir. Bu da başarılı bir saldırı riskini büyük ölçüde azaltır.
eval() güvenli bir şekilde korumalı alana alınıyor
Korumalı alan ve postMessage API sayesinde bu modelin başarısını web'e uygulamak oldukça kolaydır. Uygulamanızın parçaları korumalı alana alınmış iframe öğelerinde bulunabilir ve üst doküman, mesaj gönderip yanıtları dinleyerek bunlar arasındaki iletişime aracılık edebilir. Bu tür bir yapı, uygulamanın herhangi bir parçasındaki güvenlik açıklarının mümkün olan en az zararı vermesini sağlar. Ayrıca, net entegrasyon noktaları oluşturmanızı zorunlu kılması da bir avantajdır. Böylece, giriş ve çıkışları doğrulama konusunda tam olarak nerede dikkatli olmanız gerektiğini bilirsiniz. Nasıl işlediğini görmek için
basit bir örnek üzerinden gidelim.
Evalbox, bir dizeyi alıp JavaScript olarak değerlendiren heyecan verici bir uygulamadır. İnanılmaz, değil mi? Yıllardır beklediğiniz o an geldi. Elbette bu oldukça tehlikeli bir uygulamadır. Rastgele JavaScript'in yürütülmesine izin vermek, bir kaynağın sunduğu tüm verilerin ele geçirilebileceği anlamına gelir. Kodun korumalı alanda yürütülmesini sağlayarak kötü durumların yaşanma riskini azaltırız. Bu sayede kod çok daha güvenli hale gelir. Kodu içten dışa doğru inceleyeceğiz. Çerçevenin içeriğiyle başlayalım:
<!-- frame.html -->
<!DOCTYPE html>
<html>
<head>
<title>Evalbox's Frame</title>
<script>
window.addEventListener('message', function (e) {
var mainWindow = e.source;
var result = '';
try {
result = eval(e.data);
} catch (e) {
result = 'eval() threw an exception.';
}
mainWindow.postMessage(result, event.origin);
});
</script>
</head>
</html>
Çerçevenin içinde, window nesnesinin message etkinliğine bağlanarak üst öğesinden gelen mesajları dinleyen minimal bir dokümanımız var.
Üst öğe, iframe'in içeriklerinde postMessage'ı her çalıştırdığında bu etkinlik tetiklenir ve üst öğemizin çalıştırmamızı istediği dizeye erişmemizi sağlar.
İşleyicide, etkinliğin üst pencere olan source özelliğini alırız. Bu adresi, çalışmamızın sonucunu size göndermek için kullanacağız. Ardından, bize verilen verileri eval()'e aktararak zorlu işleri biz hallederiz. Korumalı alana alınmış bir iframe içindeki yasaklı işlemler sık sık DOM istisnaları oluşturacağından bu çağrı bir try bloğuna sarmalanmıştır. Bunları yakalayıp bunun yerine kolay anlaşılır bir hata mesajı bildireceğiz. Son olarak, sonucu üst pencereye
geri göndeririz. Bu oldukça basit bir işlemdir.
Üst öğe de benzer şekilde basittir. Kod için textarea ve yürütme için button içeren küçük bir kullanıcı arayüzü oluşturacağız. Ayrıca, korumalı alana alınmış bir iframe aracılığıyla frame.html'yi ekleyerek yalnızca komut dosyası yürütmeye izin vereceğiz:
<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
id='sandboxed'
src='frame.html'></iframe>
Şimdi yürütme için her şeyi birbirine bağlayacağız. Öncelikle iframe yanıtlarını dinleyip kullanıcılarımıza alert(). Gerçek bir uygulama muhtemelen
daha az can sıkıcı bir şey yapar:
window.addEventListener('message',
function (e) {
// Sandboxed iframes which lack the 'allow-same-origin'
// header have "null" rather than a valid origin. This means you still
// have to be careful about accepting data via the messaging API you
// create. Check that source, and validate those inputs!
var frame = document.getElementById('sandboxed');
if (e.origin === "null" && e.source === frame.contentWindow)
alert('Result: ' + e.data);
});
Ardından, button tıklamalarına bir etkinlik işleyici bağlayacağız. Kullanıcı tıkladığında textarea öğesinin mevcut içeriğini alıp yürütme için çerçeveye aktarırız:
function evaluate() {
var frame = document.getElementById('sandboxed');
var code = document.getElementById('code').value;
// Note that we're sending the message to "*", rather than some specific
// origin. Sandboxed iframes which lack the 'allow-same-origin' header
// don't have an origin which you can target: you'll have to send to any
// origin, which might alow some esoteric attacks. Validate your output!
frame.contentWindow.postMessage(code, '*');
}
document.getElementById('safe').addEventListener('click', evaluate);
Kolay, değil mi? Çok basit bir değerlendirme API'si oluşturduk ve değerlendirilen kodun çerezler veya DOM depolama alanı gibi hassas bilgilere erişimi olmadığından emin olabiliriz. Benzer şekilde, değerlendirilen kod eklenti yükleyemez, yeni pencereler açamaz veya diğer can sıkıcı ya da kötü amaçlı etkinlikleri gerçekleştiremez.
Monolitik uygulamaları tek amaçlı bileşenlere bölerek kendi kodunuz için de aynı şeyi yapabilirsiniz. Her biri, yukarıda yazdığımız gibi basit bir mesajlaşma API'siyle sarmalanabilir. Yüksek ayrıcalıklı üst pencere, denetleyici ve dağıtıcı olarak görev yapabilir. Her biri işini yapmak için mümkün olan en az ayrıcalığa sahip belirli modüllere mesaj gönderir, sonuçları dinler ve her modülün yalnızca ihtiyaç duyduğu bilgilerle beslenmesini sağlar.
Ancak üst öğeyle aynı kaynaktan gelen çerçeveli içeriklerle çalışırken çok dikkatli olmanız gerekir. https://example.com/ adresindeki bir sayfa, aynı kaynaktaki başka bir sayfayı hem allow-same-origin hem de allow-scripts işaretlerini içeren bir korumalı alanla çerçevelerse çerçevelenen sayfa üst sayfaya erişebilir ve korumalı alan özelliğini tamamen kaldırabilir.
Korumalı alanınızda oynama
Korumalı alan, şu anda çeşitli tarayıcılarda kullanılabilir: Firefox 17+, IE10+ ve bu makalenin yazıldığı sırada Chrome (caniuse'da güncel bir destek tablosu bulunmaktadır). Eklediğiniz iframes öğesine sandbox özelliğini uygulayarak, görüntüledikleri içeriğe belirli ayrıcalıklar (yalnızca içeriğin doğru şekilde çalışması için gerekli olan ayrıcalıklar) verebilirsiniz. Bu sayede, İçerik Güvenliği Politikası ile mümkün olanın ötesinde, üçüncü taraf içeriklerin dahil edilmesiyle ilişkili riski azaltabilirsiniz.
Ayrıca korumalı alan, akıllı bir saldırganın kendi kodunuzdaki boşluklardan yararlanma riskini azaltmak için güçlü bir tekniktir. Monolitik bir uygulamayı, her biri bağımsız işlevlerin küçük bir bölümünden sorumlu olan korumalı alan hizmetlerine ayırarak saldırganları yalnızca belirli çerçevelerin içeriğini değil, aynı zamanda denetleyicilerini de ihlal etmeye zorlarsınız. Bu çok daha zor bir görevdir, özellikle de denetleyicinin kapsamı büyük ölçüde azaltılabileceğinden. Geri kalan kısımlar için tarayıcıdan yardım isterseniz güvenlikle ilgili çalışmalarınızı bu kodu denetlemeye ayırabilirsiniz.
Bu, korumalı alanın internetteki güvenlik sorununa eksiksiz bir çözüm olduğu anlamına gelmez. Derinlemesine savunma sunar ve kullanıcılarınızın istemcileri üzerinde kontrolünüz yoksa tüm kullanıcılarınız için tarayıcı desteğine henüz güvenemezsiniz (kullanıcılarınızın istemcilerini kontrol ediyorsanız, örneğin kurumsal bir ortamda, ne mutlu size!). Bir gün olabilir ancak şimdilik korumalı alan, savunmanızı güçlendirmek için başka bir koruma katmanıdır. Tek başına güvenebileceğiniz eksiksiz bir savunma değildir. Yine de katmanlar mükemmel. Bu özellikten yararlanmanızı öneririm.
Daha fazla bilgi
"Privilege Separation in HTML5 Applications" (HTML5 Uygulamalarında Ayrıcalık Ayrımı) küçük bir çerçevenin tasarımı ve üç mevcut HTML5 uygulamasına uygulanmasıyla ilgili ilginç bir makaledir.
Korumalı alan, diğer iki yeni iframe özelliğiyle birleştirildiğinde daha da esnek olabilir:
srcdocveseamless. İlki, HTTP isteği ek yükü olmadan bir çerçeveyi içerikle doldurmanıza olanak tanır. İkincisi ise stilin çerçeveli içeriğe akmasına olanak tanır. Şu anda her ikisinin de tarayıcı desteği oldukça zayıf (Chrome ve WebKit gecelik sürümleri) ancak gelecekte ilginç bir kombinasyon olacak. Örneğin, aşağıdaki kodu kullanarak bir makaledeki yorumları korumalı alana alabilirsiniz:<iframe sandbox seamless srcdoc="<p>This is a user's comment! It can't execute script! Hooray for safety!</p>"></iframe>