পাঠ ১৮ · ৬০-এর মধ্যে · মডিউল ৫
Home / Courses / Cybersecurity & Ethical Hacking / ব্রোকেন অ্যাক্সেস কন্ট্রোল

A01: ব্রোকেন অ্যাক্সেস কন্ট্রোল

A01: Broken access control
৯ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • Broken Access Control কী, এবং এটি কেন এত সাধারণ ও এত ক্ষতিকর
  • IDOR-এর সঠিক সংজ্ঞা ও একটি বাস্তব-সদৃশ উদাহরণ দিয়ে এটি কীভাবে কাজ করে
  • মিসিং ফাংশন-লেভেল অ্যাক্সেস কন্ট্রোল ও পাথ ট্র্যাভার্সাল — সংক্ষেপে দুটি সম্পর্কিত সমস্যা
  • "deny-by-default" নীতি এবং কেন সার্ভার-সাইড অথরাইজেশন চেক প্রতিটি রিকোয়েস্টে বাধ্যতামূলক হওয়া উচিত

১ · Broken Access Control আসলে কী

অ্যাক্সেস কন্ট্রোলAccess Controlএকটি সিস্টেমের নিয়ম যা নির্ধারণ করে কোন অথেন্টিকেটেড ইউজার কোন রিসোর্স দেখতে বা কোন কাজ করতে পারবে। হলো এমন নিয়মকানুন যা ঠিক করে দেয় — একজন লগইন করা ইউজার ঠিক কী দেখতে বা করতে পারবে। যখন এই নিয়ম সঠিকভাবে প্রয়োগ হয় না (অর্থাৎ যাচাই বাদ পড়ে যায়, বা ভুলভাবে করা হয়), তখন সেটাই Broken Access Control — ২০২১ OWASP Top 10-এর সবচেয়ে সাধারণভাবে পাওয়া ক্যাটাগরি।

গুরুত্বপূর্ণ বিষয় — অথেন্টিকেশন (আপনি কে, সেটা যাচাই) এবং অথরাইজেশন (আপনি কী করতে পারবেন, সেটা যাচাই) দুটি সম্পূর্ণ আলাদা জিনিস। একজন ইউজার সফলভাবে লগইন করতে পারলেও (অথেন্টিকেশন ঠিক আছে), সার্ভার যদি প্রতিটি রিকোয়েস্টে অথরাইজেশন যাচাই না করে, তাহলেও Broken Access Control ঘটতে পারে।

২ · IDOR — Insecure Direct Object Reference

সবচেয়ে ক্লাসিক উদাহরণ হলো IDORIDORInsecure Direct Object Reference — একটি ইন্টারনাল ID (যেমন order_id) সরাসরি URL/API-তে expose করা হয়, এবং সার্ভার সেই ID-র মালিকানা যাচাই না করেই ডেটা ফেরত দেয়।। ধরুন আপনি লগইন করে আপনার নিজের অর্ডার দেখছেন এমন একটি URL-এ:

https://example.com/orders/123 — এটি আপনার নিজের অর্ডার। এখন যদি আপনি URL-এ শুধু নাম্বারটি বদলে 124 করেন এবং সার্ভার এই অর্ডারের মালিক কি অনুরোধকারী ইউজার-ই তা যাচাই না করে শুধু order_id = 124-এর রেকর্ড ফেরত দেয় — তাহলে আপনি অন্য কারো অর্ডার দেখে ফেলেছেন। এটাই IDOR।

একই মূল সমস্যার আরও দুটি সাধারণ রূপ সংক্ষেপে —

মিসিং ফাংশন-লেভেল অ্যাক্সেস কন্ট্রোল
একজন সাধারণ ইউজার সরাসরি একটি admin-only API এন্ডপয়েন্ট কল করে ফেলে, কারণ সার্ভার শুধু UI-তে বাটন লুকিয়েছিল কিন্তু endpoint-এ role-ভিত্তিক চেক রাখেনি।
পাথ ট্র্যাভার্সাল
ফাইল-প্যাথ ইনপুটে ../../-এর মতো সিকোয়েন্স ব্যবহার করে অনুমোদিত ফোল্ডারের বাইরে গিয়ে সার্ভারের অন্য ফাইল পড়ার চেষ্টা — একই "সার্ভার-সাইড বাউন্ডারি চেক না থাকা" সমস্যার আরেকটি রূপ।
ক্লায়েন্ট-সাইড কোড (JavaScript দিয়ে একটি বাটন লুকানো, বা একটি মেনু আইটেম না দেখানো) কখনোই একটি নিরাপত্তা নিয়ন্ত্রণ নয় — একজন আক্রমণকারী ব্রাউজারের ডেভেলপার টুল বা সরাসরি API কল দিয়ে UI সম্পূর্ণ বাইপাস করতে পারে। অথরাইজেশন সবসময় সার্ভারে যাচাই হতে হবে।

৩ · প্রতিরক্ষা — সার্ভার-সাইড চেক ও deny-by-default

মূল ফিক্স সবসময় একই নীতি অনুসরণ করে: প্রতিটি রিকোয়েস্টে সার্ভার নিজে যাচাই করবে — এই নির্দিষ্ট রিসোর্সটি কি এই নির্দিষ্ট অনুরোধকারী ইউজারের প্রাপ্য? — এবং ডিফল্ট আচরণ হবে deny (প্রত্যাখ্যান), শুধুমাত্র স্পষ্টভাবে অনুমোদিত হলেই access দেওয়া হবে। নিচের কোড সেলে একই orders ডেটার উপর একটি insecure ও একটি secure ভার্সন পাশাপাশি দেখা যাক।

Python
# ভুয়া, ইন-মেমরি "orders" ডেটাবেস — প্রতিটি অর্ডারের একজন owner আছে
orders = {
    101: {"item": "Laptop Bag", "owner": "rina"},
    102: {"item": "Mechanical Keyboard", "owner": "hasan"},
    103: {"item": "Noise Cancelling Headphones", "owner": "rina"},
}

def get_order_insecure(order_id):
    # ইনসিকিউর: মালিকানা যাচাই ছাড়াই যেকোনো অর্ডার ফেরত দেয় (classic IDOR)
    return orders.get(order_id)

def get_order_safe(order_id, requesting_user):
    # সিকিউর: সার্ভার-সাইডে যাচাই করে — এই অর্ডারের মালিক কি অনুরোধকারী ইউজার-ই?
    order = orders.get(order_id)
    if order is None:
        return None
    if order["owner"] != requesting_user:
        return None  # deny-by-default — ভুল মালিক হলে কিছুই ফেরত দেওয়া হয় না
    return order

# "rina" লগইন করা, কিন্তু order_id=102 আসলে "hasan"-এর
print("=== ইনসিকিউর ভার্সন (IDOR) ===")
result = get_order_insecure(102)
print("rina-কে ফেরত দেওয়া হলো:", result, "  <- hasan-এর অর্ডার ফাঁস হয়ে গেল!")

print()
print("=== সিকিউর ভার্সন (owner চেকসহ) ===")
result_safe = get_order_safe(102, requesting_user="rina")
print("rina-কে ফেরত দেওয়া হলো:", result_safe, "  <- সঠিকভাবে প্রত্যাখ্যাত")

print()
print("rina নিজের অর্ডার (103) ঠিকই দেখতে পারছে:")
print(get_order_safe(103, requesting_user="rina"))

    
মূল কথা · Key takeaway

get_order_insecure ও get_order_safe-এর কোড প্রায় একই রকম দেখতে — পার্থক্য শুধু একটি লাইন: owner চেক। ঠিক এভাবেই বাস্তব-জগতের অসংখ্য IDOR বাগ ঘটে — একটি ছোট্ট মিসিং চেক, বড় ডেটা লিক। প্রতিটি রিসোর্স-অ্যাক্সেস ফাংশনে "এই ইউজার কি সত্যিই এই রিসোর্সের অধিকারী?" প্রশ্নটি স্পষ্টভাবে জিজ্ঞেস করাই এই পুরো ক্যাটাগরির সবচেয়ে গুরুত্বপূর্ণ অভ্যাস।

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

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

প্র ০১ অথেন্টিকেশন ও অথরাইজেশন — এই দুটি ধারণা আলাদা কীভাবে, এবং কেন একটি ঠিক থাকলেই অন্যটি স্বয়ংক্রিয়ভাবে নিরাপদ থাকে না?

অথেন্টিকেশন যাচাই করে "আপনি কে" (যেমন সঠিক পাসওয়ার্ড দিয়ে লগইন), আর অথরাইজেশন যাচাই করে "আপনি কী করতে পারবেন" (যেমন এই নির্দিষ্ট অর্ডারটি দেখার অধিকার)। একজন ইউজার সঠিকভাবে লগইন করতে পারলেও, সার্ভার যদি প্রতিটি রিসোর্স-অনুরোধে আলাদাভাবে অথরাইজেশন যাচাই না করে (যেমন উপরের get_order_insecure), তাহলে "সঠিকভাবে লগইন করা" একজন ইউজারও অন্যের ডেটা দেখে ফেলতে পারে। তাই দুটি চেক-ই আলাদাভাবে প্রয়োজন।

প্র ০২ কেন "deny-by-default" নীতি "allow-by-default" থেকে নিরাপদ — একটি নতুন ফিচার যোগ করার সময় এর প্রভাব কী?

deny-by-default মানে ডেভেলপার ভুলে একটি চেক বাদ দিলেও ডিফল্ট ফলাফল হলো প্রত্যাখ্যান — অর্থাৎ একটি ভুল সাধারণত ডেটা ফাঁস না করে বরং একটি বৈধ রিকোয়েস্ট ভুলভাবে ব্লক করে দেয় (যা বিরক্তিকর কিন্তু নিরাপদ)। allow-by-default হলে উল্টোটা ঘটে — একটি ভুলে যাওয়া চেক সরাসরি ডেটা লিক করে দেয়। নতুন ফিচার যোগ করার সময় তাই সবসময় "স্পষ্টভাবে অনুমতি না দেওয়া পর্যন্ত ব্লকড" ভিত্তি থেকে শুরু করা উচিত।

প্র ০৩ পাথ ট্র্যাভার্সাল ও IDOR-এর মূল সমস্যা আসলে একই কেন — কী মিল আছে দুটির মধ্যে?

দুটোতেই একটি "রেফারেন্স" (order_id বা ফাইল-প্যাথ) সরাসরি ইউজার-নিয়ন্ত্রিত ইনপুট থেকে আসে, এবং সার্ভার সেই রেফারেন্স ব্যবহার করার আগে যাচাই করে না যে এটি অনুরোধকারীর জন্য বৈধ কিনা। IDOR-এ সমস্যা একটি ডেটাবেস রেকর্ডের মালিকানা যাচাই না করা, পাথ ট্র্যাভার্সালে সমস্যা একটি ফাইল-প্যাথ অনুমোদিত ফোল্ডারের মধ্যে আছে কিনা তা যাচাই না করা — উভয়ই একই "বাউন্ডারি চেক অনুপস্থিত" প্যাটার্নের ভিন্ন প্রকাশ।

অনুশীলন

  1. চিন্তা করুন: আপনি নিয়মিত ব্যবহার করেন এমন একটি ওয়েবসাইট মনে করুন যার URL-এ একটি সংখ্যা বা ID থাকে (যেমন একটি প্রোফাইল বা ইনভয়েস পেজ)। যদি সেই সাইটে IDOR থাকত, ঠিক কী তথ্য ফাঁস হতে পারত?

    উদাহরণ হিসেবে, একটি ইনভয়েস URL-এ IDOR থাকলে অন্য গ্রাহকের কেনাকাটার ইতিহাস, ঠিকানা, বা মূল্য দেখা যেতে পারত — যা Confidentiality (L01-এর CIA Triad) সরাসরি লঙ্ঘন করে। বাস্তব জগতে ব্যাংকিং ও ই-কমার্স সাইটে এই ধরনের বাগ বহুবার রিপোর্ট হয়েছে।

  2. পরীক্ষা করুন: উপরের কোড সেলে get_order_safe(101, requesting_user="hasan") চালিয়ে দেখুন ফলাফল কী আসে, এবং কেন।

    ফলাফল None আসবে — কারণ order_id 101-এর owner হলো "rina", কিন্তু অনুরোধকারী "hasan"। order["owner"] != requesting_user শর্তটি True হয়ে যায়, তাই ফাংশনটি deny-by-default নীতি অনুযায়ী কিছুই ফেরত না দিয়ে None দেয় — ঠিক যেভাবে একটি সঠিকভাবে-লেখা প্রোডাকশন API আচরণ করা উচিত।

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

আগের পাঠ
L17 · OWASP Top 10 পরিচিতি ও ওয়েব আর্কিটেকচার রিভিউ