Modul 7 / 12
Input Security
XSS, injection og path traversal — sårbar og sikker kode side om side.
XSS-typene
- ›Stored: payload lagres (kommentar, profilnavn) og kjøres for andre brukere
- ›Reflected: payload i URL/parametre speiles tilbake i respons
- ›DOM-based: klientkode tar location.hash/search og skriver til innerHTML
- ›HTML injection: ikke JS, men falske skjema/lenker injisert i siden
Vulnerable · tsx
// Markdown/rich text fra bruker
<div dangerouslySetInnerHTML={{ __html: comment.body }} />
// DOM-based
el.innerHTML = new URLSearchParams(location.search).get("q");
// Lenke fra bruker
<a href={profile.website}>Website</a> // javascript: URL Secure · tsx
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(marked(comment.body)),
}} />
// eller enda bedre: render som tekst
<p>{comment.body}</p>
const safe = /^https?:\/\//i.test(url) ? url : "#";
<a href={safe} rel="noopener noreferrer">Website</a>React escaper {tekst}. Risikoen oppstår ved dangerouslySetInnerHTML, href/src fra bruker, og tredjepartsbiblioteker som skriver HTML.
Ufarlig testpayload (kun egen app)
Bruk payloads som er synlige, ikke skadelige. Lagre i et felt, se om det tolkes som HTML.
<b>bold-test</b>
<img src=x onerror="console.log('XSS-test-123')">
javascript:console.log('href-test')SQL- og command injection
Query builders og parametriserte spørringer beskytter. Feilen oppstår i rå SQL, rpc-funksjoner med dynamisk SQL og shell-kall.
Vulnerable · ts
await sql.unsafe(`select * from items where name = '${q}'`);
-- plpgsql
execute 'select * from items order by ' || sort_col;
exec(`convert ${file.name} out.png`); Secure · ts
await sql`select * from items where name = ${q}`; // parametrisert
-- allowlist
if sort_col not in ('name','created_at') then raise exception 'bad'; end if;
execute format('select * from items order by %I', sort_col);
execFile("convert", [safePath, "out.png"]);Path traversal og filnavn
Bruk aldri brukerens filnavn som sti. ../../ eller absolutte stier kan peke ut av mappen.
Vulnerable · ts
const path = `uploads/${req.query.file}`;
return fs.readFile(path); // ?file=../../.env Secure · ts
const id = z.string().uuid().parse(req.query.file); const row = await getFileOwnedBy(userId, id); return storage.download(row.storage_key); // nøkkel generert av server
Kontroller i din egen app
Labs for denne modulen
Quiz
Scenario 1
En chat-app rendrer AI-svar med dangerouslySetInnerHTML for å støtte markdown. Brukerens prompt påvirker svaret. Risiko?
Scenario 2
Supabase .eq('name', q) med brukerinput — SQL injection?