Esikülje turvalisus: XSS, usalduspiirid ja demo, mida saate käivitada

← Tagasi blogisse

20. aprill 20266 min lugemine

Esikülje turvalisus: XSS, usalduspiirid ja demo, mida saate käivitada

Kuidas XSS pääseb DOM-i, millised brauseri API-d on valamu, ja tootmises kehtivad leevendused - sanitisatsioon, CSP, küpsised ja CSRF paarimine.

turvalisusesikülgreact

Kui string tõlgendatakse markup või skriptina asemel inertse tekstina, siis see sisu käivitub kui kood teie lehe päritolus - samade õigustega kui teie pakett. See on XSS: mitte stiiliviga, segaduses volinik andmete ja juhiste vahel. See postitus on tehniline selgroog - rünnakumehaanika, valamu, skaleeruvad parandused, pluss käivitatav frontend-xss-demo (Vite + React main ; live: xss.lucascoliveira.com) ja viited sellele Next.js hoidlasse, kus päised ja MDX vähendavad vaikimisi riski.

Sama päritolu: mida ründaja tegelikult võidab

Lehele sisestatud skriptid käivad koos samast päritolust kui teie pakett. Praktikas tähendab see sageli:

  • document.cookie nähtavus küpsiste jaoks, mida ei ole märgistatud HttpOnly (sessiooni vargus / fikseerimisahela);
  • fetch() / XMLHttpRequest teie API-le ambitentidega , kui küpsised on saadetud ja CORS lubab seda - või tokenite salvestamine localStorage / sessionStorage , kui teie rakendus need sinna pani.
  • DOM lugemised mittekonfidentsiaalsete PII-de renderdamisest lehel ja UI ümbersuunamine (võltsvormid, ülekatte) andmispüügi jaoks rakenduse sees.

Seega ei ole leevendus „kasuta Reactit” - see on mitte kunagi määrata ründaja kontrollitud baite valamusse, mis tõlgendab HTML-i või käivitab skripti, kui ülevaadatud torujuhe ei ole neid vähendanud turvalisele tüübile.

Salvestatud, peegeldatud ja DOM-põhine XSS (mehaanika)

Salvestatud XSS - Usaldamata HTML (või kasutuskoormus, mis muutub HTML-iks pärast mallide laiendamist) on püsiv (DB, vahemälu, otsing indeks). Iga kasutaja, kes laadib selle kirje, tabab valamu. Esikülje mõju: esimene renderdus , mis teeb innerHTML = row.body või samaväärse ilma sanitisatsioonita, käivitab kasutuskoormuse.

Peegeldatud XSS - Kasutuskoormus ei püsi kunagi ; see põrkab serverist või marsruutimiskihist vastusesse. Klassika: ?q=<script>…</script> peegeldub HTML-i ilma kodeeringuta. SPA ekvivalent : location.search või räsi parsitud kliendi poolel ja kirjutatud DOM-i ilma kodeeringuta. Parandus on sama: kohtuvad stringid kui andmed ; kui peate neid peegeldama, kodeerige konteksti jaoks (vt allpool).

DOM-põhine XSS - Serveri vastus on „puhas”, kuid kliendi poolel skript loeb ründaja kontrollitud sisendit (location, referrer, postMessage, WebSocket sõnumid) ja edastab selle valamusse. Näide: eval("handle" + location.hash.slice(1)) või element.innerHTML = decodeURIComponent(...). Staatiline mallide analüüs ei piisa; peate auditeerima iga teed usaldamata sisendist valamusse.

Valamud: API-d, mis muudavad stringid käivitamiseks või HTML-iks

Need on tavaliselt süüdlased React/SPA koodibaasides:

ValamuRisk
element.innerHTML, insertAdjacentHTMLParsib HTML-i; iga silt/sündmuste käitleja, mida lubate, võib käivitada skripti.
dangerouslySetInnerHTMLSama kui ülal - React ei sanitize.
document.writeSama.
eval, new Function, setTimeout(string)Otsene skripti käivitamine.
javascript: URL-id href / srcNavigeerimine või ressursi laadimine, mis käivitub kui skript URL.
postMessage käitlejad, mis eval või määravad HTML-i event.data põhjalXSS, kui origin ei ole valideeritud või event.data jõuab valamusse ilma turvalise lepinguta - mitte ainult „vale aken” vead.

Mitte valamu vaikimisi: textContent, createTextNode, Reacti tavaline tekstilaps, atribuudid, mida React käsitleb stringidena, kui te ei möödu tema põgenemisest. Markdown torujuhtmed muutuvad valamuteks, kui nad väljastavad toor-HTML-i ja määrate selle HTML-i DOM-ile ilma sanitisatsioonita.

Demo märkus (oluline): HTML5-s <script> sõlmed, mis on sisestatud innerHTML / dangerouslySetInnerHTML kaudu, ei käitu - parser ei käivita neid klassikalise peegeldatud XSS-i korral. Et näha käivitumist, kui HTML on sisestatud, kasutavad kasutuskoormused tavaliselt atribuutide käitlejaid (nt onerror on img) või sarnast. insecure-patterns dokument demo hoidlas selgitab seda välja, et saate rakendust testida näidetega, mis tegelikult käivituvad pärast sisestamist.

Leevendus 1: kontekstikohane kodeering vs sanitisatsioon

  • Kui UI vajab ainult lihtteksti - Siduge tekst textContent , Reacti tekstilapsed või MDX, mis kompileerub komponentideks ilma HTML-i torujuhtmeta. Sanitiseerijat ei nõuta; te ei ole HTML-i mängus.

  • Kui vajate rikast teksti (paks, loendid, lingid) - Teil on vaja kas piiratud märgistust, mis kompileerub turvalistele elementidele või HTML-i sanitisatsiooni lubatud nimekirjaga (sildid + atribuudid). Kodeering (nt HTML-entiteedi põgenemine) on andmete paigutamiseks HTML-tekstisõlmedesse ; sanitisatsioon on kui peate lubama HTML-i alamhulka. Ärge segi neid kahte.

  • Kaitse sügavus rikkaliku teksti jaoks reaalsetes toodetes - Valideerige/saniteerige kirjutamisel (API keeldub tundmatutest siltidest, pikkuse piirangutest) ja saniteerige või renderige turvalise tee kaudu lugemisel (renderduskiht). Salvestust saab tagasi võtta, rikkuda või kirjutada teise teenuse versiooni.

Leevendus 2: DOMPurify (ja kuidas seda tõsiselt kasutada)

DOMPurify on brauseri sanitiseerija vaikimisi profiiliga; te konfigureerite selle oma toote jaoks:

  • ALLOWED_TAGS / ALLOWED_ATTR - Alustage minimaalselt (p, br, strong, em, a ainult href lubamisel). Iga lisa silt on rünnakupind.
  • ADD_ATTR / FORBID_TAGS - Selge ületab „lubab peaaegu kõike”.
  • RETURN_DOM / RETURN_TRUSTED_TYPE - Eelistage DOM-sõlmi või TrustedHTML-stiilis väljundit, kui integreerite Trusted Types-iga.
  • Konkureerige afterSanitizeAttributes - Eemaldage href väärtused, mis algavad javascript: või veidrate data: MIME-tüüpidega, kui lubate linke.

frontend-xss-demo main -s on turvaline marsruut (/secure) käitab todo teksti läbi DOMPurify enne dangerouslySetInnerHTML ; turvatu marsruut (/insecure**) ei tee seda - sama UI, erinev usalduspoliitika. Portugali /seguro ja /inseguro on endiselt olemas kui pärandi aliasid ja suunavad vastavalt /secure ja /insecure .

Leevendus 3: Content-Security-Policy (piirangud, mitte asendus)

CSP vähendab mis võib käivituda, kui midagi libiseb läbi. Apps/web/next.config.ts selles saidis seab CSP-i default-src 'self' , range object-src 'none' , base-uri 'self' , form-action 'self' , frame-ancestors 'none' , pluss script-src / style-src koos 'unsafe-inline' , kuna Next.js App Router + MUI sx nõuavad praegu selles seadistuses sisemisi skripte/stiile - dokumenteeritud koodis. Nonce- või hash-põhine script-src eemaldaks laia sisemise skripti lubamise, kuid nõuab vahendvara nõuete süstimiseks.

Reaalsuse kontroll: CSP ei asenda sanitisatsiooni kasutaja HTML-i jaoks; see ** kitsendab** plahvatusraadiust (nt võib blokeerida skripti hostid, mida te ei lubanud). Sisemised sündmuste käitlejad (onerror jne) ei ole automaatselt neutraliseeritud ainult CSP-i tõttu - 'unsafe-inline' on script-src levinud reaalsetes rakendustes (sh selle saidi Next/MUI seadistus), ja blokeerivad käitlejad tavaliselt nõuavad selgesõnalist script-src / script-src-attr (või nonce/hashes), sõltuvalt brauserist ja CSP tasemest.

Leevendus 4: küpsised ja CSRF (paariga XSS)

XSS võib mööda CSRF-märke , kui märk on lugemisvõimeline DOM-ist või kui ründaja skript teeb päringuid volitustega. Seega: eelistage XSS-i parandusi ; ka:

  • Sessiooniküpsised: HttpOnly, Secure, SameSite=Lax või Strict , kus voogud lubavad - vähendab rist-site küpsiste lekkimist ja klassikalist CSRF-i.
  • Muutuvad lõpppunktid: paaritage SameSite küpsistega, anti-CSRF märgistega või kohandatud päistega + CORS poliitikaga, et juhuslikud saidid ei saa saata nõudeid volitustega.

Esikülje töö: ärge pange saladusi JS-lugemiseks salvestusse, kui vältida; kasutage fetch koos selgesõnalise credentials poliitikaga, mis on seotud teie API disainiga.

See koodibaas (konkreetne)

  • Päised / CSP - apps/web/next.config.ts: turvapäised /(.*) ; CSP string ehitatud koodis koos keskkonna-spetsiifilise script-src (dev unsafe-eval ainult React virnade jaoks, kus vaja).
  • Chat API - apps/web/app/api/chat/route.ts: JSON parsib, tühja kontroll, MAX_MESSAGE_LENGTH kork - kuritarvitamise kujundamine, mitte XSS ise.
  • Blog - apps/web/lib/blog/mdx.tsx: MDX fikseeritud komponendi kaardiga (next-mdx-remote/rsc), mitte toor-HTML stringid CMS-ist. Erinev ohtude mudel kui „sõnumikeha koos HTML-iga”.

Käivitatav võrdlus: mida main annab

Kloonige frontend-xss-demo, käitage npm install ja npm run devVite serveerib rakendust http://localhost:5173.

Võrrelge /insecure vs /secure Elements ja Console : samad komponendid, erinev stringi käsitlemine enne DOM-i jõudmist.

Kontroll-loend (rakendustasand)

  1. Inventariseerige valamu - rg "dangerouslySetInnerHTML|innerHTML|insertAdjacentHTML|eval\\(|new Function" teie rakenduses ja sõltuvustes.
  2. Rikkalik tekst - Lubatud sanitiseerija igal teel HTML-i; ühik-testid kasutuskoormustega nagu <img src=x onerror=...>, javascript: URL-id - ja pidage meeles innerHTML ei käitu <script> , nagu paljud petulehed viitavad. innerHTML -stiili süstimisdemonstratsioonide jaoks eelistage onerror on img (või sarnast) ; <svg onload> on sageli ebakindel , kui seda sisestatakse sellisel viisil.
  3. URL-parameetrid → DOM - Ärge määrutage kunagi otsingut/räsi HTML-iks; kui peate kuvama, tekst või kodeerige konteksti jaoks.
  4. Markdown - Saniteerige pärast täielikku MD → HTML konversiooni; keelake toor-HTML Markdownis, kui toode lubab.
  5. CSP - Kinnitage järk-järgult; kasutage arenduskeskkonnas Report-Only, kui vaja.
  6. Küpsised / API - Kohandage SameSite, volitused ja CSRF strateegia koos backendiga; eeldage, et XSS ja CSRF saavad aheldatud.

Kokkuvõte

XSS on juhtimisvoog : andmed, mis ületavad tõlgendamist. Kaitse on tüpiseerimine piiril : lihttekst, turvalised struktureeritud komponendid või saniteeritud HTML minimaalse lubatud nimekirjaga - pluss CSP ja küpsiste semantika, mis piirab, mida hulkuv skript veel teha saab. Demo main -s muudab selle piiri nähtavaks: /insecure vs /secure, dokumenteeritud mõjud docs/xss/ all ja distsipliin igas PR-is, mis puudutab stringe lähedal DOM-ile.