একটি রিড-অনলি (read-only) অ্যাকাউন্ট্যান্ট ওপেন-সোর্স বুককিপিং টুল Akaunting-এ ইনভয়েস বাতিল করতে এবং পেমেন্ট হিস্ট্রি মুছে ফেলতে পারতেন, যা ক্ষুদ্র ব্যবসাগুলোকে নিঃশব্দে ডেটা হারানোর ঝুঁকির মুখে ফেলেছিল। ত্রুটিটি ভার্সন 3.2.0-এ প্যাচ করা হয়েছে, তবে ভুলটি—পারমিশন চেকগুলোকে মেথড নামের একটি হার্ড-কোডেড তালিকার সাথে যুক্ত করা—এখনও সেই সমস্ত সিস্টেমের জন্য হুমকি স্বরূপ যা রোল-বেসড অ্যাক্সেস কন্ট্রোলের (role-based access control) ওপর নির্ভর করে।
কীভাবে বাগটি এড়িয়ে গেল
Akaunting-এর API মেথড নামের একটি allowlist দেখে একজন ব্যবহারকারীর অধিকার যাচাই করে। তালিকায় সাধারণ CRUD অপারেশনগুলো—create, read, update, delete—অন্তর্ভুক্ত ছিল, কিন্তু স্ট্যাটাস পরিবর্তনকারী বেশ কিছু এন্ডপয়েন্ট (endpoint) বাদ পড়ে গিয়েছিল:
markSentmarkCancelledmarkReceived
যেহেতু সেই হ্যান্ডলারগুলো তালিকায় ছিল না, তাই সেগুলো চলার সময় ফ্রেমওয়ার্ক কখনোই পারমিশন-চেকিং রুটিন কল করেনি। একজন রিড-অনলি রোলের ব্যবহারকারী markCancelled এন্ডপয়েন্টে একটি সাধারণ GET রিকোয়েস্ট পাঠাতে পারতেন এবং সিস্টেম এটিকে একটি বৈধ স্টেট পরিবর্তন হিসেবে গণ্য করত।
একটি ইনভয়েস বাতিল করা মানে কেবল ডকুমেন্টটিকে বাতিল হিসেবে চিহ্নিত করা নয়; এটি সেই ইনভয়েসের সাথে যুক্ত যেকোনো পেমেন্ট রেকর্ডও মুছে ফেলে। এর ফলে: এডিট করার অধিকার নেই এমন একজন ব্যবহারকারী একটি লেনদেনের আর্থিক রেকর্ড বা ট্রেইল মুছে ফেলতে পারেন।
টেস্টিং যা দেখিয়েছে
Akaunting-এর অফিসিয়াল Docker ইমেজে এই দুর্বলতাটি দেখা গেছে:
- একটি ইনভয়েস আপডেট করার জন্য স্ট্যান্ডার্ড PUT রিকোয়েস্ট 403 Forbidden রিটার্ন করেছিল, যা নিশ্চিত করে যে নিয়মিত আপডেট পাথটি সুরক্ষিত ছিল।
- ক্যানসেলেশন এন্ডপয়েন্টে একটি GET রিকোয়েস্ট কোনো অথরাইজেশন এরর ছাড়াই সফল হয়েছিল, যা এই ফাঁকটি প্রকাশ করে।
কেন এটি গুরুত্বপূর্ণ
কোনো স্পষ্ট অডিট ট্রেইল ছাড়াই আর্থিক বিবরণী পরিবর্তন করা সম্ভব হতে পারে, যা জালিয়াতি শনাক্ত করা কঠিন করে তোলে এবং সৎ ভুলগুলো সংশোধন করাকেও জটিল করে তোলে।
সমাধান
ভার্সন 3.2.0 পারমিশন ম্যাপটি সম্প্রসারিত করেছে যাতে আগে বাদ পড়া স্ট্যাটাস অ্যাকশনগুলো অন্তর্ভুক্ত করা যায়। সেই রিলিজ থেকে, যে কোনো রিকোয়েস্ট যা একটি ডকুমেন্টের স্টেট পরিবর্তন করে—সেটি sent, cancelled বা received হিসেবে চিহ্নিত করা হোক না কেন—তাকেও একটি স্ট্যান্ডার্ড আপডেটের মতো একই রোল ভেরিফিকেশনের মধ্য দিয়ে যেতে হবে। এটি এই প্রত্যাশাটি পুনরায় নিশ্চিত করে যে, একটি রিড-অনলি রোল প্রকৃতপক্ষে ডেটা পরিবর্তন করতে পারবে না।
ডেভেলপারদের জন্য শিক্ষা
- মেথড নামকে কখনোই নিরাপত্তার সমার্থক মনে করবেন না। একটি নতুন এন্ডপয়েন্ট যোগ করার মানে এই নয় যে এটি স্বয়ংক্রিয়ভাবে সুরক্ষা পাবে; প্রতিটি পাবলিক মেথড সাইড ইফেক্টের জন্য অডিট করুন।
- Allowlist শুধুমাত্র ততটুকুই সম্পূর্ণ যতটা সেই তালিকাটি। "ভালো" ভার্ব বা ক্রিয়ার একটি স্ট্যাটিক তালিকা অসাবধানতার জন্য একটি খোলা দরজা রেখে দেয়।
- উদ্দেশ্যকে HTTP ভার্ব থেকে আলাদা রাখুন। GET মূলত রিড-অনলি হওয়ার কথা, কিন্তু এখানে এটি একটি স্টেট পরিবর্তন করেছে। মিউটেশনগুলোকে (mutations) POST, PUT, DELETE, PATCH-এর মধ্যে সীমাবদ্ধ রাখুন।
- পারমিশন কভারেজ চেক স্বয়ংক্রিয় করুন। স্ট্যাটিক অ্যানালাইসিস টুলগুলো অথরাইজেশন কল নেই এমন কন্ট্রোলার মেথডগুলোকে চিহ্নিত করতে পারে, যা শিপ করার আগেই ফাঁকগুলো ধরতে সাহায্য করে।
- ন্যূনতম অধিকার (least-privilege) সম্পন্ন অ্যাকাউন্ট দিয়ে টেস্ট করুন। Docker-ভিত্তিক টেস্টে একটি রিড-অনলি ব্যবহারকারী ব্যবহার করা হয়েছিল; CI পাইপলাইনে এই ধরনের পরিস্থিতি পুনরায় তৈরি করলে এই ধরনের সমস্যাগুলো দ্রুত ধরা পড়ে।
পরবর্তী করণীয়
Akaunting-এর কমিউনিটি ইতিমধ্যে প্যাচ করা ভার্সনটি রিলিজ করেছে। অ্যাডমিনিস্ট্রেটরদের উচিত তাদের ইনস্ট্যান্সের ভার্সন যাচাই করা এবং দ্রুত আপডেটটি প্রয়োগ করা।
যে কোনো রোল-বেসড সিস্টেম তৈরি করা ডেভেলপারদের জন্য শিক্ষাটি স্পষ্ট: একটি পারমিশন মডেল যা প্রতিটি সম্ভাব্য অ্যাকশন মনে রাখার ওপর নির্ভর করে, তা ডিজাইনের দিক থেকেই ভঙ্গুর। কোন অপারেশনগুলো স্টেট পরিবর্তন করে তা স্পষ্টভাবে ঘোষণা করুন, ফ্রেমওয়ার্ক লেভেলে চেক প্রয়োগ করুন এবং নিয়মিত কোডবেস অডিট করুন। তবেই একটি "রিড-অনলি" লেবেল আর্থিক রেকর্ড অক্ষত রাখার জন্য নির্ভরযোগ্য হবে।
