# Forhindr XSS i PHP: afvær cross-site scripting sikkert

> Forhindr XSS i PHP: kod output rigtigt efter kontekst, brug skabeloner med auto-escaping og sæt en Content Security Policy. Med eksempler.

Kilde: https://phpsos.de/da/ratgeber/xss-php/  
Status: 25.10.2026  
Udbyder: PHP SOS, PHP-nødhjælp og serversikkerhed. Akut hjælp på WhatsApp: https://wa.me/491736494914?text=Hej%20PHP%20SOS%2C%20vi%20har%20brug%20for%20hj%C3%A6lp%20med%20vores%20PHP-server.%20Firma%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kort sagt:** XSS i PHP forhindrer I ved at kode hvert output passende til sin kontekst: `htmlspecialchars()` til HTML, `json_encode()` med beskyttelsesindstillinger til JavaScript, kontrollerede URL'er til links. Skabeloner med automatisk escaping tager meget af det fra jer. En Content Security Policy og HttpOnly-cookies begrænser skaden, hvis en sårbarhed alligevel består.

## Hvorfor cross-site scripting bør kontrolleres med nu

Opmærksomheden ligger i oktober 2026 på PHP2Shell, to kritiske sårbarheder i PHP-fortolkeren, som der siden 21.10.2026 findes patches til. Mange virksomheder lader i den forbindelse deres webapplikationer gennemgå. Det er en god anledning til også at se på svagheder i egen kode, som ingen PHP-opdatering udbedrer.

Cross-site scripting, kort XSS, er en af dem. OWASP Top 10 fører XSS inden for kategorien Injection. I modsætning til en sårbarhed i serveren rettes angrebet her mod besøgende og brugere af jeres website, f.eks. mod kunder i shoppen eller medarbejdere i backend.

Følgerne kan være betydelige: overtagne sessioner hos administratorer, manipulerede formularer, der sender data til tredjepart, eller forfalsket indhold på jeres website. Bliver en administratorkonto overtaget, kan det føre til fuldstændig kontrol over applikationen.

## Hvordan opstår XSS i PHP-applikationer?

XSS opstår, når en applikation udskriver data fra en ikke-troværdig kilde ukontrolleret på en side. Browseren kan så ikke længere skelne mellem jeres indhold og indsmuglet kode og udfører begge dele.

Man skelner mellem tre former. Ved reflekteret XSS stammer indholdet direkte fra den aktuelle forespørgsel, f.eks. fra et søgeord i URL'en. Ved gemt XSS ligger det i databasen, for eksempel i en kommentar eller et profilfelt, og rammer enhver, der kalder siden. DOM-baseret XSS opstår i browserens JavaScript, når scripts skriver værdier fra URL'en ind på siden.

Ikke-troværdige er alle data, der ikke står fast i koden: formularfelter, URL-parametre, cookies, filnavne, svar fra eksterne grænseflader og indhold fra egen database, som på et tidspunkt kom udefra.

## Kod output rigtigt efter kontekst

Den vigtigste regel lyder: Kod ved udlæsningen, og passende til det sted, hvor værdien havner. Til HTML-indhold og attributter i anførselstegn er `htmlspecialchars()` det rette middel. En lille hjælpefunktion forhindrer, at indstillinger glemmes:

```php
<?php
function e(?string $wert): string
{
    return htmlspecialchars($wert ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
}
?>
<h2><?= e($produkt['name']) ?></h2>
<input type="text" name="suche" value="<?= e($_GET['q'] ?? '') ?>">
```

Andre kontekster kræver andre midler:

| Kontekst | Sikker metode |
|---|---|
| HTML-indhold, attribut i anførselstegn | `htmlspecialchars()` med `ENT_QUOTES` og UTF-8 |
| Data til JavaScript | `json_encode()` med `JSON_HEX_TAG`, `JSON_HEX_AMP`, `JSON_HEX_APOS`, `JSON_HEX_QUOT` |
| Parameter i en URL | `rawurlencode()` for den enkelte værdi |
| Fuldstændig URL fra brugerinput | Kontrollér skemaet, tillad kun `https` og `http`, derefter `htmlspecialchars()` |
| Attributter uden anførselstegn, event handlers, CSS | undgå, sæt ikke værdier ind dér |

Til data, som et script har brug for, er en dataattribut eller en JSON-blok som regel den reneste vej:

```php
<?php
$daten = json_encode($konfiguration, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>
<div id="app" data-konfig="<?= e($daten) ?>"></div>
```

I JavaScript læser I derefter værdien ud via dataattributten og sætter indhold med `textContent` i stedet for med `innerHTML`.

## Skabeloner, HTML-rensning og frameworks

Skabelonmotorer som Twig og Blade koder output automatisk som standard. Det udelukker en stor del af de typiske fejl. Farligt bliver det dér, hvor denne adfærd bevidst slås fra, f.eks. med filteret `raw` i Twig eller syntaksen `{!! !!}` i Blade. Sådanne steder bør være sjældne i koden og målrettet kontrolleres ved hver gennemgang.

Skal jeres applikation bevidst tillade HTML fra brugere, f.eks. formateringer i kommentarer eller i et redaktionssystem, er kodning ikke nok, fordi HTML'en jo skal vises. Så har I brug for et gennemprøvet rensebibliotek som HTML Purifier, der kun lader en fast liste af tilladte elementer og attributter igennem. Egne filtre med regulære udtryk er erfaringsmæssigt hullede.

> **Vigtigt:**
> Kontrollér især backend-områder. XSS i visninger, som kun administratorer ser, undervurderes ofte, men rammer netop de konti med flest rettigheder.

## Find XSS i eksisterende PHP-kode

I vokset kode er det første spørgsmål som regel ikke, hvordan man koder rigtigt, men hvor det hidtil er blevet glemt. En pragmatisk start er søgningen efter steder, hvor variabler udskrives direkte:

```bash
# Direkte udskrivning af forespørgselsdata
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Slået fra auto-escaping i Twig og Blade
grep -rnE "\|raw|\{!!" templates/ resources/views/
```

Fund er kontrolopgaver, ikke færdige resultater. Gemt XSS finder sådan en søgning kun delvist, fordi værdierne dér kommer fra databasen. Kontrollér derfor desuden alle visninger, der viser indhold fra brugere: kommentarer, profiler, ordrenoter, supporttickets. Ved CMS og shops hører plugins og temaer fra tredjepart udtrykkeligt med, for dér sidder en stor del af udlæsningslogikken.

## Anden forsvarslinje: CSP og sikre cookies

Også ved omhyggelig kode kan et sted blive overset. To foranstaltninger begrænser da skaden. En Content Security Policy tillader kun browseren scripts fra fastlagte kilder og forbyder indlejrede scripts, medmindre de er udtrykkeligt frigivet:

```apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
```

Indfør sådan en politik trinvis, først i ren rapporttilstand via `Content-Security-Policy-Report-Only`, for eksisterende integrationer af analyse- eller betalingstjenester skal som regel suppleres. Yderligere headere beskriver vejledningen [Sikkerhedsheadere til websites](https://phpsos.de/da/ratgeber/sicherheits-header/).

Desuden bør sessionscookies bære attributten `HttpOnly`, så scripts ikke kan læse dem. Hvordan I sikrer sessioner samlet set, står i vejledningen [Sikr sessioner i PHP](https://phpsos.de/da/ratgeber/session-sicherheit-php/). En beslægtet svaghed på databasesiden behandler [Forhindr SQL-injection i PHP](https://phpsos.de/da/ratgeber/sql-injection-php/).

## Hvordan hjælper PHP SOS konkret?

- Vi gennemgår skabeloner og output i jeres applikation for manglende eller forkert kodning.
- Vi vurderer fund efter tilgængelighed og berørte konti og viser konkrete rettelser.
- Vi indfører en Content Security Policy trinvis uden at forstyrre shop- eller betalingsfunktioner.
- Vi tester igen efter rettelsen og dokumenterer resultatet.

En uafhængig kontrol beskriver ydelsen [Sikkerhedsaudit og penetrationstest](https://phpsos.de/da/leistungen/sicherheitsaudit-penetrationstest/).


## Ofte stillede spørgsmål

**Er htmlspecialchars nok mod XSS?**  
Til almindelig tekst i HTML-indhold og i attributter i anførselstegn ja, hvis I angiver ENT_QUOTES og tegnsættet UTF-8. I JavaScript, i URL'er eller i CSS gælder andre regler, dér kræves en egen, passende kodning.

**Skal jeg filtrere input ved lagring eller ved udlæsning?**  
Kod ved udlæsningen, passende til målet. Input bør I ved modtagelsen kontrollere for plausibilitet, f.eks. længde og format. Den, der kun filtrerer ved lagring, overser data fra andre kilder og besværliggør senere brug i andre kontekster.

**Hvad kan en Content Security Policy?**  
En Content Security Policy fastlægger, fra hvilke kilder browseren må indlæse og udføre scripts. Den kan tydeligt begrænse følgerne af en overset XSS-sårbarhed, men erstatter ikke korrekt kodning.

**Hvordan tillader jeg formateringer som fed eller links i kommentarer?**  
Brug et gennemprøvet HTML-rensebibliotek med en hvidliste over tilladte elementer og attributter. Egne regulære udtryk til at fjerne scripts er fejlbehæftede og bør ikke bruges.

## Kilder

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [php.net: Sikkerhedsmeddelelser](https://www.php.net/)
