Same-Origin Policy & CORS
Same-Origin Policy (SOP)
The browser's most fundamental security boundary. Two URLs share an origin only when all three parts match: protocol, host, and port.
| URL | Compared to https://app.io:443/page | Same Origin? |
|---|---|---|
https://app.io:443/other | Same protocol, host, port | Yes |
http://app.io:443/page | Different protocol | No |
https://api.app.io:443/page | Different host (subdomain) | No |
https://app.io:8080/page | Different port | No |
SOP blocks cross-origin reads by default: scripts on origin A cannot read responses from origin B. Writes (form submissions, navigations) and embeds (images, scripts, iframes) are generally allowed.
Cross-Origin Resource Sharing (CORS)
CORS is the opt-in mechanism that lets a server relax SOP for specific origins. The server declares who may read its responses via Access-Control-* headers.
Simple requests (GET/POST with standard headers and content types like text/plain) skip preflight. Preflighted requests (custom headers, methods like PUT/DELETE, or JSON content type) trigger an OPTIONS preflight first.
# Key CORS response headers
Access-Control-Allow-Origin: https://app.io
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true is rejected by browsers. If you need credentials, you must specify the exact origin.
Cross-Site Scripting (XSS)
XSS lets an attacker inject malicious scripts into pages viewed by other users. The browser executes the script in the context of the victim's session, giving access to cookies, DOM, and APIs.
Malicious input is echoed back in the server response. Requires the victim to click a crafted link. Example: search query reflected in results page without escaping.
Payload is persisted (database, comment, profile field) and served to every user who views the page. More dangerous because no user interaction beyond visiting the page is needed.
The vulnerability is entirely in client-side JavaScript. The page reads from a source (e.g., location.hash) and writes to a sink (e.g., innerHTML) without sanitization.
Prevention
| Defense | What It Does | Limitation |
|---|---|---|
| Output escaping | Encode < > & " ' before inserting into HTML | Must be context-aware (HTML vs. JS vs. URL) |
| Input sanitization | Strip or neutralize dangerous tags/attributes (e.g., DOMPurify) | Allow-lists are fragile; keep libraries updated |
| Content Security Policy | Browser-enforced allow-list of script sources | Complex to deploy; unsafe-inline weakens it |
HttpOnly cookies | Prevents document.cookie access from JS | Doesn't prevent XSS itself, only limits cookie theft |
// BAD: DOM-based XSS
document.getElementById('output').innerHTML = location.hash.slice(1);
// GOOD: use textContent instead
document.getElementById('output').textContent = location.hash.slice(1);
Cross-Site Request Forgery (CSRF)
CSRF tricks a logged-in user's browser into making an unwanted request to a site where they're authenticated. The browser automatically attaches cookies, so the server sees a legitimate session.
Prevention
| Defense | How It Works |
|---|---|
| CSRF tokens | Server embeds a random token in forms; verifies it on submission. The attacker's page cannot read the token due to SOP. |
SameSite cookies | SameSite=Strict blocks the cookie on all cross-site requests. SameSite=Lax allows top-level GET navigations but blocks cross-site POST. |
| Double-submit cookie | Token sent as both a cookie and a request parameter. Server checks they match. No server-side state needed. |
| Custom request headers | Require a custom header (e.g., X-Requested-With) that triggers CORS preflight, which the attacker's form cannot set. |
SameSite=Lax, which blocks cross-site POST requests from automatically attaching cookies. This significantly reduces CSRF risk without any server-side changes.
Content Security Policy & Cookies
Content Security Policy (CSP)
CSP is a response header that tells the browser which sources of content are allowed. It is the strongest defense against XSS because it prevents inline scripts and restricts where scripts can load from.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-abc123' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src *;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
| Directive | Controls | Example |
|---|---|---|
default-src | Fallback for all resource types | 'self' |
script-src | JavaScript sources | 'nonce-xyz' https://cdn.io |
style-src | CSS sources | 'self' 'sha256-...' |
img-src | Image sources | * data: |
connect-src | Fetch, XHR, WebSocket targets | 'self' https://api.io |
frame-ancestors | Who can embed this page in an iframe | 'none' (prevents clickjacking) |
Nonces: Generate a random nonce per request and include it in both the CSP header and each <script nonce="..."> tag. Only scripts with the matching nonce execute. Hashes: Alternatively, specify the SHA-256 hash of the inline script content in the CSP header.
'unsafe-inline' and 'unsafe-eval' defeat the purpose of CSP. If you must use inline scripts, use nonces or hashes instead.
Cookie Security Attributes
| Attribute | Purpose | Recommended Value |
|---|---|---|
Secure | Cookie only sent over HTTPS | Always set for session cookies |
HttpOnly | Blocks JavaScript access via document.cookie | Always set for session cookies |
SameSite | Controls cross-site cookie sending | Lax (default) or Strict |
Domain | Which domains receive the cookie | Omit to restrict to exact origin |
Path | URL path prefix required for sending | / unless scoping is needed |
Set-Cookie: session=abc123;
Secure;
HttpOnly;
SameSite=Lax;
Path=/;
Max-Age=3600
Clickjacking, SSRF & More
Attacker overlays an invisible iframe of the target site over a decoy page. User thinks they're clicking the decoy but actually interacts with the hidden site. Defend with frame-ancestors 'none' in CSP or X-Frame-Options: DENY.
Server-Side Request Forgery: attacker tricks the server into making requests to internal services (e.g., cloud metadata endpoints at 169.254.169.254). Defend with allow-lists for outbound URLs and blocking private IP ranges.
Application redirects to a user-supplied URL without validation. Attacker uses it for phishing: app.com/redirect?url=evil.com. Defend with allow-lists of permitted redirect domains.
Attacker injects SQL through user input that is concatenated into queries. Defend with parameterized queries / prepared statements, never string concatenation.
User input passed to shell commands (e.g., os.system("ping " + ip)). Defend by avoiding shell calls; use language-native libraries or strict input validation.
Attacker uses ../ sequences to access files outside the intended directory. Defend by canonicalizing paths and validating they stay within the allowed root.
OWASP Top 10 Overview
The OWASP Top 10 is a widely-adopted awareness document for web application security. The 2021 edition reorganized categories based on data and community input.
| # | Category | Key Concern |
|---|---|---|
| A01 | Broken Access Control | Users acting beyond their permissions; IDOR, missing function-level access control |
| A02 | Cryptographic Failures | Sensitive data exposure; weak encryption, plaintext storage, missing TLS |
| A03 | Injection | SQL, NoSQL, OS, LDAP injection via untrusted input in queries/commands |
| A04 | Insecure Design | Flawed architecture; missing threat modeling, no security by design |
| A05 | Security Misconfiguration | Default credentials, open cloud storage, verbose error messages, missing headers |
| A06 | Vulnerable Components | Outdated libraries, frameworks with known CVEs |
| A07 | Auth & Identification Failures | Broken authentication, session fixation, weak passwords |
| A08 | Software & Data Integrity Failures | Untrusted CI/CD pipelines, insecure deserialization, unsigned updates |
| A09 | Security Logging & Monitoring Failures | No audit trail, alerts, or incident detection |
| A10 | Server-Side Request Forgery | SSRF via unvalidated URLs fetched by the server |
Test Yourself
HttpOnly cookie attribute prevent?SameSite cookie value blocks the cookie on all cross-site requests, including top-level navigations?http://169.254.169.254/latest/meta-data/. What type of attack is this?