Latte Güvenlik ile Eş Anlamlıdır

Latte, kritik Siteler Arası Komut Dosyası (XSS) güvenlik açığına karşı etkili koruma sağlayan tek PHP şablonlama sistemidir. Bu, bağlama duyarlı kaçış olarak adlandırılan özellik sayesinde gerçekleşmektedir. Hadi konuşalım,

  • XSS güvenlik açığının prensibi nedir ve neden bu kadar tehlikelidir?
  • Latte'yi XSS'e karşı savunmada bu kadar etkili kılan şey
  • Twig, Blade ve diğer şablonların neden kolayca tehlikeye atılabileceği

Siteler Arası Komut Dosyası Oluşturma (XSS)

Siteler arası komut dosyası oluşturma (kısaca XSS) web sitelerindeki en yaygın güvenlik açıklarından biridir ve çok tehlikelidir. Bir saldırganın yabancı bir siteye kötü amaçlı bir komut dosyası (kötü amaçlı yazılım olarak adlandırılır) eklemesine ve bu komut dosyasının şüphelenmeyen bir kullanıcının tarayıcısında çalışmasına olanak tanır.

Böyle bir komut dosyası ne yapabilir? Örneğin, oturum açtıktan sonra görüntülenen hassas veriler de dahil olmak üzere, ele geçirilen siteden saldırgana rastgele içerik gönderebilir. Sayfayı değiştirebilir veya kullanıcı adına başka isteklerde bulunabilir. Örneğin, web postası olsaydı, hassas mesajları okuyabilir, görüntülenen içeriği değiştirebilir veya ayarları değiştirebilir, örneğin, gelecekteki e-postalara erişim elde etmek için tüm mesajların kopyalarını saldırganın adresine yönlendirmeyi açabilir.

XSS'nin en tehlikeli güvenlik açıkları listesinin başında yer almasının nedeni de budur. Bir web sitesinde bir güvenlik açığı keşfedilirse, istismar edilmesini önlemek için mümkün olan en kısa sürede kaldırılmalıdır.

Güvenlik Açığı Nasıl Ortaya Çıkar?

Hata, web sayfasının oluşturulduğu ve değişkenlerin yazdırıldığı yerde meydana gelir. Bir arama sayfası oluşturduğunuzu ve başlangıçta formda arama teriminin bulunduğu bir paragraf olacağını düşünün:

echo '<p>Search results for <em>' . $search . '</em></p>';

Bir saldırgan, aşağıdaki gibi HTML kodu da dahil olmak üzere herhangi bir dize yazabilir <script>alert("Hacked!")</script>arama alanına ve dolayısıyla $search değişkenine aktarılır. Çıktı herhangi bir şekilde sterilize edilmediğinden, görüntülenen sayfanın bir parçası haline gelir:

<p>Search results for <em><script>alert("Hacked!")</script></em></p>

Tarayıcı, arama dizesinin çıktısını vermek yerine JavaScript'i çalıştırır. Ve böylece saldırgan sayfayı ele geçirir.

Bir değişkene kod yerleştirmenin gerçekten de JavaScript'i çalıştıracağını, ancak bunun yalnızca saldırganın tarayıcısında gerçekleşeceğini iddia edebilirsiniz. Kurbana nasıl ulaşır? Bu açıdan bakıldığında, çeşitli XSS türlerini ayırt edebiliriz. Arama sayfası örneğimizde, yansıtılmış XSS'den bahsediyoruz. Bu durumda, kurbanın parametresinde kötü amaçlı kod içeren bir bağlantıya tıklaması için kandırılması gerekir:

https://example.com/?search=<script>alert("Hacked!")</script>

Kullanıcının bağlantıya erişmesini sağlamak biraz sosyal mühendislik gerektirse de, bu zor değildir. Kullanıcılar, ister e-postalarda ister sosyal medyada olsun, bağlantılara fazla düşünmeden tıklar. Ve adreste şüpheli bir şey olduğu gerçeği URL kısaltıcı tarafından maskelenebilir, böylece kullanıcı yalnızca bit.ly/xxx adresini görür.

Bununla birlikte, depolanmış XSS veya kalıcı XSS olarak bilinen ikinci ve çok daha tehlikeli bir saldırı şekli vardır; bu durumda saldırgan, kötü amaçlı kodu sunucuda depolamayı başararak belirli sayfalara otomatik olarak eklenmesini sağlar.

Bunun bir örneği, kullanıcıların yorum gönderdiği web siteleridir. Bir saldırgan kod içeren bir gönderi gönderir ve bu kod sunucuya kaydedilir. Eğer site yeterince güvenli değilse, bu kod her ziyaretçinin tarayıcısında çalışacaktır.

Görünüşe göre saldırının amacı <script> dizesini sayfaya yerleştirebilirsiniz. Aslında, JavaScript'i gömmenin birçok yolu vardır. Bir HTML niteliği kullanarak gömme örneğini ele alalım. Resimlere alt özniteliğinde yazdırılan bir başlık ekleyebileceğiniz bir fotoğraf galerimiz olsun:

echo '<img src="' . $imageFile . '" alt="' . $imageAlt . '">';

Bir saldırganın etiket olarak akıllıca oluşturulmuş bir " onload="alert('Hacked!') dizesi eklemesi yeterlidir ve çıktı sterilize edilmezse, ortaya çıkan kod aşağıdaki gibi görünecektir:

<img src="photo0145.webp" alt="" onload="alert('Hacked!')">

Sahte onload niteliği artık sayfanın bir parçası haline gelir. Tarayıcı, resim indirilir indirilmez içerdiği kodu çalıştıracaktır. Hacklendi!

XSS'e Karşı Nasıl Savunma Yapılır?

Kara liste kullanarak bir saldırıyı tespit etmeye yönelik herhangi bir girişim, örneğin <script> dize vb. yetersizdir. Uygulanabilir bir savunmanın temeli, sayfa içinde yazdırılan tüm verilerin tutarlı bir şekilde sterilize edilmesidir.

Öncelikle bu, özel anlamı olan tüm karakterlerin diğer eşleşen dizilerle değiştirilmesini içerir, buna argoda kaçış denir (dizinin ilk karakteri kaçış karakteri olarak adlandırılır, dolayısıyla adı da buradan gelir). Örneğin, HTML metninde, < has a special meaning, which, if it is not to be interpreted as the beginning of a tag, must be replaced by a visually corresponding sequence, the so-called HTML entity &lt; karakteri . Ve tarayıcı bir karakter yazdırır.

Verilerin hangi bağlamda çıktılandığını ayırt etmek çok önemlidir. Çünkü farklı bağlamlar dizeleri farklı şekilde sterilize eder. Farklı karakterlerin farklı bağlamlarda özel anlamları vardır. Örneğin, HTML metninde, HTML niteliklerinde, bazı özel öğelerin içinde vb. kaçış farklıdır. Bunu birazdan ayrıntılı olarak tartışacağız.

En iyisi, kaçış işlemini doğrudan dize sayfaya yazıldığında gerçekleştirmek ve bunun gerçekten yapılmasını ve yalnızca bir kez yapılmasını sağlamaktır. İşlemin doğrudan şablonlama sistemi tarafından otomatik olarak gerçekleştirilmesi en iyisidir. Çünkü işlem otomatik olarak yapılmazsa, programcı bunu unutabilir. Ve bir ihmal, sitenin savunmasız olduğu anlamına gelir.

Ancak, XSS yalnızca şablonlardaki verilerin çıktısını etkilemez, aynı zamanda güvenilmeyen verileri düzgün bir şekilde işlemesi gereken uygulamanın diğer bölümlerini de etkiler. Örneğin, uygulamanızdaki JavaScript'ler innerHTML ile birlikte kullanılmamalı, yalnızca innerText veya textContent kullanılmalıdır. JavaScript gibi dizeleri değerlendiren işlevlere özel dikkat gösterilmelidir. eval(), aynı zamanda setTimeout() veya setAttribute() ile onload gibi olay özniteliklerini kullanma vb. Ancak bu, şablonlar tarafından kapsanan kapsamın ötesine geçer.

İdeal 3 sayı savunması:**

  1. Veri çıktısının alındığı bağlamı tanıyın
  2. verileri bu bağlamın kurallarına göre sterilize eder (yani “bağlama duyarlı”)
  3. bunu otomatik olarak yapar

Bağlam Farkında Kaçış

Bağlam kelimesi ile tam olarak ne kastedilmektedir? Çıktısı alınacak verilerin işlenmesi için belgede kendi kuralları olan bir yerdir. Belgenin türüne (HTML, XML, CSS, JavaScript, düz metin, …) bağlıdır ve belgenin belirli bölümlerinde değişiklik gösterebilir. Örneğin, bir HTML belgesinde, çok farklı kuralların geçerli olduğu bu tür birçok yer (bağlam) vardır. Ne kadar çok olduğuna şaşırabilirsiniz. İşte ilk dördü:

<p>#text</p>
<img src="#attribute">
<textarea>#rawtext</textarea>
<!-- #comment -->

Bir HTML sayfasının ilk ve temel içeriği HTML metnidir. Buradaki kurallar nelerdir? Özel anlamlı karakterler < and & bir etiketin veya varlığın başlangıcını temsil eder, bu nedenle bunları HTML varlığıyla (< with &lt;, & with &amp) değiştirerek kaçmamız gerekir.

İkinci en yaygın bağlam, bir HTML niteliğinin değeridir. Metinden farklı olarak buradaki özel anlam, özniteliği sınırlandıran tırnak işaretine " or ' gider. Bunun, niteliğin sonu olarak görülmemesi için bir varlık olarak yazılması gerekir. Öte yandan, &lt; karakteri bir öznitelikte güvenle kullanılabilir çünkü burada özel bir anlamı yoktur; bir etiketin veya yorumun başlangıcı olarak anlaşılamaz. Ancak dikkat edin, HTML'de öznitelik değerlerini tırnak işaretleri olmadan yazabilirsiniz, bu durumda bir dizi karakterin özel anlamı vardır, bu nedenle bu başka bir ayrı bağlamdır.

Bu sizi şaşırtabilir, ancak özel kurallar <textarea> ve <title> < character need not (but can) be escaped unless followed by / . Ama bu daha çok bir merak konusu.

HTML yorumlarının içinde ilginçtir. Burada, HTML varlıkları kaçış için kullanılmaz. Yorumlarda nasıl kaçış yapılacağını belirten bir spesifikasyon bile yoktur. Sadece biraz tuhaf kurallara uymanız ve içlerindeki belirli karakter kombinasyonlarından kaçınmanız gerekir.

JavaScript veya CSS'yi HTML içine gömdüğümüzde olduğu gibi, bağlamlar da katmanlı olabilir. Bu işlem iki farklı şekilde yapılabilir: öğe veya öznitelik:

<script>#js-element</script>
<img onclick="#js-attribute">

<style>#css-element</style>
<p style="#css-attribute"></p>

İki yol ve iki farklı türde veri kaçışı. İçinde <script> ve <style> öğelerinde, HTML yorumlarında olduğu gibi, HTML varlıkları kullanılarak kaçış gerçekleştirilmez. Bu öğelerin içindeki verileri kaçarken, tek bir kural vardır: metin sırasıyla </script ve </style dizisini içermemelidir.

Öte yandan, style ve on*** öznitelikleri HTML varlıkları kullanılarak öncelenir.

Ve elbette, gömülü JavaScript veya CSS içinde, bu dillerin kaçış kuralları geçerlidir. Dolayısıyla, onload gibi bir öznitelikteki bir dize önce JS kurallarına göre, sonra da HTML öznitelik kurallarına göre öncelenir.

Ugh… Gördüğünüz gibi, HTML bağlam katmanları olan çok karmaşık bir belgedir ve veriyi tam olarak nerede (yani hangi bağlamda) çıktıladığımı bilmeden, bunu nasıl doğru yapacağımı bilemem.

Bir Örnek İster misiniz?

Bir dize alalım Rock'n'Roll.

HTML metninde çıktısını alırsanız, bu durumda herhangi bir ikame yapmanıza gerek yoktur, çünkü dize özel anlamı olan herhangi bir karakter içermez. Tek tırnak içine alınmış bir HTML niteliğinin içine yazarsanız durum farklıdır. Bu durumda, tırnak işaretlerini HTML varlıklarına kaçmanız gerekir:

<div title='Rock&apos;n&apos;Roll'></div>

Bu kolaydı. Bağlam katmanlı olduğunda, örneğin dize JavaScript'in bir parçası olduğunda çok daha ilginç bir durum ortaya çıkar.

Bu yüzden önce bunu JavaScript'in içine yazıyoruz. Yani, onu tırnak içine alıyoruz ve aynı zamanda \ karakterini kullanarak içerdiği tırnaklardan kaçıyoruz:

'Rock\'n\'Roll'

Kodun bir şey yapmasını sağlamak için bir fonksiyon çağrısı ekleyebiliriz:

alert('Rock\'n\'Roll');

Bu kodu bir HTML belgesine eklersek <script>başka hiçbir şeyi değiştirmemize gerek yoktur, çünkü yasak </script dizisi mevcut değildir:

<script> alert('Rock\'n\'Roll'); </script>

Ancak, bunu bir HTML niteliğine eklemek istiyorsak, yine de tırnak işaretlerini HTML varlıklarına kaçmamız gerekir:

<div onclick='alert(&apos;Rock\&apos;n\&apos;Roll&apos;)'></div>

Ancak, iç içe geçmiş bağlam sadece JS veya CSS olmak zorunda değildir. Genellikle bir URL de olabilir. URL'lerdeki parametreler, özel karakterler % ile başlayan dizilere dönüştürülerek kaçılır. Örnek:

https://example.org/?a=Jazz&b=Rock%27n%27Roll

Ve bu dizeyi bir öznitelikte çıktıladığımızda, yine bu bağlama göre kaçış uygularız ve & with &amp yerine koyarız:

<a href="https://example.org/?a=Jazz&amp;b=Rock%27n%27Roll">

Buraya kadar okuduysanız, tebrikler, yorucu oldu. Artık bağlamların ve kaçışın ne olduğu hakkında iyi bir fikriniz var. Ve karmaşık olması konusunda endişelenmenize gerek yok. Latte bunu sizin için otomatik olarak yapıyor.

Latte vs Naif Sistemler

Bir HTML belgesinde kaçış işleminin nasıl düzgün bir şekilde yapılacağını ve bağlamı, yani verinin çıktısını nerede aldığınızı bilmenin ne kadar önemli olduğunu gösterdik. Başka bir deyişle, bağlama duyarlı kaçış nasıl çalışır. Bu, işlevsel XSS savunması için bir ön koşul olsa da, Latte, PHP için bunu yapan tek şablonlama sistemidir.

Günümüzde tüm sistemler otomatik kaçış özelliğine sahip olduğunu iddia ederken bu nasıl mümkün olabilir? Bağlamı bilmeden otomatik kaçış, yanlış bir güvenlik duygusu yaratan bir saçmalıktır.

Twig, Laravel Blade ve diğerleri gibi şablonlama sistemleri şablonda herhangi bir HTML yapısı görmez. Bu nedenle, bağlamları da görmezler. Latte ile karşılaştırıldığında, kör ve naiftirler. Sadece kendi işaretlemelerini ele alırlar, diğer her şey onlar için alakasız bir karakter akışıdır:

░░░░░░░░░░░░░░░░░{{ text }}░░░░░░░
░░░░░░░░░░░░░░░░{{ text }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ text }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ text }}░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ text }}░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ text }}░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ text }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ text }}░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░{{ text }}░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░{{ text }}░░░░
- in text: <span>{{ text }}</span>
- in tag: <span {{ text }} ></span>
- in attribute: <span title='{{ text }}'></span>
- in unquoted attribute: <span title={{ text }}></span>
- in attribute containing URL: <a href="{{ text }}"></a>
- in attribute containing JavaScript: <img onload="{{ text }}">
- in attribute containing CSS: <span style="{{ text }}"></span>
- in JavaScriptu: <script>var = {{ text }}</script>
- in CSS: <style>body { content: {{ text }}; }</style>
- in comment: <!-- {{ text }} -->

Naif sistemler sadece mekanik olarak < > & ' " karakterlerini HTML varlıklarına dönüştürür, bu da çoğu kullanımda geçerli bir kaçış yoludur, ancak her zaman değil. Bu nedenle, aşağıda göstereceğimiz gibi çeşitli güvenlik açıklarını tespit edemez veya önleyemezler.

Latte şablonu sizinle aynı şekilde görür. HTML ve XML'i anlar, etiketleri, nitelikleri vb. tanır. Ve bu nedenle, bağlamlar arasında ayrım yapar ve verileri buna göre ele alır. Böylece kritik Siteler Arası Komut Dosyası Açığına karşı gerçekten etkili bir koruma sunar.

Canlı Gösteri

Solda Latte'deki şablonu, sağda ise oluşturulan HTML kodunu görebilirsiniz. $text değişkeni, her seferinde biraz farklı bir bağlamda olmak üzere birkaç kez çıktılanır. Ve bu nedenle biraz farklı şekilde öncelenmiştir. Şablon kodunu kendiniz düzenleyebilirsiniz, örneğin değişkenin içeriğini vb. değiştirebilirsiniz. Deneyin:

{* TRY TO EDIT THIS TEMPLATE *}
{var $text = "Rock'n'Roll"}
- <span>{$text}</span>
- <span title='{$text}'></span>
- <span title={$text}></span>
- <img onload="{$text}">
- <script>var = {$text}</script>
- <!-- {$text} -->
- <span>Rock'n'Roll</span>
- <span title='Rock&apos;n&apos;Roll'></span>
- <span title="Rock&apos;n&apos;Roll"></span>
- <img onload="&quot;Rock&apos;n&apos;Roll&quot;">
- <script>var = "Rock'n'Roll"</script>
- <!-- Rock'n'Roll -->

Bu harika değil mi! Latte bağlama duyarlı kaçışı otomatik olarak yapar, böylece programcı:

  • düşünmek ya da veriden nasıl kaçacağını bilmek zorunda değildir
  • Yanlış olamaz.
  • bunu unutamam

Bunlar Latte'nin çıktı alırken ayırt ettiği ve veri işlemeyi özelleştirdiği tüm bağlamlar bile değildir. Şimdi daha ilginç durumların üzerinden geçeceğiz.

Naif Sistemler Nasıl Hacklenir

Bağlam farklılaştırmasının ne kadar önemli olduğunu ve Latte'nin aksine naif şablonlama sistemlerinin neden XSS'ye karşı yeterli koruma sağlamadığını göstermek için birkaç pratik örnek kullanacağız. Örneklerde Twig'i naif bir sistemin temsilcisi olarak kullanacağız, ancak aynı şey diğer sistemler için de geçerlidir.

Öznitelik Güvenlik Açığı

Yukarıda gösterdiğimiz gibi HTML özniteliğini kullanarak sayfaya kötü amaçlı kod enjekte etmeye çalışalım. Twig'de bir resim görüntüleyen bir şablonumuz olsun:

<img src={{ imageFile }} alt={{ imageAlt }}>

Öznitelik değerlerinin etrafında tırnak işareti olmadığına dikkat edin. Kodlayıcı bunları unutmuş olabilir, ki bu sadece olur. Örneğin, React'te kod tırnak işaretleri olmadan bu şekilde yazılır ve dil değiştiren bir kodlayıcı tırnak işaretlerini kolayca unutabilir.

Saldırgan, akıllıca oluşturulmuş bir dizeyi foo onload=alert('Hacked!') resim başlığı olarak ekler. Twig'in bir değişkenin HTML metin akışı içinde mi, bir niteliğin içinde mi, bir HTML yorumunun içinde mi, vb. mi yazdırıldığını söyleyemediğini zaten biliyoruz; kısacası, bağlamlar arasında ayrım yapmaz. Ve sadece mekanik olarak < > & ' " karakterlerini HTML varlıklarına dönüştürür. Böylece ortaya çıkan kod şöyle görünecektir:

<img src=photo0145.webp alt=foo onload=alert(&#039;Hacked!&#039;)>

Bir güvenlik açığı oluşturuldu!

Sahte bir onload özelliği sayfanın bir parçası haline gelmiştir ve tarayıcı resmi indirdikten hemen sonra bu özelliği çalıştırır.

Şimdi Latte'nin aynı şablonu nasıl ele aldığını görelim:

<img src={$imageFile} alt={$imageAlt}>

Latte şablonu sizinle aynı şekilde görür. Twig'in aksine, HTML'yi anlar ve bir değişkenin tırnak içinde olmayan bir öznitelik değeri olarak yazdırıldığını bilir. Bu yüzden onları ekler. Bir saldırgan aynı başlığı eklediğinde, ortaya çıkan kod aşağıdaki gibi görünecektir:

<img src="photo0145.webp" alt="foo onload=alert(&apos;Hacked!&apos;)">

Latte XSS'yi başarıyla engelledi.

JavaScript'te Değişken Yazdırma

Bağlama duyarlı kaçış sayesinde PHP değişkenlerini JavaScript içinde yerel olarak kullanmak mümkündür.

<p onclick="alert({$movie})">{$movie}</p>

<script>var movie = {$movie};</script>

$movie değişkeni 'Amarcord & 8 1/2' dizesini depolarsa aşağıdaki çıktıyı üretir. HTML ve JavaScript'te ve ayrıca onclick niteliğinde kullanılan farklı kaçışlara dikkat edin:

<p onclick="alert(&quot;Amarcord &amp; 8 1\/2&quot;)">Amarcord &amp; 8 1/2</p>

<script>var movie = "Amarcord & 8 1\/2";</script>

Latte, src veya href niteliklerinde kullanılan değişkenin bir web URL'si (yani HTTP protokolü) içerip içermediğini otomatik olarak kontrol eder ve güvenlik riski oluşturabilecek bağlantıların yazılmasını engeller.

{var $link = 'javascript:attack()'}

<a href={$link}>click here</a>

Yazıyor:

<a href="">click here</a>

Kontrol, nocheck filtresi kullanılarak kapatılabilir.

Latte'nin Sınırları

Latte, tüm uygulama için eksiksiz bir XSS koruması değildir. Latte'yi kullanırken güvenlik hakkında düşünmeyi bırakırsanız mutsuz oluruz. Latte'nin amacı, bir saldırganın bir sayfanın yapısını değiştirememesini, HTML öğelerini veya niteliklerini kurcalayamamasını sağlamaktır. Ancak çıktısı alınan verilerin içerik doğruluğunu kontrol etmez. Ya da JavaScript davranışının doğruluğunu. Bu, şablonlama sisteminin kapsamı dışındadır. Özellikle kullanıcı tarafından girilen ve dolayısıyla güvenilmeyen verilerin doğruluğunu kontrol etmek programcı için önemli bir görevdir.

versiyon: 3.0