A series of 5 hands-on labs exploring browser security mechanisms using a simulated WebMail application built with Node.js/Express.
The project simulates a multi-origin web ecosystem:
| Server | Port | Role |
|---|---|---|
| GoodHost | 3000 | Main WebMail application |
| StaticHost (CDN) | 8001 | Static assets (JS, CSS, images) |
| TrustCo (Partner) | 4000 | Support chat widget |
| WeatherApp (Utility) | 8002 | Weather widget / Attacker |
| Proxy | 8080 | MitM proxy (Lab 5 only) |
Concept: Same-Origin Policy (SOP) and CORS
The browser blocks cross-origin fetch requests by default (Same-Origin Policy).
support.js from port 4000 cannot fetch data from port 3000 without CORS headers.
Adding cors() middleware to the servers allows cross-origin communication.
All external scripts load successfully and the Support Chat works.
WeatherApp switched to breach1 mode executes a malicious script that reads
document.cookie and innerHTML — demonstrating XSS via third-party script trust.
Concept: Using HTTP headers to restrict which resources the browser may load.
All external resources are blocked — scripts, styles, images from other origins.
Maximum security breaks all third-party integrations.
A granular policy allows trusted origins (4000, 8001) while blocking the malicious
WeatherApp (8002). Even with WeatherApp in breach1 mode, the alert never fires.
Concept: Subresource Integrity (SRI) — cryptographic hash verification of scripts.
The CDN is compromised — react-mock.js returns a malicious alert.
CSP cannot detect this because port 8001 is trusted. The alert fires.
Adding integrity="sha256-..." to the script tag causes the browser to verify
the file hash. The compromised script hash does not match — it is blocked.
Updating the CDN to v1.0.1 (a legitimate change) breaks the app until
the integrity hash is updated. SRI enforces disciplined version management.
Concept: Cookie security flags and their effect on session token protection.
Cookie set without any flags. document.cookie exposes the session token to JavaScript.
WeatherApp in breach2 mode silently exfiltrates the cookie via fetch().
The stolen SessionID appears in the attacker's server terminal — no visible alert.
HttpOnly hides the cookie from document.cookie. JavaScript returns empty string.
The attacker's fetch() sends nothing useful — theft is prevented.
Cookie scoped to Path=/api. Sent only to /api/* routes, not to the root path.
Cookie present on /api/emails:

Concept: The Secure flag and protection against network-level interception.
A proxy server sits between browser and app. Even with HttpOnly set,
the proxy reads raw HTTP headers and logs the session token in plain text.
Adding Secure to the cookie instructs the browser to only send it over HTTPS.
Over plain HTTP (via the proxy), the cookie is withheld by the browser.
Note: Chrome makes a localhost exception, but the Set-Cookie header confirms the flag is set.
| Lab | Mode | Flags | Attack Result |
|---|---|---|---|
| Lab 4 Task 1 | task1-naive |
none | Cookie stolen via JS |
| Lab 4 Task 2 | task1-naive |
none | Cookie silently exfiltrated |
| Lab 4 Task 3 | task3-httponly |
HttpOnly |
JS theft prevented |
| Lab 4 Task 4 | task4-path |
HttpOnly; Path=/api |
Scope limited |
| Lab 5 Task 1 | task5-httponly |
HttpOnly |
MitM still works |
| Lab 5 Task 2 | task5-secure |
HttpOnly; Secure |
MitM prevented |
# Install dependencies in each server folder
cd labN/goodhost && npm install
cd labN/statichost && npm install
cd labN/trustco && npm install
cd labN/weatherapp && npm install
# Start servers (4 terminals)
node server.js # goodhost (port 3000)
node server.js # statichost (port 8001)
node server.js # trustco (port 4000)
node server.js --mode breach1 # weatherapp (port 8002)
# Lab 5 only — proxy
cd lab5/proxy && npm install
node server.js --mode breach # proxy (port 8080)












