ASP.NET Core'da Siteler Arası Betik Çalıştırmayı (XSS) Engelleme

Gönderen Rick Anderson

Siteler Arası Betik (XSS), saldırganın istemci tarafı betiklerini (genellikle JavaScript) web sayfalarına yerleştirmesine olanak tanıyan bir güvenlik açığıdır. Diğer kullanıcılar etkilenen sayfaları yüklediğinde, saldırganın betikleri çalıştırılarak saldırganın s ve oturum belirteçlerini çalmasını cookie, DOM işlemesi aracılığıyla web sayfasının içeriğini değiştirmesini veya tarayıcıyı başka bir sayfaya yönlendirmesini sağlar. XSS güvenlik açıkları genellikle bir uygulama kullanıcı girişini aldığında ve doğrulama, kodlama veya kaçış yapmadan sayfaya çıkışını aldığında oluşur.

Bu makale öncelikle görünümlere, Razor Sayfalara ve XSS'ye açık olabilecek HTML döndüren diğer uygulamalara sahip ASP.NET Core MVC için geçerlidir. HTML, XML veya JSON biçiminde veri döndüren Web API'leri, istemci uygulamasının API'ye ne kadar güven olduğuna bağlı olarak kullanıcı girişini düzgün temizlemedikleri takdirde istemci uygulamalarında XSS saldırılarını tetikleyebilir. Örneğin, bir API kullanıcı tarafından oluşturulan içeriği kabul eder ve bir HTML yanıtında döndürürse, saldırgan yanıt kullanıcının tarayıcısında işlendiğinde yürütülen içeriğe kötü amaçlı betikler ekleyebilir.

XSS saldırılarını önlemek için web API'leri giriş doğrulama ve çıkış kodlaması uygulamalıdır. Giriş doğrulaması, kullanıcı girişinin beklenen ölçütleri karşıladığından ve kötü amaçlı kod içermediğinden emin olur. Çıktı kodlaması, API tarafından döndürülen tüm verilerin düzgün bir şekilde temizlendiğinden emin olur ve böylece kullanıcının tarayıcısı tarafından kod olarak yürütülemez. Daha fazla bilgi için bu GitHub konusuna bakın.

Uygulamanızı XSS'ye karşı koruma

Temel düzeyde XSS, uygulamanızı işlenmiş sayfanıza etiket <script> eklemesi için kandırarak veya bir öğeye olay On* ekleyerek çalışır. Geliştiriciler, uygulamalarına XSS'yi tanıtmaktan kaçınmak için aşağıdaki önleme adımlarını kullanmalıdır:

  1. Aşağıdaki adımların geri kalanını izlemediğiniz sürece, html girişinize güvenilmeyen verileri hiçbir zaman yerleştirmeyin. Güvenilmeyen veriler, html formu girişleri, sorgu dizeleri, HTTP üst bilgileri ve hatta veritabanından alınan veriler gibi bir saldırgan tarafından denetlenebilen verilerdir çünkü bir saldırgan uygulamanızı ihlal edemese bile veritabanınızı ihlal edebilir.

  2. Güvenilmeyen verileri bir HTML öğesinin içine yerleştirmeden önce HTML ile kodlanmış olduğundan emin olun. HTML kodlaması gibi < karakterler alır ve bunları <

  3. Güvenilmeyen verileri bir HTML özniteliğine yerleştirmeden önce HTML ile kodlanmış olduğundan emin olun. HTML öznitelik kodlaması, HTML kodlamasının üst kümesidir ve " ve " gibi ek karakterleri kodlar.

  4. Güvenilmeyen verileri JavaScript'e yerleştirmeden önce, çalışma zamanında içeriğini aldığınız bir HTML öğesine yerleştirin. Bu mümkün değilse verilerin JavaScript kodlanmış olduğundan emin olun. JavaScript kodlaması, JavaScript için tehlikeli karakterler alır ve bunları onaltılıklarıyla değiştirir, örneğin olarak <\u003Ckodlanır.

  5. Güvenilmeyen verileri bir URL sorgu dizesine yerleştirmeden önce URL'nin kodlanmış olduğundan emin olun.

KULLANARAK HTML Kodlama Razor

Razor MVC'de kullanılan altyapı, bunu önlemek için çok çalışmadığınız sürece değişkenlerden alınan tüm çıktıları otomatik olarak kodlar. Yönergesini @ her kullandığınızda HTML özniteliği kodlama kurallarını kullanır. HTML öznitelik kodlaması, HTML kodlamasının üst kümesi olduğundan, HTML kodlaması mı yoksa HTML öznitelik kodlaması mı kullanmanız gerektiği konusunda endişelenmeniz gerekmez. Güvenilmeyen girişi doğrudan JavaScript'e eklemeye çalışırken değil, yalnızca HTML bağlamında @ kullandığınızdan emin olmanız gerekir. Etiket yardımcıları, etiket parametrelerinde kullandığınız girişleri de kodlar.

Aşağıdaki Razor görünümü alın:

@{
    var untrustedInput = "<\"123\">";
}

@untrustedInput

Bu görünümde untrustedInput değişkeninin içeriği çıkarılır. Bu değişken, XSS saldırılarında <kullanılan , " ve >gibi bazı karakterleri içerir. Kaynağın incelenmesi, işlenen çıkışın şu şekilde kodlanmış olduğunu gösterir:

&lt;&quot;123&quot;&gt;

Uyarı

ASP.NET Core MVC, çıkışta otomatik olarak kodlanabilen bir HtmlString sınıf sağlar. Bu, XSS güvenlik açığını ortaya çıkaracağı için hiçbir zaman güvenilmeyen girişle birlikte kullanılmamalıdır.

Kullanarak JavaScript Kodlaması Razor

Görünümünüzde işlemek için JavaScript'e değer eklemek istediğiniz zamanlar olabilir. Bunu yapmanın iki yolu vardır. Değer eklemenin en güvenli yolu, değeri bir etiketin veri özniteliğine yerleştirmek ve JavaScript'inize almaktır. Örneğin:

@{
    var untrustedInput = "<script>alert(1)</script>";
}

<div id="injectedData"
     data-untrustedinput="@untrustedInput" />

<div id="scriptedWrite" />
<div id="scriptedWrite-html5" />

<script>
    var injectedData = document.getElementById("injectedData");

    // All clients
    var clientSideUntrustedInputOldStyle =
        injectedData.getAttribute("data-untrustedinput");

    // HTML 5 clients only
    var clientSideUntrustedInputHtml5 =
        injectedData.dataset.untrustedinput;

    // Put the injected, untrusted data into the scriptedWrite div tag.
    // Do NOT use document.write() on dynamically generated data as it
    // can lead to XSS.

    document.getElementById("scriptedWrite").innerText += clientSideUntrustedInputOldStyle;

    // Or you can use createElement() to dynamically create document elements
    // This time we're using textContent to ensure the data is properly encoded.
    var x = document.createElement("div");
    x.textContent = clientSideUntrustedInputHtml5;
    document.body.appendChild(x);

    // You can also use createTextNode on an element to ensure data is properly encoded.
    var y = document.createElement("div");
    y.appendChild(document.createTextNode(clientSideUntrustedInputHtml5));
    document.body.appendChild(y);

</script>

Yukarıdaki işaretleme aşağıdaki HTML'yi oluşturur:

<div id="injectedData"
     data-untrustedinput="&lt;script&gt;alert(1)&lt;/script&gt;" />

<div id="scriptedWrite" />
<div id="scriptedWrite-html5" />

<script>
    var injectedData = document.getElementById("injectedData");

    // All clients
    var clientSideUntrustedInputOldStyle =
        injectedData.getAttribute("data-untrustedinput");

    // HTML 5 clients only
    var clientSideUntrustedInputHtml5 =
        injectedData.dataset.untrustedinput;

    // Put the injected, untrusted data into the scriptedWrite div tag.
    // Do NOT use document.write() on dynamically generated data as it can
    // lead to XSS.

    document.getElementById("scriptedWrite").innerText += clientSideUntrustedInputOldStyle;

    // Or you can use createElement() to dynamically create document elements
    // This time we're using textContent to ensure the data is properly encoded.
    var x = document.createElement("div");
    x.textContent = clientSideUntrustedInputHtml5;
    document.body.appendChild(x);

    // You can also use createTextNode on an element to ensure data is properly encoded.
    var y = document.createElement("div");
    y.appendChild(document.createTextNode(clientSideUntrustedInputHtml5));
    document.body.appendChild(y);

</script>

Yukarıdaki kod aşağıdaki çıkışı oluşturur:

<script>alert(1)</script>
<script>alert(1)</script>
<script>alert(1)</script>

Uyarı

DOM öğeleri oluşturmak veya dinamik olarak oluşturulan içerikte kullanmak document.write() için JavaScript'te güvenilmeyen girişleri birleştirmeYIN.

Kodun DOM tabanlı XSS'ye açık olmasını önlemek için aşağıdaki yaklaşımlardan birini kullanın:

  • createElement() ve veya gibi uygun yöntemlere veya özelliklere sahip özellik değerleri atayın node.textContent=node.InnerText=.
  • document.CreateTextNode() ve uygun DOM konumuna ekleyin.
  • element.SetAttribute()
  • element[attribute]=

Koddaki kodlayıcılara erişme

HTML, JavaScript ve URL kodlayıcıları kodunuz için iki şekilde kullanılabilir:

  • Bağımlılık ekleme yoluyla bunları ekleme.
  • Ad alanında bulunan varsayılan kodlayıcıları System.Text.Encodings.Web kullanın.

Varsayılan kodlayıcıları kullanırken, güvenli olarak kabul edilecek karakter aralıklarına uygulanan özelleştirmeler geçerli olmaz. Varsayılan kodlayıcılar mümkün olan en güvenli kodlama kurallarını kullanır.

YAPıLANDıRılabilir kodlayıcıları DI aracılığıyla kullanmak için oluşturucularınız uygun şekilde bir HtmlEncoder, JavaScriptEncoder ve UrlEncoder parametresi almalıdır. Örneğin;

public class HomeController : Controller
{
    HtmlEncoder _htmlEncoder;
    JavaScriptEncoder _javaScriptEncoder;
    UrlEncoder _urlEncoder;

    public HomeController(HtmlEncoder htmlEncoder,
                          JavaScriptEncoder javascriptEncoder,
                          UrlEncoder urlEncoder)
    {
        _htmlEncoder = htmlEncoder;
        _javaScriptEncoder = javascriptEncoder;
        _urlEncoder = urlEncoder;
    }
}

URL Parametrelerini Kodlama

Değer olarak güvenilmeyen giriş içeren bir URL sorgu dizesi oluşturmak istiyorsanız değerini UrlEncoder kodlamak için kullanın. Örneğin:

var example = "\"Quoted Value with spaces and &\"";
var encodedValue = _urlEncoder.Encode(example);

Kodlandıktan sonracodedValue değişkeni içerir %22Quoted%20Value%20with%20spaces%20and%20%26%22. Boşluklar, tırnak işaretleri, noktalama işaretleri ve diğer güvenli olmayan karakterler yüzde olarak onaltılık değerlerine kodlanır, örneğin boşluk karakteri %20 olur.

Uyarı

URL yolunun parçası olarak güvenilmeyen girişi kullanmayın. Güvenilmeyen girişi her zaman sorgu dizesi değeri olarak geçirin.

Kodlayıcıları Özelleştirme

Varsayılan olarak kodlayıcılar Temel Latin Unicode aralığıyla sınırlı bir güvenli liste kullanır ve bu aralığın dışındaki tüm karakterleri karakter kodu eşdeğerleri olarak kodlar. Bu davranış, dizelerinizin çıktısını almak için kodlayıcıları Razor kullandığından TagHelper ve HtmlHelper işlemesini de etkiler.

Bunun arkasındaki neden bilinmeyen veya gelecekteki tarayıcı hatalarına karşı koruma sağlamaktır (önceki tarayıcı hataları İngilizce olmayan karakterlerin işlenmesine bağlı olarak ayrıştırma özelliğine sahiptir). Web siteniz Çince, Kiril veya diğerleri gibi Latin olmayan karakterleri yoğun bir şekilde kullanıyorsa, bu muhtemelen istediğiniz davranış değildir.

Kodlayıcı güvenli listeleri, içinde, Program.csbaşlatma sırasında uygulamaya uygun Unicode aralıklarını içerecek şekilde özelleştirilebilir:

Örneğin, aşağıdakine benzer bir Razor HtmlHelper kullanarak varsayılan yapılandırmayı kullanma:

<p>This link text is in Chinese: @Html.ActionLink("汉语/漢語", "Index")</p>

Yukarıdaki işaretleme Çince metin kodlanmış olarak işlenir:

<p>This link text is in Chinese: <a href="/">&#x6C49;&#x8BED;/&#x6F22;&#x8A9E;</a></p>

Kodlayıcı tarafından güvenli olarak ele alınan karakterleri genişletmek için içine aşağıdaki satırı Program.csekleyin:

builder.Services.AddSingleton<HtmlEncoder>(
     HtmlEncoder.Create(allowedRanges: new[] { UnicodeRanges.BasicLatin,
                                               UnicodeRanges.CjkUnifiedIdeographs }));

Kodlayıcı güvenli listelerini, ConfigureServices()içinde, başlatma sırasında uygulamanıza uygun Unicode aralıkları içerecek şekilde özelleştirebilirsiniz.

Örneğin, varsayılan yapılandırmayı kullanarak bunun gibi bir Razor HtmlHelper kullanabilirsiniz;

<p>This link text is in Chinese: @Html.ActionLink("汉语/漢語", "Index")</p>

Web sayfasının kaynağını görüntülediğinizde, çince metin kodlanmış şekilde aşağıdaki gibi işlendiğini görürsünüz;

<p>This link text is in Chinese: <a href="/">&#x6C49;&#x8BED;/&#x6F22;&#x8A9E;</a></p>

Kodlayıcı tarafından güvenli olarak ele alınan karakterleri genişletmek için içindeki yöntemine ConfigureServices()startup.csaşağıdaki satırı eklersiniz: ;

services.AddSingleton<HtmlEncoder>(
     HtmlEncoder.Create(allowedRanges: new[] { UnicodeRanges.BasicLatin,
                                               UnicodeRanges.CjkUnifiedIdeographs }));

Bu örnek, güvenli listeyi Unicode Aralığı CjkUnifiedIdeographs içerecek şekilde genişletmektedir. İşlenen çıkış artık

<p>This link text is in Chinese: <a href="/">汉语/漢語</a></p>

Kasa liste aralıkları dil olarak değil Unicode kod grafikleri olarak belirtilir. Unicode standardı, karakterlerinizi içeren grafiği bulmak için kullanabileceğiniz kod grafiklerinin bir listesine sahiptir. Html, JavaScript ve Url kodlayıcıların her birinin ayrı olarak yapılandırılması gerekir.

Dekont

Güvenli listenin özelleştirilmesi yalnızca DI aracılığıyla alınan kodlayıcıları etkiler. Bir kodlayıcıya o zaman varsayılan aracılığıyla System.Text.Encodings.Web.*Encoder.Default doğrudan erişiyorsanız Temel Latin yalnızca güvenli listesi kullanılır.

Kodlama nerede gerçekleşmelidir?

Genel olarak kabul edilen uygulama, kodlamanın çıkış noktasında gerçekleştiği ve kodlanmış değerlerin hiçbir zaman veritabanında depolanmaması gerektiğidir. Çıktı noktasında kodlama, verilerin kullanımını (örneğin, HTML'den bir sorgu dizesi değerine) değiştirmenize olanak tanır. Ayrıca, arama yapmadan önce değerleri kodlamak zorunda kalmadan verilerinizi kolayca aramanızı sağlar ve kodlayıcılarda yapılan değişikliklerden veya hata düzeltmelerinden yararlanmanıza olanak tanır.

XSS önleme tekniği olarak doğrulama

Doğrulama, XSS saldırılarını sınırlamak için kullanışlı bir araç olabilir. Örneğin, yalnızca 0-9 karakterlerini içeren sayısal bir dize XSS saldırısını tetiklemez. Kullanıcı girişinde HTML kabul edildiğinde doğrulama daha karmaşık hale gelir. HTML girişini ayrıştırmak imkansız değilse zordur. Katıştırılmış HTML'yi silen bir ayrıştırıcı ile birlikte gelen Markdown, zengin girişi kabul etmek için daha güvenli bir seçenektir. Yalnızca doğrulamaya asla güvenmeyin. Hangi doğrulama veya temizleme gerçekleştirildi olursa olsun, her zaman güvenilmeyen girişi çıkış öncesinde kodla.