Race Conditions: ডেটা প্রসেসিংয়ের টাইমিং দুর্বলতা কাজে লাগিয়ে ওয়েব অ্যাপ্লিকেশন হ্যাকিং!
Race Condition ভালনারেবিলিটির বিস্তারিত বিশ্লেষণ, রিয়েল-ওয়ার্ল্ড উদাহরণ এবং কার্যকর প্রতিরক্ষা কৌশল।
ধরা যাক, আপনি একটি ই-কমার্স ওয়েবসাইটে $১০০ ডিসকাউন্ট পেলেন একটি প্রোমো কোডের মাধ্যমে। সাধারণত প্রতি ইউজার শুধু একবার এটি ব্যবহার করতে পারেন। কিন্তু যদি আপনি একই সাথে ১০০টি রিকোয়েস্ট পাঠান এবং ব্যাকএন্ড একটি Race Condition-এ ভোগে, তাহলে সম্ভবত সবগুলো রিকোয়েস্ট সফল হবে — আপনি $১০,০০০ ডিসকাউন্ট পেয়ে যাবেন। ২০২৩ সালে Bug Bounty গবেষক James Kettle তার "Smashing the State Machine" গবেষণাপত্রে এমন আক্রমণের অসংখ্য উদাহরণ তুলে ধরেছেন।
Race Condition একটি ক্লাসিক্যাল কনকারেন্সি বাগ যা যখনই দুটি বা ততোধিক প্রক্রিয়া বা থ্রেড একই রিসোর্সে একসাথে অ্যাক্সেস করে এবং সঠিক সিনক্রোনাইজেশন থাকে না, তখনই ঘটে। ওয়েব অ্যাপ্লিকেশনে এই দুর্বলতা ফাইন্যান্সিয়াল ক্ষতি, অথেন্টিকেশন বাইপাস, এবং প্রিভিলেজ এস্কেলেশনের কারণ হতে পারে। এই প্রবন্ধে আমরা Race Condition-এর কারিগরি বিশ্লেষণ, বিভিন্ন প্রকার, রিয়েল-ওয়ার্ল্ড উদাহরণ, এবং প্রতিরক্ষা কৌশল বিস্তারিতভাবে আলোচনা করব।
মূল ধারণা
Race Condition বুঝতে হলে কনকারেন্সি এবং কনসিস্টেন্সির মৌলিক ধারণা জানা দরকার। একটি সাধারণ ব্যাংক অ্যাপ্লিকেশনে ফান্ড ট্রান্সফার ফাংশন ভাবুন:
def transfer(from_account, to_account, amount):
balance = get_balance(from_account)
if balance >= amount:
update_balance(from_account, balance - amount)
update_balance(to_account, get_balance(to_account) + amount)
return True
return False
এই কোডে একটি গুরুতর Race Condition আছে। দুটি একসাথে রিকোয়েস্ট নিয়ে চিন্তা করুন, যেখানে অ্যাকাউন্টে $১০০ আছে এবং উভয়ই $১০০ ট্রান্সফার করতে চায়:
Request A: balance = 100, check passes
Request B: balance = 100, check passes (read before A wrote!)
Request A: update_balance to 0
Request B: update_balance to 0
ফলাফল: $২০০ ট্রান্সফার হয়েছে, কিন্তু অ্যাকাউন্ট থেকে শুধু $১০০ কমেছে। এটি TOCTOU (Time-of-Check to Time-of-Use) ভালনারেবিলিটির ক্লাসিক উদাহরণ।
Race Condition-এর প্রকার
TOCTOU: একটি চেক এবং সংশ্লিষ্ট অ্যাকশনের মধ্যে অবস্থা পরিবর্তিত হয়। ফাইল সিস্টেমে এটি সাধারণ — access() চেক করে open() করার মধ্যে।
Limit Overrun: কাউন্টার বা লিমিট পরীক্ষা এবং বৃদ্ধির মধ্যে ব্যবধান। উদাহরণ — কুপন ব্যবহারের সংখ্যা।
State Confusion: একটি অবজেক্টের অবস্থা একটি প্রক্রিয়া পড়ে নিচ্ছে যখন অন্যটি লিখছে। অসংগতি দেখা দেয়।
Order of Operations: একাধিক অপারেশনের ক্রম যেভাবে ধরে নেওয়া হয়, বাস্তবে তা পরিবর্তন হয়।
বাস্তব উদাহরণ
কুপন রিডেম্পশন (Limit Overrun)
def redeem_coupon(user_id, coupon_code):
coupon = get_coupon(coupon_code)
if coupon.used_by(user_id):
return "Already used"
if coupon.usage_count >= coupon.max_uses:
return "Coupon exhausted"
coupon.usage_count += 1
coupon.mark_used_by(user_id)
apply_discount(user_id, coupon.discount)
একই কুপন কোডে আক্রমণকারী ৫০টি সমান্তরাল রিকোয়েস্ট পাঠালে সম্ভবত সবগুলোই সফল হবে, এবং তিনি ৫০ গুণ ডিসকাউন্ট পাবেন।
Bug Bounty রিপোর্ট: ক্রিপ্টোকারেন্সি এক্সচেঞ্জ
২০১৯ সালে একটি প্রধান ক্রিপ্টো এক্সচেঞ্জে গবেষক একটি Race Condition আবিষ্কার করেন। উইথড্রয়াল রিকোয়েস্টে balance check এবং debit অপারেশনের মধ্যে গ্যাপ ছিল। গবেষক ২০টি সমান্তরাল উইথড্রয়াল রিকোয়েস্ট পাঠিয়ে $২০,০০০ মূল্যের অতিরিক্ত ক্রিপ্টো পেয়েছিলেন। বাউন্টি ছিল $৫০,০০০।
Starbucks Gift Card
২০১৫ সালে গবেষক Egor Homakov দেখিয়েছিলেন Starbucks-এর গিফট কার্ড সিস্টেমে Race Condition দিয়ে একই ব্যালেন্স একাধিকবার ব্যবহার করা যেত। তিনি $২৫ গিফট কার্ড দিয়ে একই সাথে দুটি কার্ডে $২৫ ট্রান্সফার করতে সক্ষম হয়েছিলেন — মূল ব্যালেন্স কমার আগেই।
James Kettle's Single-Packet Attack
২০২৩ সালে Kettle ব্যাখ্যা করেছিলেন কীভাবে HTTP/2-এর সাহায্যে একটি TCP প্যাকেটে একাধিক রিকোয়েস্ট পাঠিয়ে কয়েক মাইক্রোসেকেন্ডের মধ্যে সার্ভারে পৌঁছানো যায়। এটি পূর্বের যুগের সেকেন্ড-লেভেল Race Window-কে মাইক্রোসেকেন্ড-লেভেলে নিয়ে গেছে।
import h2.connection
import h2.config
config = h2.config.H2Configuration(client_side=True)
conn = h2.connection.H2Connection(config=config)
conn.initiate_connection()
# Prepare 50 identical requests
for i in range(50):
headers = [(':method', 'POST'),
(':path', '/redeem'),
(':authority', 'target.com')]
conn.send_headers(i*2+1, headers, end_stream=False)
# Send all in single TCP packet
conn.data_to_send()
File System Race (Local Privilege Escalation)
ক্লাসিক্যাল উদাহরণ — Linux-এ একটি setuid বাইনারি যা ফাইল চেক করে তারপর খোলে:
if (access("/tmp/user_file", W_OK) == 0) {
// Window for symlink attack
fd = open("/tmp/user_file", O_WRONLY);
write(fd, secret_data, len);
}
আক্রমণকারী access() কল এবং open() কলের মধ্যবর্তী মাইক্রোসেকেন্ডে /tmp/user_file-কে /etc/passwd-এর symlink-এ পরিবর্তন করতে পারে।
এক্সপ্লয়টেশন কৌশল
একসাথে রিকোয়েস্ট পাঠানো
সবচেয়ে সাধারণ পদ্ধতি — multi-threaded HTTP client দিয়ে একই রিকোয়েস্ট দ্রুত পুনরাবৃত্তি।
import threading
import requests
def send_request():
requests.post("https://target.com/api/redeem",
json={"code": "PROMO50"},
cookies={"session": "abc123"})
threads = [threading.Thread(target=send_request) for _ in range(50)]
[t.start() for t in threads]
[t.join() for t in threads]
Burp Suite Turbo Intruder
Turbo Intruder Race Condition টেস্টিংয়ের জন্য বিশেষভাবে ডিজাইন করা। requestsPerConnection এবং pipeline কনফিগারেশন দিয়ে অত্যন্ত দ্রুত রিকোয়েস্ট পাঠানো যায়।
Single-Packet Attack
Burp Suite-এ ইন্টিগ্রেটেড এই কৌশল HTTP/2-এর সাহায্যে একটি TCP প্যাকেটে একাধিক রিকোয়েস্ট পাঠায়। এটি নেটওয়ার্ক জিটারের প্রভাব এড়ায় এবং সবচেয়ে সংকীর্ণ Race Window-ও কাজে লাগাতে পারে।
ডিটেকশন কৌশল
কোড রিভিউ প্যাটার্ন
কোড রিভিউতে যেসব প্যাটার্ন খেয়াল রাখতে হবে:
- Check-then-Act প্যাটার্ন
- Read-Modify-Write যেখানে অ্যাটোমিক অপারেশন ব্যবহৃত হয়নি
- ডাটাবেস ট্রানজ্যাকশন ছাড়া কাউন্টার অপারেশন
- ক্যাশ থেকে পড়ে সরাসরি লেখা
- ফাইল সিস্টেম অপারেশনে চেক এবং অ্যাক্সেসের মধ্যে গ্যাপ
লগ অ্যানালিসিস
লগে অস্বাভাবিক প্যাটার্ন: একই সময়ে একই ইউজার থেকে অনেক অভিন্ন রিকোয়েস্ট, ইনকনসিসটেন্ট ডাটাবেস অবস্থা, এবং প্রত্যাশিত অপেক্ষা একই অপারেশন সম্পন্ন হওয়া।
স্ট্রেস টেস্টিং
লোড টেস্টিং টুল (JMeter, Locust) দিয়ে ক্রিটিক্যাল এন্ডপয়েন্টে উচ্চ কনকারেন্সি প্রয়োগ করে অসঙ্গতি খোঁজা।
প্রতিরোধ ও প্রতিকার
Race Condition প্রতিরোধে কয়েকটি প্রমাণিত কৌশল রয়েছে।
প্রথমত, ডাটাবেস ট্রানজ্যাকশন। ACID গ্যারান্টি সহ ট্রানজ্যাকশন ব্যবহার করুন। SELECT FOR UPDATE বা সমতুল্য লকিং ব্যবহার করুন।
BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE id = ? FOR UPDATE;
-- check and update
UPDATE accounts SET balance = balance - ? WHERE id = ?;
COMMIT;
দ্বিতীয়ত, অপ্টিমিস্টিক লকিং। প্রতিটি রেকর্ডে version কলাম রাখুন। আপডেটের সময় version যাচাই করুন।
UPDATE accounts
SET balance = ?, version = version + 1
WHERE id = ? AND version = ?;
যদি affected rows = 0, অন্য কেউ ইতিমধ্যে আপডেট করেছে, retry প্রয়োজন।
তৃতীয়ত, অ্যাটমিক অপারেশন। Redis-এ INCR, DECR, এবং Lua স্ক্রিপ্ট ব্যবহার করুন। PostgreSQL-এ UPDATE ... RETURNING বা অ্যাটোমিক কাউন্টার।
# Redis atomic decrement
remaining = redis.decr(f"coupon:{code}:uses")
if remaining < 0:
redis.incr(f"coupon:{code}:uses")
return "Exhausted"
চতুর্থত, আইডেমপোটেন্সি কী। প্রতিটি রিকোয়েস্টে unique idempotency key পাঠাতে ক্লায়েন্টকে বাধ্য করুন। সার্ভার একই key-এর জন্য একই উত্তর দেবে।
@app.post("/api/transfer")
def transfer(idempotency_key: str, ...):
cached = cache.get(idempotency_key)
if cached: return cached
result = perform_transfer(...)
cache.set(idempotency_key, result, ttl=3600)
return result
পঞ্চমত, রেট লিমিটিং। ব্যবহারকারী এবং IP প্রতি অনুমোদিত রিকোয়েস্ট সীমিত করুন। বিশেষত সংবেদনশীল অপারেশনে কঠোর লিমিট।
ষষ্ঠত, মিউটেক্স ও লক। অ্যাপ্লিকেশন লেভেলে distributed lock (Redis Redlock, Zookeeper) ব্যবহার করুন। তবে সাবধান — distributed locking জটিল এবং নিজেও Race Condition তৈরি করতে পারে।
সপ্তমত, অ্যাসিনক্রোনাস প্রসেসিং। সংবেদনশীল অপারেশনকে queue-এ পাঠান এবং serial workers দিয়ে প্রসেস করুন।
অষ্টমত, নিয়মিত পেনিট্রেশন টেস্টিং। Race Condition টেস্টিং পেনিট্রেশন টেস্টের একটি অপরিহার্য অংশ। Burp Suite Turbo Intruder, Single-Packet Attack টুলিং ব্যবহার করুন।
নবমত, CI/CD-এ অটোমেটেড টেস্ট। কনকারেন্সি টেস্ট লিখুন। গুরুত্বপূর্ণ এন্ডপয়েন্টে সমান্তরাল রিকোয়েস্ট পাঠিয়ে নিশ্চিত করুন ফলাফল সঠিক।
দশমত, মনিটরিং এবং অ্যালার্টিং। অস্বাভাবিক প্যাটার্ন (যেমন একই ইউজার থেকে দ্রুত পরপর একই অপারেশন) অ্যালার্টে রূপান্তর করুন।
Race Condition একটি অপ্রিয়, প্রায়ই উপেক্ষিত নিরাপত্তা ত্রুটি যা ক্লাসিক্যাল ভালনারেবিলিটি স্ক্যানারে সহজে ধরা পড়ে না। কিন্তু আধুনিক বাগ বাউন্টি কমিউনিটি — বিশেষত James Kettle-এর গবেষণার পর — এই ক্যাটাগরির ভয়াবহতা প্রকাশ করেছে। ফাইন্যান্সিয়াল প্রতিষ্ঠান, ই-কমার্স, এবং ক্রিপ্টোকারেন্সি প্ল্যাটফর্মের জন্য এটি বিশেষভাবে বিপজ্জনক। ডেভেলপারদের কনকারেন্সি প্রাইমিটিভ গভীরভাবে বোঝা, ডাটাবেস ট্রানজ্যাকশন সঠিকভাবে ব্যবহার করা, এবং নিয়মিত স্ট্রেস টেস্টিং করা অপরিহার্য। সাইবার সিকিউরিটি গবেষকদের জন্য Race Condition একটি ফলপ্রসূ ক্ষেত্র, যেখানে সৃজনশীল চিন্তাধারা এবং সঠিক টুলিং একসাথে চমকপ্রদ আবিষ্কার এনে দিতে পারে।
আপনার জ্ঞান যাচাই করতে প্রস্তুত? আজই HackCert-এ Race Conditions MCQ Quiz-টি দিন!

