വിദ്യാർത്ഥികൾക്ക് MongoDB ക്വറികൾ (queries) തയ്യാറാക്കാൻ അനുവദിക്കുന്ന ഒരു ലേണിംഗ് പ്ലാറ്റ്‌ഫോം, Node.js-ന്റെ vm മോഡ്യൂൾ ഒഴിവാക്കി. ഇപ്പോൾ ഓരോ ക്വറിയും എക്സിക്യൂട്ട് ചെയ്യുന്നതിന് മുമ്പ് ഒരു അബ്‌സ്‌ട്രാക്റ്റ് സിന്റാക്സ് ട്രീ (AST) ആയി മാറ്റുന്നു. ഈ മാറ്റം തകരാറിലാക്കാൻ സാധ്യതയുള്ള ഒരു സാൻഡ്‌ബോക്സ് (sandbox) ഒഴിവാക്കുകയും, തെറ്റായ രീതിയിൽ നൽകുന്ന സ്ട്രിംഗുകൾ വഴി ബാക്കെൻഡിലേക്ക് കടന്നുകൂടാൻ സാധ്യതയുള്ള ഏർബിട്രറി കോഡുകളിൽ (arbitrary code) നിന്ന് സംരക്ഷണം നൽകുകയും ചെയ്യുന്നു.

എന്തുകൊണ്ടാണ് ആദ്യത്തെ സാൻഡ്‌ബോക്സ് പരാജയപ്പെട്ടത്

ആദ്യത്തെ രീതിയിൽ ഒരു ലൈവ് ഡാറ്റാബേസ് ഹാൻഡിലിനെ (database handle) vm.runInContext എന്ന കോൾ ഉപയോഗിച്ച് പൊതിഞ്ഞു (wrapped) വെക്കുകയും, റെഗുലർ എക്സ്പ്രഷൻ (regular expression) ഉപയോഗിച്ച് ഉപയോക്താക്കളെ നിയന്ത്രിക്കാൻ ശ്രമിക്കുകയും ചെയ്തു. find അല്ലെങ്കിൽ aggregate പോലുള്ള മെത്തേഡ് പേരുകൾ മാത്രമേ ഇതിൽ അനുവദിച്ചിരുന്നുള്ളൂ; മറ്റെല്ലാ ഐഡന്റിഫയറുകളും (identifiers) ഫിൽട്ടർ ചെയ്യപ്പെടേണ്ടതായിരുന്നു.

രണ്ട് പോരായ്മകൾ ആ സമീപനത്തെ സുരക്ഷിതമല്ലാതാക്കി:

  • Regex ഫിൽട്ടറിംഗ് മറികടക്കാൻ സാധിക്കും. ബ്രാക്കറ്റ് നോട്ടേഷൻ (obj["constructor"]) ഉപയോഗിച്ച് ഏത് പ്രോപ്പർട്ടിയും ആക്സസ് ചെയ്യാൻ ജാവാസ്ക്രിപ്റ്റ് അനുവദിക്കുന്നു. ഒരു അറ്റാക്കർക്ക് Function കൺസ്ട്രക്റ്റർ (constructor) വീണ്ടെടുക്കാനും, പുതിയൊരു ഫംഗ്ഷൻ നിർമ്മിക്കാനും, തങ്ങൾക്ക് ഇഷ്ടമുള്ള ഏത് കോഡും പ്രവർത്തിപ്പിക്കാനും സാധിക്കും. സ്രോതസ്സ് (source) എണ്ണമറ്റ രീതികളിൽ മാറ്റം വരുത്താൻ കഴിയുന്നതിനാൽ, റെഗുലർ എക്സ്പ്രഷന് ഈ പ്രോപ്പർട്ടി ആക്സസ് കണ്ടെത്താൻ കഴിയില്ല.
  • vm ഒരു സെക്യൂരിറ്റി ബൗണ്ടറിയല്ല. vm ആഗോള (global) ഒബ്‌ജക്റ്റിനെ മാത്രമേ ഐസൊലേറ്റ് (isolate) ചെയ്യുന്നുള്ളൂ, എന്നാൽ മുഴുവൻ പ്രോസസിനെയും അല്ല എന്ന് Node-ന്റെ ഡോക്യുമെന്റേഷൻ വ്യക്തമാക്കുന്നു. ഒരു ലൈവ് ഡാറ്റാബേസ് കണക്ഷനെ സാൻഡ്‌ബോക്സിലേക്ക് ഇൻജക്റ്റ് ചെയ്യുന്നതിലൂടെ, ആ കണക്ഷനിൽ ഡാറ്റ എഴുതാനോ ഡിലീറ്റ് ചെയ്യാനോ ഉള്ള മെത്തേഡുകൾ ഉൾപ്പെടെയുള്ളവ വിളിക്കാനുള്ള കഴിവ് കോഡിന് ലഭിക്കുന്നു. സാൻഡ്‌ബോക്സിനുള്ളിലെ കോഡ് ഹോസ്റ്റ് പ്രോസസിനെ ബാധിക്കുന്നത് തടയാൻ ഇതിന് സാധിച്ചില്ല.

AST അടിസ്ഥാനമാക്കിയുള്ള പരിഹാരം

ടീം കോഡ് എക്സിക്യൂഷന് പകരം സ്റ്റാറ്റിക് അനാലിസിസ് (static analysis) നടപ്പിലാക്കി. ക്വറി സ്ട്രിംഗുകൾ ഇപ്പോൾ acorn പാഴ്സറിലേക്ക് (parser) നൽകുന്നു, ഇത് കോഡിന്റെ സിന്റാക്റ്റിക് ഘടനയുടെ ഒരു മരരൂപമായ (tree representation) AST നിർമ്മിക്കുന്നു. ഈ AST ഓരോ നോഡായി (node) പരിശോധിക്കുകയും ഒരു കർശനമായ വൈറ്റ്‌ലിസ്റ്റ് (whitelist) അനുസരിച്ച് പരിശോധിക്കുകയും ചെയ്യുന്നു:

  • ലിറ്ററലുകൾ (Literals), അറേകളും ഒബ്‌ജക്റ്റുകളും സാധാരണ വാല്യൂകളായി വരുമ്പോൾ മാത്രമേ അനുവദിക്കൂ.
  • മെത്തേഡ് കോളുകൾ (Method calls) മുൻകൂട്ടി നിശ്ചയിച്ച ഒരു കൂട്ടത്തിലേക്ക് (find, sort, limit, മുതലായവ) മാത്രമായി പരിമിതപ്പെടുത്തിയിരിക്കുന്നു. മറ്റേതൊരു കോളും നിരസിക്കപ്പെടും.
  • കംപ്യൂട്ടഡ് പ്രോപ്പർട്ടി ആക്സസ് (ഉദാഹരണത്തിന്, obj[expr]) അല്ലെങ്കിൽ പ്രത്യേകം ലിസ്റ്റ് ചെയ്യാത്ത മറ്റേതെങ്കിലും നോഡ് ടൈപ്പ് കണ്ടാൽ ഉടൻ തന്നെ പരാജയപ്പെടുത്തും.

പാഴ്സർ പ്രവർത്തിക്കുന്നത് ടെക്സ്റ്റിന് പകരം ട്രീയിൽ ആയതുകൊണ്ട്, വ്യത്യസ്തമായ സ്പെല്ലിംഗുകളോ ബ്രാക്കറ്റ് നോട്ടേഷൻ തന്ത്രങ്ങളോ ഉപയോഗിച്ച് ഇതിനെ കബളിപ്പിക്കാൻ കഴിയില്ല. റെഗുലർ എക്സ്പ്രഷനെ മറികടക്കാൻ സാധ്യതയുള്ള ഒരു കൺസ്ട്രക്റ്റർ ചെയിൻ (constructor chain), തിരിച്ചറിയപ്പെടാത്ത ഒരു നോഡായി കാണപ്പെടുകയും കോഡ് പ്രവർത്തിക്കുന്നതിന് മുമ്പ് തന്നെ നിരസിക്കപ്പെടുകയും ചെയ്യുന്നു.

ഇതിന്റെ സുരക്ഷാപരമായ അർത്ഥം

പുതിയ ഡിസൈൻ "ഡിനൈ ബൈ ഡിഫോൾട്ട്" (deny by default) എന്ന തത്വമാണ് പിന്തുടരുന്നത്:

  • എന്താണ് അനുവദനീയമെന്ന് നിർവചിക്കുക, എന്താണ് നിഷിദ്ധമെന്ന് മാത്രമല്ല. ടെക്സ്റ്റ് അടിസ്ഥാനമാക്കിയുള്ള അലൗ-ലിസ്റ്റിംഗ് (allow-listing) പൂർണ്ണമാകണമെന്നില്ല; എന്നാൽ AST-ക്ക് പരിമിതമായ നോഡ് ടൈപ്പുകൾ ഉള്ളതിനാൽ സമഗ്രമായ പരിശോധന സാധ്യമാണ്.
  • സാൻഡ്‌ബോക്സിനുള്ളിൽ ഒരിക്കലും ലൈവ് റിസോഴ്‌സുകൾ വെളിപ്പെടുത്തരുത്. ഒരു ഡാറ്റാബേസ് ഹാൻഡിൽ ഐസൊലേറ്റ് ചെയ്ത ഒരു കോൺടെക്സ്റ്റിലേക്ക് നൽകുന്നത് സാൻഡ്‌ബോക്സിലെ കോഡിന് ബാക്കെൻഡിലേക്ക് നേരിട്ട് പ്രവേശിക്കാൻ വഴിയൊരുക്കുന്നു. പാഴ്സർ രീതിയിൽ ഉപയോക്താവിന്റെ കോഡിന് ഒരിക്കലും ഒരു ലൈവ് ഒബ്‌ജക്റ്റ് കൈമാറുന്നില്ല; പകരം ക്വറിയുടെ ഉദ്ദേശ്യം (intent) മാത്രമാണ് അത് വേർതിരിച്ചെടുക്കുന്നത്.
  • ഘടന പരിശോധിക്കുക, തുടർന്ന് സുരക്ഷിതമായി പ്രവർത്തിപ്പിക്കുക. AST പരിശോധനയിൽ വിജയിച്ചുകഴിഞ്ഞാൽ, പ്ലാറ്റ്‌ഫോം അനുവദനീയമായ കോളുകളെ അതിന്റെ സ്വന്തം വിശ്വസനീയമായ കോഡ് പാത്ത് ഉപയോഗിച്ച് യഥാർത്ഥ MongoDB ഡ്രൈവർ മെത്തേഡുകളാക്കി മാറ്റുന്നു.

ശ്രദ്ധിക്കേണ്ട കാര്യങ്ങൾ

  • നിങ്ങളുടെ കോഡ്ബേസിൽ eval, new Function, അല്ലെങ്കിൽ vm എന്നിവയുടെ ഉപയോഗം പരിശോധിക്കുക. വൈറ്റ്‌ലിസ്റ്റ് ഉണ്ടെങ്കിൽ പോലും ജാവാസ്ക്രിപ്റ്റിന്റെ ഡൈനാമിക് സ്വഭാവം കാരണം അത് മറികടക്കാൻ സാധ്യതയുണ്ട്.
  • സാധ്യമാകുന്നിടത്തോളം ഉപയോക്താക്കൾ നിർമ്മിക്കുന്ന കോഡുകൾക്കായി AST പാഴ്സിംഗ് സ്വീകരിക്കുക. acorn, esprima, അല്ലെങ്കിൽ babel-parser പോലുള്ള ലൈബ്രറികൾ ഈ മാറ്റം എളുപ്പമാക്കുന്നു.
  • ലൈവ് ഒബ്‌ജക്റ്റുകളുടെ എക്സ്പോഷർ പരിമിതപ്പെടുത്തുക. ഒരു ഡാറ്റാബേസ് കണക്ഷൻ, ഫയൽ ഹാൻഡിൽ അല്ലെങ്കിൽ നെറ്റ്‌വർക്ക് സോക്കറ്റ് എന്നിവ ഉപയോഗിക്കേണ്ടി വന്നാൽ, നിങ്ങൾ അനുവദിക്കാൻ ഉദ്ദേശിക്കുന്ന കുറഞ്ഞ മെത്തേഡുകൾ മാത്രം വെളിപ്പെടുത്തുന്ന ഒരു പ്രോക്സിയിൽ (proxy) അവയെ പൊതിയുക.
  • എഡ്ജ് കേസുകളുടെ (edge cases) ടെസ്റ്റിംഗ് ഓട്ടോമേറ്റ് ചെയ്യുക. ബ്രാക്കറ്റ് നോട്ടേഷൻ, കംപ്യൂട്ടഡ് കീകൾ അല്ലെങ്കിൽ പ്രോട്ടോടൈപ്പ് മാനിപുലേഷൻ (prototype manipulation) എന്നിവ ഉപയോഗിക്കുന്ന ക്വറികൾ നിർമ്മിക്കുകയും നിങ്ങളുടെ പാഴ്സർ അവ നിരസിക്കുന്നുണ്ടെന്ന് ഉറപ്പുവരുത്തുകയും ചെയ്യുക.

ചുരുക്കത്തിൽ

vm.runInContext-നെ ഒരു സാൻഡ്‌ബോക്സായി ആശ്രയിക്കുന്നത് സുരക്ഷയെക്കുറിച്ച് തെറ്റായ ധാരണ നൽകുന്നു; റെഗുലർ എക്സ്പ്രഷൻ ഫിൽട്ടറുകൾക്ക് ജാവാസ്ക്രിപ്റ്റിന്റെ ഫ്ലെക്സിബിൾ സിന്റാക്സിനെ പൂർണ്ണമായി കവർ ചെയ്യാൻ കഴിയില്ല, കൂടാതെ സാൻഡ്‌ബോക്സ് പ്രോസസ്സ് റിസോഴ്‌സുകളെ ഐസൊലേറ്റ് ചെയ്യുകയുമില്ല. ഉപയോക്താവിന്റെ ഇൻപുട്ടിനെ ഒരു AST ആയി പാഴ്സ് ചെയ്യുകയും നിങ്ങൾക്ക് മനസ്സിലാകുന്ന നോഡുകൾ മാത്രം വൈറ്റ്‌ലിസ്റ്റ് ചെയ്യുകയും ചെയ്യുന്നത്, ദോഷകരമായ കോഡ് പ്രവർത്തിക്കുന്നതിന് മുമ്പ് തന്നെ അതിനെ തടയുന്ന ശക്തമായ ഒരു പ്രതിരോധം നൽകുന്നു. നിങ്ങളുടെ പ്ലാറ്റ്‌ഫോം ഉപയോക്താക്കളെ കോഡ് എഴുതാൻ അനുവദിക്കുന്നുണ്ടെങ്കിൽ, eval-രീതിയിലുള്ള എക്സിക്യൂഷന് പകരം ഇന്ന് തന്നെ സ്റ്റാറ്റിക് അനാലിസിസ് ഉപയോഗിച്ചു തുടങ്ങുക.