একটি লার্নিং প্ল্যাটফর্ম যা শিক্ষার্থীদের MongoDB কুয়েরি তৈরি করতে দেয়, তারা Node.js-এর vm মডিউলটি বাদ দিয়েছে এবং এখন প্রতিটি কুয়েরিকে এক্সিকিউশন বা কার্যকর করার আগে একটি abstract syntax tree (AST)-এ পার্স (parse) করে। এই পরিবর্তনের ফলে একটি ভঙ্গুর স্যান্ডবক্স (sandbox) অপসারিত হয়েছে, যা ব্যাকএন্ডকে এমন যেকোনো ক্ষতিকারক কোড থেকে রক্ষা করে যা একটি ত্রুটিপূর্ণ স্ট্রিং-এর মাধ্যমে ইনজেক্ট করা হতে পারে।

কেন মূল স্যান্ডবক্সটি ব্যর্থ হয়েছিল

প্রথম ইমপ্লিমেন্টেশনে একটি লাইভ ডাটাবেস হ্যান্ডেলকে vm.runInContext কলের মাধ্যমে র‍্যাপ (wrap) করা হয়েছিল এবং রেগুলার এক্সপ্রেশন (regular expression) ব্যবহার করে ব্যবহারকারীদের সীমাবদ্ধ করার চেষ্টা করা হয়েছিল। এটি শুধুমাত্র find বা aggregate-এর মতো মেথড নামগুলোকে অনুমতি দিত; অন্য সব আইডেন্টিফায়ার ফিল্টার করে বাদ দেওয়ার কথা ছিল।

দুটি ত্রুটির কারণে সেই পদ্ধতিটি অনিরাপদ ছিল:

  • Regex ফিল্টারিং বাইপাস করা সম্ভব। JavaScript কোডকে ব্র্যাকেট নোটেশন (obj["constructor"]) ব্যবহার করে যেকোনো প্রপার্টিতে পৌঁছানোর সুযোগ দেয়। একজন আক্রমণকারী Function কনস্ট্রাক্টরটি উদ্ধার করতে পারে, একটি নতুন ফাংশন তৈরি করতে পারে এবং তাদের পছন্দমতো যেকোনো কোড চালাতে পারে। রেগুলার এক্সপ্রেশন কখনোই মূল প্রপার্টি অ্যাক্সেসটি দেখতে পায় না কারণ সোর্স কোডটি অসংখ্য উপায়ে পুনরায় লেখা যেতে পারে।
  • vm কোনো সিকিউরিটি বাউন্ডারি নয়। Node-এর ডকুমেন্টেশনে বলা হয়েছে যে vm শুধুমাত্র global অবজেক্টকে আইসোলেট (isolate) করে, পুরো প্রসেসকে নয়। স্যান্ডবক্সের মধ্যে একটি লাইভ ডাটাবেস কানেকশন ইনজেক্ট করার ফলে, কনটেক্সটের ভেতরের কোডটির সেই কানেকশনে থাকা যেকোনো মেথড কল করার ক্ষমতা থেকে যায়, যার মধ্যে ডাটা লেখা বা মুছে ফেলার মেথডও অন্তর্ভুক্ত। স্যান্ডবক্সটি কোডটিকে হোস্ট প্রসেসকে প্রভাবিত করা থেকে আটকাতে পারেনি।

AST-ভিত্তিক সমাধান

টিমটি কোড এক্সিকিউশনের পরিবর্তে স্ট্যাটিক অ্যানালাইসিস (static analysis) ব্যবহার করেছে। কুয়েরি স্ট্রিংগুলো এখন acorn পার্সারে পাঠানো হয়, যা একটি AST তৈরি করে—এটি হলো কোডের সিনট্যাকটিক স্ট্রাকচারের একটি ট্রি রিপ্রেজেন্টেশন। একটি কঠোর হোয়াইটলিস্টের (whitelist) বিপরীতে AST-কে নোড-বাই-নোড পরীক্ষা করা হয়:

  • Literals, arrays এবং objects শুধুমাত্র তখনই অনুমতি দেওয়া হয় যখন সেগুলো সাধারণ ভ্যালু হিসেবে থাকে।
  • Method calls একটি পূর্বনির্ধারিত সেটের (find, sort, limit, ইত্যাদি) মধ্যে সীমাবদ্ধ। অন্য যেকোনো কল প্রত্যাখ্যান করা হয়।
  • Computed property access (যেমন, obj[expr]) বা স্পষ্টভাবে তালিকাভুক্ত নয় এমন যেকোনো নোড টাইপ তাৎক্ষণিক ব্যর্থতা (failure) ঘটিয়ে দেয়।

যেহেতু পার্সারটি র-টেক্সট (raw text) এর পরিবর্তে ট্রি-এর ওপর কাজ করে, তাই বিকল্প বানান বা ব্র্যাকেট-নোটেশনের কারসাজি দিয়ে একে ধোঁকা দেওয়া সম্ভব নয়। একটি কনস্ট্রাক্টর চেইন যা রেগুলার এক্সপ্রেশনকে ফাঁকি দিয়ে চলে যেত, সেটি একটি অপরিচিত নোড হিসেবে প্রদর্শিত হয় এবং কোনো কোড চলার আগেই প্রত্যাখ্যান করা হয়।

নিরাপত্তার জন্য এর অর্থ কী

নতুন ডিজাইনটি “deny by default” (ডিফল্টভাবে প্রত্যাখ্যান) দর্শন অনুসরণ করে:

  • কী অনুমোদিত তা নির্ধারণ করুন, কী নিষিদ্ধ তা নয়। টেক্সচুয়াল অ্যালাউ-লিস্টিং (allow-listing) কখনোই সম্পূর্ণ হতে পারে না; কিন্তু একটি AST-তে নোড টাইপের একটি নির্দিষ্ট সেট থাকে, যা পুঙ্খানুপুঙ্খভাবে পরীক্ষা করা সম্ভব করে তোলে।
  • স্যান্ডবক্সের ভেতরে কখনোই লাইভ রিসোর্স উন্মুক্ত করবেন না। একটি আইসোলেটেড কনটেক্সটে ডাটাবেস হ্যান্ডেল পাস করলে স্যান্ডবক্সড কোডটি ব্যাকএন্ডে সরাসরি প্রবেশের সুযোগ পেয়ে যায়। পার্সার পদ্ধতিটি ইউজার কোডকে কখনোই কোনো লাইভ অবজেক্ট দেয় না; এটি শুধুমাত্র কুয়েরির উদ্দেশ্যটি (intent) বের করে আনে।
  • স্ট্রাকচার যাচাই করুন, তারপর নিরাপদে এক্সিকিউট করুন। একবার AST ভ্যালিডেশন পাস করলে, প্ল্যাটফর্মটি তার নিজস্ব বিশ্বস্ত কোড পাথ ব্যবহার করে অনুমোদিত কলগুলোকে প্রকৃত MongoDB ড্রাইভার মেথডে রূপান্তর করে।

পরবর্তীতে যা খেয়াল রাখতে হবে

  • আপনার কোডবেসে eval, new Function, বা vm-এর যেকোনো ব্যবহার অডিট (audit) করুন। এমনকি একটি হোয়াইটলিস্টও JavaScript-এর ডায়নামিক প্রকৃতির কারণে ক্ষতিগ্রস্ত হতে পারে।
  • যেখানে সম্ভব ইউজার-জেনারেটেড কোডের জন্য AST parsing গ্রহণ করুন। acorn, esprima, বা babel-parser-এর মতো লাইব্রেরিগুলো এই রূপান্তরকে সহজ করে তোলে।
  • লাইভ অবজেক্টের এক্সপোজার সীমিত করুন। যদি একটি ডাটাবেস কানেকশন, ফাইল হ্যান্ডেল বা নেটওয়ার্ক সকেট ব্যবহার করা প্রয়োজন হয়, তবে সেটিকে একটি প্রক্সির (proxy) মাধ্যমে র‍্যাপ করুন যা শুধুমাত্র আপনার অনুমোদিত ন্যূনতম মেথডগুলো প্রকাশ করবে।
  • এজ কেসগুলোর (edge cases) টেস্টিং অটোমেট করুন। আপনার পার্সার এগুলো প্রত্যাখ্যান করছে কিনা তা যাচাই করতে ব্র্যাকেট নোটেশন, কম্পিউটেড কি (computed keys), বা প্রোটোটাইপ ম্যানিপুলেশন ব্যবহার করে কুয়েরি তৈরি করুন।

সারসংক্ষেপ

স্যান্ডবক্স হিসেবে vm.runInContext-এর ওপর নির্ভর করা নিরাপত্তার একটি মিথ্যা ধারণা দেয়; রেগুলার এক্সপ্রেশন ফিল্টার JavaScript-এর নমনীয় সিনট্যাক্সকে কভার করতে পারে না এবং স্যান্ডবক্স প্রসেস রিসোর্সগুলোকে আইসোলেট করতে পারে না। ইউজার ইনপুটকে একটি AST-তে পার্স করা এবং শুধুমাত্র আপনার পরিচিত নোডগুলোকে হোয়াইটলিস্ট করা একটি সুনির্দিষ্ট ও রক্ষণাবেক্ষণযোগ্য বাধা প্রদান করে যা ক্ষতিকারক কোড চলার আগেই তা থামিয়ে দেয়। আপনার প্ল্যাটফর্ম যদি ব্যবহারকারীদের কোড লিখতে দেয়, তবে আজই eval-স্টাইল এক্সিকিউশনের পরিবর্তে স্ট্যাটিক অ্যানালাইসিস ব্যবহার শুরু করুন।