সেশন-ভিত্তিক অথেন্টিকেশন
এই পাঠে যা শিখবেন
- সেশন-ভিত্তিক অথেন্টিকেশনের ধারণা — লগইন থেকে সেশন তৈরি, কুকির ভূমিকা, ও রিকোয়েস্ট যাচাই
- একটি সত্যিকারের ইন-মেমরি সেশন স্টোর,
secrets.token_hex()দিয়ে র্যান্ডম সেশন আইডি তৈরি login(),get_current_user(), ওlogout()ফাংশন লিখে সম্পূর্ণ জীবনচক্র বাস্তবায়ন- বৈধ ও অবৈধ সেশন আইডির আচরণ যাচাই — সেশন-ভিত্তিক অথেন্টিকেশন কেন "স্টেটফুল"
১ · সেশন-ভিত্তিক অথেন্টিকেশন কীভাবে কাজ করে
সেশনSessionএকটি নির্দিষ্ট ব্যবহারকারীর লগইন-করা অবস্থার তথ্য, যা সার্ভারের মেমরিতে বা ডেটাবেসে সংরক্ষিত থাকে এবং একটি আইডি দিয়ে শনাক্ত করা হয়। ভিত্তিক অথেন্টিকেশনে HTTP নিজে স্টেটলেস হলেও (প্রতিটি রিকোয়েস্ট স্বাধীন, আগের রিকোয়েস্ট মনে রাখে না — বিস্তারিত L07-এ), সার্ভার নিজে একটি অতিরিক্ত স্তর যোগ করে ব্যবহারকারীকে "মনে রাখে"। প্রবাহটি চারটি ধাপে ঘটে —
- ব্যবহারকারী ইউজারনেম/পাসওয়ার্ড দিয়ে লগইন করে।
- সার্ভার পরিচয় যাচাই করে একটি র্যান্ডম, অনুমান-অসম্ভব সেশন আইডি তৈরি করে এবং
session_store-এ সেই আইডির বিপরীতে ব্যবহারকারীর তথ্য রেখে দেয়। - সার্ভার সেই আইডি ব্রাউজারকে একটি কুকি হিসেবে পাঠায় (
Set-Cookieহেডার) — এই কোর্সে যেহেতু সত্যিকারের ব্রাউজার/কুকি নেই, সেশন আইডিকে শুধু একটি রিটার্ন-হওয়া স্ট্রিং হিসেবে সিমুলেট করা হচ্ছে। - পরবর্তী প্রতিটি রিকোয়েস্টে ব্রাউজার স্বয়ংক্রিয়ভাবে সেই কুকি পাঠায়; সার্ভার আইডি দিয়ে
session_storeথেকে ব্যবহারকারী খুঁজে বের করে।
L07-এ শেখা REST-এর স্টেটলেসনেস নীতির সাথে এটি একটি গুরুত্বপূর্ণ ব্যতিক্রম — সেশন-ভিত্তিক অথেন্টিকেশনে সার্ভারকে প্রতিটি সক্রিয় ব্যবহারকারীর জন্য স্টেট (সেশন) মনে রাখতে হয়। একাধিক সার্ভার ইনস্ট্যান্স থাকলে (লোড-ব্যালান্স করা), সবগুলোকে একই সেশন স্টোর শেয়ার করতে হয়, নাহলে ভুল ইনস্ট্যান্সে রিকোয়েস্ট গেলে ব্যবহারকারী "লগআউট" হয়ে যায় বলে মনে হবে। এই সীমাবদ্ধতাই L34-এ স্টেটলেস JWT টোকেনের প্রয়োজনীয়তা তৈরি করে।
২ · একটি সত্যিকারের ইন-মেমরি সেশন সিস্টেম
নিচের কোড সেলে secrets.token_hex() দিয়ে একটি সত্যিকারের র্যান্ডম সেশন আইডি তৈরি করে, একটি
ডিকশনারিকে সেশন স্টোর হিসেবে ব্যবহার করে login(), get_current_user(), ও
logout() ফাংশন লেখা হয়েছে — এবং একটি বৈধ সেশন আইডি ও একটি বানানো সেশন আইডি দুটোই পরীক্ষা করা
হয়েছে।
import secrets
# ব্যবহারকারী "ডেটাবেস" -- এই লেসনের জন্য সরল ডিকশনারি
users_db = {
"sohel": {"user_id": 1, "name": "Sohel Rana"},
"priya": {"user_id": 2, "name": "Priya Das"},
}
# সেশন স্টোর -- সার্ভারের মেমরিতে রাখা হয় (session_id -> user info)
session_store = {}
def login(username):
"""সঠিক ইউজারনেম দিলে একটি নতুন সেশন তৈরি করে সেশন আইডি রিটার্ন করে
(বাস্তবে এই আইডিটি ব্রাউজারে 'Set-Cookie' হেডার দিয়ে পাঠানো হতো)"""
if username not in users_db:
return None
session_id = secrets.token_hex(16) # ৩২-হেক্স-ক্যারেক্টার, অনুমান-অসম্ভব
session_store[session_id] = users_db[username]
return session_id
def get_current_user(session_id):
"""সেশন আইডি দিয়ে সংশ্লিষ্ট ইউজার খুঁজে বের করে -- অবৈধ/অস্তিত্বহীন
আইডি হলে None রিটার্ন করে"""
return session_store.get(session_id)
def logout(session_id):
"""সেশন স্টোর থেকে এন্ট্রি মুছে ফেলে -- এরপর সেই আইডি আর কাজ করবে না"""
return session_store.pop(session_id, None) is not None
# --- ধাপ ১: লগইন করে সেশন আইডি পাওয়া ---
valid_session = login("sohel")
print("লগইন সফল, সেশন আইডি:", valid_session)
# --- ধাপ ২: বৈধ সেশন আইডি দিয়ে ইউজার শনাক্ত করা ---
user = get_current_user(valid_session)
print("বৈধ সেশন আইডি দিয়ে পাওয়া ইউজার:", user)
# --- ধাপ ৩: একটি বানানো, কখনো ইস্যু না-করা সেশন আইডি দিয়ে চেষ্টা ---
fake_session = secrets.token_hex(16)
fake_user = get_current_user(fake_session)
print("ভুয়া সেশন আইডি দিয়ে পাওয়া ইউজার:", fake_user)
# --- ধাপ ৪: লগআউট করার পর একই আইডি আর কাজ করে না ---
logged_out = logout(valid_session)
print("\nলগআউট সফল হয়েছে:", logged_out)
print("লগআউটের পর একই সেশন আইডি দিয়ে ইউজার:", get_current_user(valid_session))
session_id-টি প্রতিবার Run করলে ভিন্ন হবে — কারণ secrets.token_hex()
সত্যিকারের ক্রিপ্টোগ্রাফিকভাবে-নিরাপদ র্যান্ডম নম্বর জেনারেটর ব্যবহার করে (Python-এর সাধারণ
random মডিউলের বদলে) — যাতে কেউ একটি বৈধ সেশন আইডি অনুমান করে অন্য কারো অ্যাকাউন্টে ঢুকতে না
পারে। fake_session-এর জন্য get_current_user() সবসময় None রিটার্ন
করবে, কারণ সেটি কখনো session_store-এ যোগই হয়নি।
সেশন-ভিত্তিক অথেন্টিকেশনে সার্ভার প্রতিটি লগ-ইন-করা ব্যবহারকারীর জন্য একটি সেশন মনে রাখে, এবং ক্লায়েন্টকে শুধু একটি র্যান্ডম আইডি দেয়। এটি সহজ ও পরীক্ষিত, কিন্তু সার্ভার-সাইড স্টেট প্রয়োজন বলে স্কেল করা জটিল হতে পারে — এই সীমাবদ্ধতা কাটানোর জন্যই L34-এ স্টেটলেস টোকেন-ভিত্তিক (JWT) অথেন্টিকেশন শেখানো হবে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ সেশন আইডি কেন সহজ, অনুমানযোগ্য কিছু (যেমন ক্রমিক সংখ্যা ১, ২, ৩...) না হয়ে র্যান্ডম হওয়া জরুরি?
সেশন আইডিই একমাত্র জিনিস যা প্রমাণ করে অনুরোধকারী আসলে সেই লগইন-করা ব্যবহারকারী। যদি আইডি অনুমানযোগ্য হয়
(যেমন ক্রমিক সংখ্যা), তাহলে একজন আক্রমণকারী শুধু বিভিন্ন সংখ্যা চেষ্টা করে অন্য কারো সেশন আইডি বের করে
ফেলতে পারবে এবং তাদের অ্যাকাউন্টে প্রবেশ করতে পারবে — এটিকে "সেশন হাইজ্যাকিং" বলা হয়। তাই
secrets.token_hex()-এর মতো ক্রিপ্টোগ্রাফিকভাবে-নিরাপদ র্যান্ডম জেনারেটর ব্যবহার করা হয়,
যাতে অনুমান করা ব্যবহারিকভাবে অসম্ভব হয়।
প্র ০২ একাধিক সার্ভার ইনস্ট্যান্স (লোড-ব্যালান্সিং-এর পেছনে) থাকলে সেশন-ভিত্তিক অথেন্টিকেশনে কী সমস্যা হতে পারে?
উপরের কোডে session_store একটি নির্দিষ্ট সার্ভার প্রসেসের মেমরিতে থাকে। যদি লগইন রিকোয়েস্ট
সার্ভার A-তে যায় (এবং সেশন A-এর মেমরিতে সংরক্ষিত হয়), কিন্তু পরের রিকোয়েস্ট লোড-ব্যালান্সার সার্ভার
B-তে পাঠায় — B-এর মেমরিতে সেই সেশন নেই, তাই ব্যবহারকারীকে ভুলভাবে "লগ-আউট" দেখানো হবে। সমাধান হলো সব
সার্ভার একটি শেয়ার্ড সেশন স্টোর (যেমন Redis) ব্যবহার করা, অথবা L34-এর স্টেটলেস JWT পদ্ধতিতে যাওয়া, যেখানে
কোনো সার্ভার-সাইড স্টোরেজ প্রয়োজনই হয় না।
প্র ০৩
উপরের কোডে logout() এর পর get_current_user(valid_session) কেন
None রিটার্ন করে?
কারণ logout() ফাংশনটি session_store.pop(session_id, None) দিয়ে সেই সেশন
আইডির এন্ট্রিটি ডিকশনারি থেকে সম্পূর্ণ মুছে ফেলে। এরপর get_current_user() যখন
session_store.get(session_id) কল করে, ডিকশনারিতে সেই কী (key) আর নেই বলে এটি
None রিটার্ন করে — ঠিক যেমন কখনো ইস্যু না-করা একটি ভুয়া আইডির ক্ষেত্রে হয়েছিল।
অনুশীলন
-
চিন্তা করুন: একটি ই-কমার্স সাইটে "আমাকে মনে রাখো" (Remember Me) অপশন চাপলে সেশন সাধারণত
অনেক দিন (যেমন ৩০ দিন) বাঁচে, কিন্তু না চাপলে ব্রাউজার বন্ধ করলেই সেশন শেষ হয়ে যায় — এই আচরণটি উপরের
session_storeডিজাইনে কীভাবে যোগ করা যেতে পারে?প্রতিটি সেশন এন্ট্রিতে ইউজার তথ্যের পাশাপাশি একটি
expires_atটাইমস্ট্যাম্প যোগ করা যেতে পারে (যেমনtime.time() + 30*24*3600"Remember Me" চাপলে, অথবা ছোট একটি মেয়াদ না চাপলে)।get_current_user()-কে তখন আইডি খোঁজার পাশাপাশিexpires_atবর্তমান সময়ের চেয়ে বড় কি না তাও যাচাই করতে হবে — মেয়াদ পার হয়ে গেলে এন্ট্রি থাকলেওNoneরিটার্ন করতে হবে (এবং চাইলে সেই মুহূর্তে স্টোর থেকে মুছেও ফেলা যায়)। -
পরীক্ষা করুন: উপরের কোড সেলে একটি নতুন ফাংশন
session_count()লিখুন যাlen(session_store)রিটার্ন করে, তারপরlogin("priya")কল করে ও Run চেপে দেখুন মোট সক্রিয় সেশন সংখ্যা কীভাবে বদলায়।def session_count(): return len(session_store)যোগ করার পর, শুরুতেlogin("sohel")একটি এন্ট্রি যোগ করে (গণনা ১), তারপরlogin("priya")আরেকটি এন্ট্রি যোগ করে (গণনা ২), আরlogout(valid_session)একটি এন্ট্রি সরিয়ে গণনা আবার কমায় — এভাবে সরাসরি দেখা যায় সেশন স্টোর সত্যিই একটি জীবন্ত, পরিবর্তনশীল ডিকশনারি, শুধু ধারণা নয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট।
- Cybersecurity কোর্স সহোদর কোর্স সেশন হাইজ্যাকিং, নিরাপদ কুকি ফ্ল্যাগ (HttpOnly, Secure, SameSite)-সহ গভীর নিরাপত্তা বিষয়গুলো সেই কোর্সে বিস্তারিত আলোচিত।
-
Python Programming কোর্স সহোদর কোর্স
secretsমডিউলসহ Python-এর স্ট্যান্ডার্ড লাইব্রেরির ভিত্তি সেই কোর্সে তৈরি হয়েছে। - সব 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 — সব এক জায়গায়।