পাঠ ৩৭ · ৫৮-এর মধ্যে · মডিউল ৮
Home / Courses / Full-Stack Web Frameworks / CSRF, CORS ও XSS বেসিক

ফুল-স্ট্যাক অ্যাপ সুরক্ষিত করা — CSRF, CORS ও XSS বেসিক

Securing full-stack apps — CSRF, CORS & XSS basics
১০ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • CSRF, CORS, ও XSS — তিনটি সাধারণ ওয়েব-নিরাপত্তা সমস্যার সংক্ষিপ্ত ধারণা
  • একটি সত্যিকারের HTML-এস্কেপিং ফাংশন লিখে XSS ইনজেকশন নিষ্ক্রিয় করা
  • একটি সত্যিকারের allow-list-ভিত্তিক CORS origin-checking ফাংশন লেখা
  • কেন এই সুরক্ষাগুলো ফ্রেমওয়ার্ক-স্তরে ডিফল্ট হিসেবে থাকা উচিত, প্রতিবার হাতে লেখার বদলে

১ · তিনটি সাধারণ সমস্যা — সংক্ষেপে

গভীর নিরাপত্তা তত্ত্ব ও আক্রমণ-প্রতিরোধের প্রতিটি খুঁটিনাটি ../cybersecurity/ কোর্সের বিষয়; এখানে শুধু ফুল-স্ট্যাক অ্যাপ তৈরির সময় এই তিনটি নাম কেন গুরুত্বপূর্ণ তা সংক্ষেপে দেখা যাক।

CSRF
একজন লগ-ইন-করা ইউজারকে না জানিয়ে তার ব্রাউজার দিয়ে একটি অনাকাঙ্ক্ষিত রিকোয়েস্ট পাঠানো — CSRF টোকেন যাচাই করে ঠেকানো হয়।
CORS
ব্রাউজার ডিফল্টভাবে একটি origin-এর JavaScript-কে আরেকটি origin-এর ডেটা পড়া থেকে আটকায় — সার্ভার নির্দিষ্ট origin-কে অনুমতি দিলেই তা সম্ভব হয়।
XSS
ইউজারের দেওয়া ইনপুট (যেমন কমেন্ট) escape না করে সরাসরি HTML-এ বসালে সেটি স্ক্রিপ্ট হিসেবে চলে যেতে পারে।

২ · XSS ঠেকানো — একটি সত্যিকারের HTML-এস্কেপিং ফাংশন

নিচের কোডে একটি বাস্তব html_escape() ফাংশন পাঁচটি বিশেষ ক্যারেক্টার (&, <, >, ", ') তাদের HTML entity সমতুল্যে রূপান্তর করে। একটি ক্ষতিকর-দেখতে ইনপুট স্ট্রিং র (raw) ও escape-করা — দুই অবস্থায় পাশাপাশি দেখানো হয়েছে।

Python
def html_escape(text: str) -> str:
    """HTML-এ বিশেষ অর্থ বহনকারী ৫টি ক্যারেক্টারকে entity-তে রূপান্তর করে,
    যাতে ব্রাউজার এগুলোকে মার্কআপ/স্ক্রিপ্ট হিসেবে না বুঝে সাধারণ টেক্সট হিসেবে দেখায়"""
    replacements = [
        ("&", "&amp;"),   # সবার আগে -- নাহলে পরের entity-গুলোর '&' আবার এস্কেপ হয়ে যাবে
        ("<", "&lt;"),
        (">", "&gt;"),
        ('"', "&quot;"),
        ("'", "&#39;"),
    ]
    escaped = text
    for char, entity in replacements:
        escaped = escaped.replace(char, entity)
    return escaped

# ব্যবহারকারীর কমেন্ট বক্সে টাইপ করা একটি ক্ষতিকর ইনপুট
user_comment = "<script>alert(1)</script>"

print("র (raw) ইনপুট -- সরাসরি পেজে বসালে ব্রাউজার একে <script> ট্যাগ হিসেবে চালাত:")
print(user_comment)

safe_comment = html_escape(user_comment)
print("\nescape করা ইনপুট -- এখন নিরাপদে প্লেইন টেক্সট হিসেবে দেখানো যায়:")
print(safe_comment)

print("\nরাউ ইনপুট আসলেই <script> দিয়ে শুরু কি না:", user_comment.startswith("<script>"))
print("escape করার পর কোনো '<' অক্ষর অবশিষ্ট নেই:", "<" not in safe_comment)

    
লক্ষ্য করুন replacements তালিকায় &-কে সবার প্রথমে রূপান্তর করা হয়েছে। যদি এটি শেষে করা হতো, তাহলে "<"-কে "<" লেখার সময় তৈরি হওয়া নতুন &-টিও আবার এস্কেপ হয়ে যেত (&amp;lt;-এর মতো ভুল ফলাফল) — এই ক্রম-নির্ভরতা একটি বাস্তব, সাধারণ বাগ যা এস্কেপিং ফাংশন লেখার সময় খেয়াল রাখতে হয়।

৩ · CORS — কোন origin-কে বিশ্বাস করা যাবে

ব্রাউজার ডিফল্টভাবে ধরে নেয় ভিন্ন origin থেকে আসা JavaScript আপনার API-এর রেসপন্স পড়তে পারবে না, যতক্ষণ না সার্ভার স্পষ্টভাবে সেই origin-কে অনুমতি দেয়। নিচের কোডে একটি allow-list ও একটি origin-checking ফাংশন বাস্তবায়ন করে বিভিন্ন Origin হেডার মান পরীক্ষা করা হয়েছে।

Python
allowed_origins = {
    "https://abcltech.com",
    "https://app.abcltech.com",
    "http://localhost:3000",   # ডেভেলপমেন্ট origin
}

def is_origin_allowed(origin_header: str) -> bool:
    """CORS: শুধুমাত্র allow-list-এ থাকা origin থেকে আসা রিকোয়েস্টকে
    ব্রাউজারকে ক্রস-অরিজিন রেসপন্স পড়ার অনুমতি দেওয়া হয়"""
    return origin_header in allowed_origins

test_origins = [
    "https://abcltech.com",
    "https://app.abcltech.com",
    "http://localhost:3000",
    "https://evil-attacker.com",
    "https://abcltech.com.evil-attacker.com",   # সাবডোমেইন-সদৃশ প্রতারণামূলক origin
]

print(f"{'Origin':40s} | সিদ্ধান্ত")
print("-" * 58)
for origin in test_origins:
    decision = "Allow" if is_origin_allowed(origin) else "Deny"
    print(f"{origin:40s} | {decision}")

    
লক্ষ্য করুন "https://abcltech.com.evil-attacker.com" দেখতে abcltech.com-এর মতো মনে হলেও এটি আসলে সম্পূর্ণ ভিন্ন একটি origin (evil-attacker.com-এর একটি সাবডোমেইন) — কারণ is_origin_allowed() সরাসরি সেট-মেম্বারশিপ (exact string match) চেক করে, স্ট্রিং-এর মধ্যে "abcltech.com" আছে কি না তা নয়। কখনো "abcltech.com" in origin_header-এর মতো আংশিক-মিল (substring) চেক দিয়ে CORS বাস্তবায়ন করা উচিত নয় — এটিই একটি বাস্তব, সাধারণ নিরাপত্তা ভুল।
মূল কথা · Key takeaway

XSS, CORS, ও CSRF তিনটিই "কে কী পড়তে/চালাতে পারে" সংক্রান্ত সমস্যা, ঠিক যেমন L36-এর RBAC "কে কী করতে পারে" সংক্রান্ত। বাস্তব ফ্রেমওয়ার্কগুলো (Django, Rails, Express-এর cors মিডলওয়্যার) এই সুরক্ষাগুলো বিল্ট-ইন হিসেবে দেয়, যাতে প্রতিটি প্রজেক্টে নতুন করে হাতে-কলমে লিখতে না হয় — L01-এ শেখা "সমাধান হওয়া সমস্যা পুনরায় সমাধান না করা" নীতিরই একটি প্রয়োগ।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ যদি একজন ডেভেলপার ভুলে html_escape() কল করতে ভুলে যান, তাহলে কী ঘটতে পারে?

যদি ইউজারের র (raw) ইনপুট সরাসরি পেজে বসানো হয়, এবং সেই ইনপুটে <script>...</script> -এর মতো কিছু থাকে, ব্রাউজার সেটিকে সত্যিকারের চালযোগ্য JavaScript হিসেবে মেনে নেবে — এটিই Cross-Site Scripting (XSS)। এই স্ক্রিপ্ট তখন ভুক্তভোগী ইউজারের সেশন কুকি চুরি করতে, ফর্ম ডেটা পাঠিয়ে দিতে, বা তার পক্ষে অ্যাকশন চালাতে পারে — এই কারণেই আউটপুটে বসানোর আগে ইউজার-ইনপুট সবসময় এস্কেপ করা জরুরি।

প্র ০২ CORS আসলে সার্ভারকে সুরক্ষা দেয়, নাকি ব্রাউজার-ব্যবহারকারীকে? পার্থক্যটা কী?

CORS মূলত ব্রাউজারকে সুরক্ষা দেয় — এটি একটি ব্রাউজার-প্রয়োগিত নীতি যা নির্ধারণ করে কোন origin-এর JavaScript আরেকটি origin-এর রেসপন্স পড়তে পারবে। এটি সার্ভারকে সরাসরি "আক্রমণ" থেকে রক্ষা করে না (একজন আক্রমণকারী `curl` দিয়ে সরাসরি API কল করলে CORS কোনো বাধা দেয় না, কারণ CORS ব্রাউজার-নির্দিষ্ট নিয়ম) — বরং এটি রক্ষা করে সেই ইউজারকে, যার ব্রাউজারে ভিন্ন একটি ক্ষতিকর সাইট খোলা থাকতে পারে এবং যেটি ইউজারের অজান্তে তার লগ-ইন-করা সেশন ব্যবহার করে আপনার API-এর ডেটা পড়ার চেষ্টা করতে পারে।

প্র ০৩ উপরের কোডে is_origin_allowed("http://localhost:3000") কেন সঠিকভাবে Allow রিটার্ন করে, অথচ এটি একটি লোকাল ডেভেলপমেন্ট ঠিকানা?

কারণ "http://localhost:3000" স্ট্রিংটি হুবহু allowed_origins সেটে যোগ করা আছে — এটি একটি বাস্তব প্র্যাকটিসও প্রতিফলিত করে, যেখানে ডেভেলপমেন্ট পরিবেশের জন্য আলাদা করে localhost-ভিত্তিক origin allow-list-এ রাখা হয় (এবং প্রোডাকশন ডিপ্লয়মেন্টে সেটি সাধারণত বাদ দেওয়া হয়, L51-এর এনভায়রনমেন্ট-নির্দিষ্ট কনফিগারেশনের ধারণার সাথে সামঞ্জস্যপূর্ণ)।

অনুশীলন

  1. চিন্তা করুন: L34-এর JWT ও L37-এর CSRF-এর মধ্যে একটি সম্পর্ক আছে — কেন টোকেন-ভিত্তিক (JWT, সাধারণত একটি Authorization হেডারে পাঠানো) API-গুলো সাধারণত কুকি-ভিত্তিক সেশনের চেয়ে CSRF-এর প্রতি কম ঝুঁকিপূর্ণ বলে বিবেচিত হয়?

    CSRF আক্রমণ কাজ করে কারণ ব্রাউজার স্বয়ংক্রিয়ভাবে কুকি সংযুক্ত করে পাঠায়, এমনকি একটি ক্ষতিকর সাইট থেকে শুরু-হওয়া রিকোয়েস্টেও। কিন্তু JWT সাধারণত JavaScript কোড স্পষ্টভাবে Authorization: Bearer <token> হেডারে যোগ করে — ব্রাউজার এটি স্বয়ংক্রিয়ভাবে সংযুক্ত করে না, তাই একটি ক্ষতিকর সাইট সহজে ব্যবহারকারীর টোকেন "চুরি করে" তার পক্ষে অনুরোধ করাতে পারে না, যদি না সেই ক্ষতিকর সাইট আসলেই টোকেনটির মান জানে।

  2. পরীক্ষা করুন: উপরের CORS কোড সেলে allowed_origins-এ "https://staging.abcltech.com" যোগ করুন এবং test_origins-এ সেই একই স্ট্রিং যোগ করে Run চেপে দেখুন সিদ্ধান্তটি Deny থেকে Allow-এ বদলায় কি না।

    allowed_origins-এ নতুন এন্ট্রি যোগ করার আগে is_origin_allowed("https://staging.abcltech.com") False (Deny) রিটার্ন করত, কারণ সেই origin সেটে ছিল না। যোগ করার পর সেটে "https://staging.abcltech.com" থাকায় সেট-মেম্বারশিপ চেক সত্য হয়ে যায়, তাই ফাংশন এখন True (Allow) রিটার্ন করবে — এটাই দেখায় allow-list একটি জীবন্ত কনফিগারেশন, প্রতিটি নতুন বৈধ ফ্রন্ট-এন্ড ডিপ্লয়মেন্টের জন্য স্পষ্টভাবে আপডেট করতে হয়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

  • কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
  • Cybersecurity কোর্স সহোদর কোর্স CSRF টোকেন বাস্তবায়ন, কনটেন্ট সিকিউরিটি পলিসি (CSP), ও XSS-এর গভীর প্রকারভেদ সেই কোর্সে বিস্তারিত আলোচিত।
  • JavaScript Programming কোর্স সহোদর কোর্স ব্রাউজারের ফেচ/XHR ও origin-সম্পর্কিত আচরণের ভাষাগত ভিত্তি সেই কোর্সে তৈরি হয়েছে।
  • সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks — সব এক জায়গায়।
আগের পাঠ
রোল-বেসড অ্যাক্সেস কন্ট্রোল (RBAC)