Security Academy→Course→Input Security
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?