Megosztás a következőn keresztül:


Helyek közötti szkriptelés (XSS) megakadályozása a ASP.NET Core-ban

Készítette: Rick Anderson

A helyek közötti szkriptelés (XSS) egy biztonsági rés, amely lehetővé teszi, hogy a kiberbűnözők ügyféloldali szkripteket (általában JavaScriptet) helyezzenek el a weboldalakon. Amikor más felhasználók betöltik az érintett oldalakat, a kiberattacker szkriptjei futnak, lehetővé téve a kiberbűnöző számára a cookie-k és munkamenet-jogkivonatok ellopását, a weblap tartalmának MÓDOSÍTÁSÁT DOM-manipulációval, vagy a böngésző átirányítását egy másik oldalra. Az XSS biztonsági rései általában akkor fordulnak elő, ha egy alkalmazás felhasználói bemenetet használ, és azokat egy lapra irányítja ellenőrzés, kódolás vagy menekülés nélkül.

Ez a cikk elsősorban az ASP.NET Core MVC-hez vonatkozik, amely nézetekkel, oldalakkal és más olyan alkalmazásokkal rendelkezik, Razor amelyek olyan HTML-t adnak vissza, amely sebezhető lehet az XSS-sel szemben. Azok a webes API-k, amelyek HTML, XML vagy JSON formátumban adnak vissza adatokat, XSS-támadásokat aktiválhatnak az ügyfélalkalmazásaikban, ha nem megfelelően fertőtlenítik a felhasználói bemenetet attól függően, hogy az ügyfélalkalmazás mennyire bízik az API-ban. Ha például egy API elfogadja a felhasználó által létrehozott tartalmat, és HTML-válaszban adja vissza, a kiberbűnözők rosszindulatú szkripteket injektálhatnak a felhasználó böngészőjében a válasz megjelenítésekor végrehajtó tartalomba.

Az XSS-támadások megakadályozása érdekében a webes API-knak bemeneti ellenőrzést és kimeneti kódolást kell implementálniuk. A bemeneti ellenőrzés biztosítja, hogy a felhasználói bemenet megfeleljen a várt feltételeknek, és ne tartalmazzon rosszindulatú kódot. A kimeneti kódolás biztosítja, hogy az API által visszaadott adatok megfelelően legyenek megtisztítva, hogy a felhasználó böngészője ne hajthassa végre kódként. További információkért tekintse meg ezt a GitHub-problémát.

Az alkalmazás védelme az XSS ellen

Alapszinten az XSS úgy működik, hogy becsapja az alkalmazást egy <script> címke beszúrásával a renderelt lapra, vagy egy On* esemény elembe való beszúrásával. A fejlesztőknek az alábbi megelőzési lépéseket kell követniük az XSS alkalmazásba való bevezetésének elkerülése érdekében:

  1. Soha ne tegyen nem megbízható adatokat a HTML-bemenetbe, hacsak nem követi az alábbi lépéseket. A nem megbízható adatok olyan adatok, amelyeket egy kiberbűnöző szabályozhat, például HTML-űrlapbemenetek, lekérdezési sztringek, HTTP-fejlécek vagy akár adatbázisból származó adatforrások, mivel a kibertámadások akkor is feltörhetik az adatbázist, ha nem tudják feltörni az alkalmazást.
  2. Mielőtt nem megbízható adatokat helyez egy HTML-elembe, győződjön meg arról, hogy HTML-kódolású. A HTML-kódolás olyan karaktereket, mint például <, biztonságos formába alakítja át, például <.
  3. Mielőtt nem megbízható adatokat helyez egy HTML-attribútumba, győződjön meg arról, hogy a HTML-attribútum kódolva van. A HTML-kódolás ezen speciális formája kettős idézőjeleket ("), egyszeri idézőjeleket ('), ampersands (>) és kisebb (<) karaktereket kezel. A nem megbízható bemenetek kezelésekor használjon HTML-kódolást az általános HTML-tartalmakhoz és HTML-attribútumok kódolását a HTML-attribútumok esetében.
  4. Mielőtt nem megbízható adatokat helyez a JavaScriptbe, helyezze az adatokat egy HTML-elembe, amelynek tartalmát futásidőben kéri le. Ha ez nem lehetséges, győződjön meg arról, hogy az adatok JavaScript-kódolásúak. A JavaScript veszélyes karaktereit úgy kódolja, hogy azok hexadecimális ábrázolásra cserélődnek, például a < kódolása \u003C formában történik.
  5. Mielőtt nem megbízható adatokat helyez egy URL-lekérdezési sztringbe, győződjön meg arról, hogy az URL-cím kódolva van.

HTML-kódolás a következő használatával: Razor

Az Razor motor, amelyet az MVC használ, automatikusan kódolja a változókból eredő összes kimenetet, hacsak nem tesz nagyon sokat azért, hogy ezt megakadályozza. Html-attribútumkódolási szabályokat használ az @ irányelv használatakor. Mivel a HTML-attribútumok kódolása a HTML-kódolás szuperhalmaza, ez azt jelenti, hogy nem kell foglalkoznia azzal, hogy HTML-kódolást vagy HTML-attribútumkódolást kell-e használnia. Győződjön meg arról, hogy a @-t csak HTML-környezetben használja, nem pedig akkor, ha nem megbízható bemenetet próbál közvetlenül a JavaScriptbe beszúrni. A címkesegédek a címkeparaméterekben használt bemenetet is kódolni fogják.

Tekintse meg a következő Razor nézetet:

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

@untrustedInput

Ez a nézet a nem megbízhatóInput változó tartalmát adja ki. Ez a változó tartalmaz néhány karaktert, amelyeket az XSS-támadásokban használnak, nevezetesen <a " és >a . A forrás vizsgálata a renderelt kimenetet a következőképpen kódolja:

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

Figyelmeztetés

ASP.NET Core MVC olyan osztályt HtmlString biztosít, amely nem lesz automatikusan kódolva a kimenet alapján. Ezt soha nem szabad nem megbízható bemenettel kombinálni, mivel ez egy XSS-biztonsági rést tesz elérhetővé.

JavaScript-kódolás a következő használatával: Razor

Előfordulhat, hogy be szeretne szúrni egy értéket a JavaScriptbe a nézetben való feldolgozáshoz. Ezt kétféleképpen teheti meg. Az értékek beszúrásának legbiztonságosabb módja, ha az értéket egy címke adatattribútumában helyezi el, és lekéri a JavaScriptben. Például:

@{
    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>

Az előző korrektúra a következő HTML-t hozza létre:

<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>

Az előző kód a következő kimenetet hozza létre:

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

Figyelmeztetés

NE fűzz össze nem megbízható bemenetet a JavaScriptben DOM-elemek létrehozásához vagy dinamikusan létrehozott tartalomhoz való használatáhozdocument.write().

Az alábbi módszerek egyikével megakadályozhatja, hogy a kód a DOM-alapú XSS-nek legyen kitéve:

  • createElement() tulajdonságértékek hozzárendelése megfelelő módszerekkel vagy tulajdonságokkal, például node.textContent= vagy node.InnerText=.
  • document.CreateTextNode() és fűzze hozzá a megfelelő DOM-helyen.
  • element.SetAttribute()
  • element[attribute]=

Kódkódolók elérése

A HTML-, JavaScript- és URL-kódolók kétféleképpen érhetők el a kódhoz:

  • Függőségi injektáláson keresztül illessze be őket.
  • Használja a névtérben System.Text.Encodings.Web található alapértelmezett kódolókat.

Az alapértelmezett kódolók használatakor a biztonságosként kezelendő karaktertartományokra alkalmazott testreszabások nem lépnek érvénybe. Az alapértelmezett kódolók a lehető legbiztonságosabb kódolási szabályokat használják.

A konfigurálható kódolók DI-en keresztüli használatához a konstruktoroknak szükség szerint egy HtmlEncoder, JavaScriptEncoder és UrlEncoder paramétert kell használniuk. Például;

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-paraméterek kódolása

Ha nem megbízható bemenettel rendelkező URL-lekérdezési sztringet szeretne építeni, az érték kódolásához használja a UrlEncoder elemet. Például

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

A kódolás után a kódoltValue változó tartalmazza a következőt %22Quoted%20Value%20with%20spaces%20and%20%26%22: . A szóközök, idézőjelek, írásjelek és egyéb nem biztonságos karakterek a hexadecimális értékükhöz vannak kódolva, például egy szóköz karakter %20lesz.

Figyelmeztetés

Ne használjon nem megbízható bemenetet URL-elérési út részeként. Mindig adjon meg nem megbízható bemenetet lekérdezési sztringértékként.

A Kódolók testreszabása

Alapértelmezés szerint a kódolók az egyszerű latin Unicode-tartományra korlátozott biztonságos listát használnak, és az adott tartományon kívüli karaktereket a karakterkód megfelelőiként kódolják. Ez a viselkedés a TagHelper és a HtmlHelper renderelésére is hatással van, mivel a kódolókkal adja ki a karakterláncokat Razor.

Ennek indoka az ismeretlen vagy jövőbeli böngészőhibák elleni védelem (a korábbi böngészőhibák megbotlották a nem angol karakterek feldolgozása alapján történő elemzést). Ha a webhely nem latin karaktereket használ, például kínai, cirill vagy más karaktereket, akkor valószínűleg nem ez a viselkedés.

A kódoló biztonságos listái testre szabhatók úgy, hogy tartalmazzák az alkalmazásnak megfelelő Unicode-tartományokat az indítás során a következő helyen Program.cs:

Az alapértelmezett konfigurációt például az Razor alábbihoz hasonló HtmlHelper használatával használhatja:

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

Az előző korrektúra kínai kódolású szöveggel jelenik meg:

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

A kódoló által biztonságosként kezelt karakterek kibővítéséhez szúrja be a következő sort: Program.cs.

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

Testre szabhatja a kódoló biztonságos listáit úgy, hogy az alkalmazásnak megfelelő Unicode-tartományokat tartalmazzon az indítás során.ConfigureServices()

Például az alapértelmezett konfigurációt használva így használhatja a Razor HtmlHelper-t:

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

Amikor megtekinti a weblap forrását, láthatja, hogy az a következőképpen lett renderelve, a kínai szöveg kódolással;

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

A kódoló által biztonságosként kezelt karakterek körének szélesítéséhez illessze be a következő sort a ConfigureServices() metódusába a startup.cs helyére.

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

Ez a példa kibővíti az engedélyezett listát, hogy tartalmazza a Unicode-tartomány CJK Unified Ideographs. A renderelt kimenet mostantól a következő lesz

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

A biztonságos listatartományok Unicode-kóddiagramként vannak megadva, nem nyelvekként. A Unicode szabvány a karaktereket tartalmazó diagramok kereséséhez használható kóddiagramok listáját tartalmazza. Minden kódolót, HTML-t, JavaScriptet és URL-címet külön kell konfigurálni.

Megjegyzés:

A biztonságos lista testreszabása csak a DI-en keresztül forrásként használt kódolókat érinti. Ha közvetlenül hozzáfér egy kódolóhoz a System.Text.Encodings.Web.*Encoder.Default segítségével, akkor az alapértelmezett, csak egyszerű latin betűs biztonságos lista kerül használatra.

Hol történjen a kódolás?

Az általánosan elfogadott gyakorlat az, hogy a kódolás a kimenet helyén történik, és a kódolt értékeket soha nem szabad adatbázisban tárolni. A kódolás a kimenetnél lehetővé teszi az adatok felhasználásának megváltoztatását, például HTML-ről lekérdezési sztringértékre. Emellett lehetővé teszi az adatok egyszerű keresését anélkül, hogy értékeket kellene kódolnia a keresés előtt, és kihasználhatja a kódolók által végzett módosításokat vagy hibajavításokat.

Az érvényesítés, mint XSS-megelőzési technika

Az érvényesítés hasznos eszköz lehet az XSS-támadások korlátozásában. Például egy olyan numerikus sztring, amely csak a 0-9 karaktert tartalmazza, nem indít el XSS-támadást. Az érvényesítés bonyolultabbá válik a HTML felhasználói bemenetben való elfogadásakor. A HTML-bemenet elemzése nehéz, ha nem lehetetlen. A Markdown egy beágyazott HTML-et csíkozó elemzővel párosítva biztonságosabb megoldás a gazdag bemenetek elfogadásához. Soha ne támaszkodjon egyedül az ellenőrzésre. A kimenet előtt mindig kódolja a nem megbízható bemeneteket, függetlenül attól, hogy milyen ellenőrzést vagy fertőtlenítést hajtottak végre.