Cloudflare-এর বট-চ্যালেঞ্জ সিস্টেম সাধারণ HTML ফর্ম সাবমিশনগুলোকে নিঃশব্দে বন্ধ করে দিতে পারে, যা একটি সাধারণ পেমেন্ট ক্লিককে প্রকৃত ব্যবহারকারীদের জন্য একটি ডেড-এন্ডে পরিণত করে। রিকোয়েস্টটিকে একটি নেটিভ নেভিগেশন POST থেকে fetch-first ফ্লো-তে পরিবর্তন করলে নিরাপত্তা বিঘ্নিত না করেই অভিজ্ঞতা পুনরুদ্ধার করা সম্ভব।
কেন এই সমস্যাটি গুরুত্বপূর্ণ
একজন ডেভেলপার একটি পেমেন্ট ফর্ম রিলিজ করেছিলেন যা curl, প্রতিটি টেস্ট স্যুট এবং লোকাল সার্ভারে সঠিকভাবে কাজ করছিল। কিন্তু যখন একজন গ্রাহক Chrome ব্যবহার করে সেই একই ফর্মটি ব্যবহার করেন, তখন প্রথম ক্লিকের পর একটি সিকিউরিটি এরর এবং দ্বিতীয় ক্লিকের পর একটি “timeout-or-duplicate” মেসেজ দেখায়। এই ব্যর্থতার কারণে তিনটি হট-ফিক্স রিলিজ এবং পুরো একদিন ডিবাগিং করতে হয়েছে।
লুকানো সমস্যাটি
ফর্মটি একটি ওপেন-সোর্স Astro প্যাকেজে রয়েছে যা একটি সাধারণ HTML <form> এলিমেন্টের ওপর নির্ভর করে। যখন ব্যবহারকারী Pay বাটনে ক্লিক করেন, সার্ভার Stripe-এ একটি 303 রিডাইরেক্টের মাধ্যমে উত্তর দেয় এবং ব্রাউজার কোনো JavaScript ছাড়াই সেই রিডাইরেক্ট অনুসরণ করে। স্ক্রিপ্ট ডিজেবল করা থাকলে বিকল্প ব্যবস্থা হিসেবে সাইটগুলো এই প্যাটার্নটি ব্যবহার করে।
Cloudflare সাইটের সামনে অবস্থান করে এবং একটি বট-ডিটেকশন ইঞ্জিন চালায়। সাধারণ GET রিকোয়েস্টের জন্য এটি একটি ইন্টারস্টিশিয়াল চ্যালেঞ্জ (একটি CAPTCHA বা JavaScript চেক) দেখাতে পারে। ব্রাউজার চ্যালেঞ্জটি পার করার পর রিকোয়েস্টটি অগ্রসর হয়।
তবে, একটি নেভিগেশন POST-কে চ্যালেঞ্জের জন্য থামিয়ে রাখা এবং তারপর তার বডি (body) অক্ষত রেখে পুনরায় শুরু করা সম্ভব নয়। এজ (edge) রিকোয়েস্টটি বাতিল করে দেয় এবং একটি 503 স্ট্যাটাস রিটার্ন করে, যার ফলে ব্রাউজারে একটি ফাঁকা পেজ বা একটি সাধারণ এরর মেসেজ দেখায়। অটোমেটেড টেস্ট ব্রাউজারগুলো, যেগুলোর ফিঙ্গারপ্রিন্ট Cloudflare বিশ্বাস করে, কখনোই চ্যালেঞ্জটি ট্রিগার করে না, তাই প্রকৃত ব্যবহারকারী সাইটে না আসা পর্যন্ত সমস্যাটি অদৃশ্য থেকে যায়।
লগ যা প্রকাশ করেছে
একজন ব্যবহারকারীর Chrome সেশনের একটি লাইভ নেটওয়ার্ক ট্রেস একই এন্ডপয়েন্টে দুটি বিপরীতধর্মী রিকোয়েস্ট দেখিয়েছে:
- Navigation POST → 503 রেসপন্স, ট্যাব হ্যাং হয়ে যায়।
- fetch() POST → রিকোয়েস্ট সম্পন্ন হয়েছে।
উভয় রিকোয়েস্ট একই অরিজিন থেকে এসেছিল, একই ক্রেডেনশিয়াল বহন করছিল এবং একই সময়ে ঘটেছিল। একমাত্র পার্থক্য ছিল ট্রান্সপোর্ট মেথড বা পরিবহনের পদ্ধতিতে। fetch রিকোয়েস্টটি সেই ইন্টারস্টিশিয়াল ফ্লো-কে বাইপাস করেছে যা নেভিগেশন POST-গুলোকে ব্লক করে।
ব্যর্থ প্রচেষ্টাগুলো
ডেভেলপার মূল কারণটি না বুঝে বেশ কিছু সমাধানের চেষ্টা করেছিলেন:
- Turnstile টোকেন রিনিউ করেছেন, ধরে নিয়েছিলেন যে সেগুলো এক্সপায়ার হয়ে গেছে।
- IP রেঞ্জ হোয়াইটলিস্ট করেছেন, ভেবেছিলেন ব্লকটি লোকেশন-ভিত্তিক।
- এক্সটেনশন ডিজেবল করেছেন, সার্ভিস ওয়ার্কার ক্লিয়ার করেছেন এবং কুকিজ ডিলিট করেছেন।
প্রতিটি পরিবর্তনই এররটি অপরিবর্তিত রেখেছিল কারণ ব্যর্থতাটি ক্লায়েন্ট বা সার্ভার কোডে নয়, বরং আপস্ট্রিম এজ-এ (upstream at the edge) উদ্ভূত হয়েছিল।
বাস্তবসম্মত সমাধান
Cloudflare-এর সুরক্ষা বন্ধ করার পরিবর্তে, ফর্মটিকে একটি fetch-first প্যাটার্ন ব্যবহার করার জন্য পুনরায় ডিজাইন করা হয়েছিল:
- ফর্মের ডেটা সংগ্রহ করুন এবং এটি
fetch()এর মাধ্যমে একটি JSON পেলোড হিসেবে পাঠান। - সার্ভারের রেসপন্স হ্যান্ডেল করুন। যদি সার্ভার পেমেন্ট গেটওয়ের জন্য একটি URL রিটার্ন করে, তবে একটি সাধারণ GET রিকোয়েস্টের মাধ্যমে সেখানে নেভিগেট করতে
location.assign()কল করুন।
Fetch রিকোয়েস্টগুলো ইন্টারস্টিশিয়াল চ্যালেঞ্জ ট্রিগার করে না, তাই POST রিকোয়েস্টটি অরিজিন সার্ভারে পৌঁছায়। পরবর্তী GET রিডাইরেক্টটি নিরাপদে যেকোনো চ্যালেঞ্জ পার করতে পারে, কারণ GET বডি খালি থাকে এবং ব্যবহারকারী চ্যালেঞ্জটি সম্পন্ন করার পর তা পুনরায় চালানো সম্ভব।
ডেভেলপারদের জন্য ঝুঁকি
- ব্যবহারকারীর বিশ্বাস: একটি পেমেন্ট ফর্ম যা নিঃশব্দে ব্যর্থ হয় তা আস্থা কমিয়ে দেয় এবং রাজস্বের ক্ষতি করতে পারে।
- রক্ষণাবেক্ষণের বোঝা (Maintenance overhead): এই ঘটনার জন্য তিনটি প্যাচ রিলিজ এবং একদিনের তদন্তের প্রয়োজন হয়েছিল।
- টেস্টিংয়ের অন্ধস্থান (Testing blind spots): শুধুমাত্র অভ্যন্তরীণ টেস্ট এনভায়রনমেন্টের ওপর নির্ভর করলে এমন কিছু এজ-কেস ব্যর্থতা মিস হতে পারে যা শুধুমাত্র বাস্তব ক্ষেত্রে দেখা যায়।
বৃহত্তর কমিউনিটির জন্য শিক্ষা
- প্রকৃত ব্রাউজার মনিটর করুন। যখন কোনো সমস্যা শুধুমাত্র প্রকৃত ব্যবহারকারীদের ক্ষেত্রে দেখা দেয়, তখন অটোমেটেড টেস্ট রানের ওপর নির্ভর না করে সেই সেশনগুলো থেকে নেটওয়ার্ক লগ সংগ্রহ করুন।
- এজ-কে স্ট্যাকের অংশ হিসেবে বিবেচনা করুন। Cloudflare ক্লায়েন্ট এবং সার্ভারের মাঝে অবস্থান করে; এর আচরণ রিকোয়েস্টগুলো কীভাবে স্ট্রাকচার করতে হবে তা প্রভাবিত করে।
- সঠিক ট্রান্সপোর্ট বেছে নিন। নেভিগেশন POST এবং fetch POST এজ-এ ভিন্ন ভিন্ন পথ দিয়ে যাতায়াত করে। এই পার্থক্য মাথায় রেখে API ডিজাইন করুন।
- এরর ডিটেইলস প্রকাশ করুন। UI-তে 503 বা “timeout-or-duplicate” মেসেজগুলো দেখান যাতে ডেভেলপাররা লগ খোঁজার ঝামেলা ছাড়াই সঠিক ব্যর্থতার ধরন দেখতে পান।
পরবর্তীতে যা খেয়াল রাখা উচিত
ডেভেলপারদের এমন যেকোনো ফর্ম-ভিত্তিক ওয়ার্কফ্লো অডিট করা উচিত যা নেটিভ POST নেভিগেশনের ওপর নির্ভর করে, বিশেষ করে যখন Cloudflare বা অনুরূপ CDN সিকিউরিটি সার্ভিস সাইটের সামনে থাকে। একটি লাইটওয়েট fetch র্যাপার (wrapper) যোগ করলে এই ধরনের ব্যর্থতা প্রতিরোধ করা সম্ভব। মনিটরিং টুলস যা এজ-জেনারেটেড স্ট্যাটাস কোডগুলো ক্যাপচার করে, তা গ্রাহকদের কাছে পৌঁছানোর আগেই সমস্যাটি চিহ্নিত করতে পারবে।
সারসংক্ষেপ: যখন Cloudflare-এর বট চ্যালেঞ্জগুলো সক্রিয় থাকে, তখন একটি সাধারণ HTML ফর্ম সাবমিশন নিঃশব্দে ব্যর্থ হওয়ার ঝুঁকিতে থাকে। fetch()-এর মাধ্যমে POST রিকোয়েস্টটি রি-রুট করা এবং একটি GET রিডাইরেক্টের মাধ্যমে ফ্লোটি সম্পন্ন করা এজ (edge)-এর সীমাবদ্ধতাকে এড়িয়ে যেতে সাহায্য করে এবং একই সাথে নিরাপত্তা অক্ষুণ্ণ রাখে। এজ-কে কেবল একটি নেটওয়ার্ক হপ হিসেবে নয়, বরং কোড হিসেবে বিবেচনা করুন এবং সেই অনুযায়ী আপনার ট্রান্সপোর্টগুলো ডিজাইন করুন।
