Yesterday, the attack worked only if a victim clicked a malicious link. Today? The payload lives inside the application and attacks everyone automatically.
Welcome to Stored XSS.
π§ What Is Stored XSS?
Stored (Persistent) XSS happens when:
- User input is submitted
- The application stores it in the database
- The stored input is rendered later
- The browser executes it
Unlike reflected XSS, this one doesn't need a crafted link every time.
The attack persists. The application does not sanitize or encode user input before storing it.
That's the root cause.
π§ͺ Lab Overview
Vulnerability: Stored Cross-Site Scripting Severity: High (CVSS 8.6)
The vulnerable area:
- Stored messages: chat section/ Comment section etc..
User messages were saved directly and rendered without encoding.
π£ Proof of Concept
π― Payload Used (From Report)
<script>alert('stored')</script>
π Steps To Reproduce
- Navigate to the Stored Message section.
- Enter the payload inside the message field:
- <script>alert('stored')</script>
- Submit the message.
- Refresh or revisit the page.
π₯ Result
The JavaScript executes automatically.
No clicking required.
Anyone who loads that page becomes a victim.
πΈ Screenshot evidence appears on Page 5 of the report
PW VAPT REPORT ISSAN PDF
.
β οΈ Why This Is More Dangerous Than Reflected XSS
Let's compare:
Reflected XSS
Stored XSS
Requires crafted link
Executes automatically
Affects one victim at a time
Affects all users
Temporary
Persistent
MediumβHigh impact
Very High impact
Stored XSS spreads like infection.
If an admin views the page? Game over.
𧨠Real-World Impact
An attacker can:
- πͺ Steal session cookies
- π Hijack accounts
- π Inject fake login forms (phishing)
- π§Ύ Modify the DOM
- π Take over admin accounts
- π¦ Spread malicious payloads to every visitor
This is why it scored:
CVSS: 8.6 (High)
π¬ Why It Works (Technical Breakdown)
Imagine backend logic like:
$message = $_POST['message'];
INSERT INTO chat (msg) VALUES ('$message');
Then later:
echo $row['msg'];
No encoding. No filtering. No escaping.
So the database stores raw JavaScript.
And the browser executes it.
π‘οΈ How To Fix It
β 1. Output Encoding (Mandatory)
echo htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
β 2. Sanitize Input
Use:
- DOMPurify
- HTMLPurifier
β 3. Implement CSP
Content-Security-Policy: script-src 'self'
β 4. Disable Inline JS
Block:
- <script>
- onerror
- onclick
- onmouseover
π§© Beginner Explanation
Reflected XSS = You send attack β server reflects it β victim clicks β it runs.
Stored XSS = You inject attack β server saves it β every visitor runs it automatically.
One is a spear.
The other is a landmine.
π― Day 2 Key Takeaway
If user input is stored and later rendered:
It must be encoded on output.
Always.
Never trust:
- Forms
- Comments
- Chat messages
- Profile fields
- Headers
- URLs
Everything is attacker-controlled.

Tomorrow we go deeper!