സ്വന്തമായി ഒരു ഓതന്റിക്കേഷൻ ലെയർ (authentication layer) നിർമ്മിക്കുന്നത് ഒരു വലിയ നേട്ടമായിട്ടാണ് ഞാൻ കരുതിയിരുന്നത്. നിങ്ങൾക്ക് JWT-കളെ കുറിച്ച് അറിവുണ്ടെങ്കിൽ, ഒരു പാസ്വേഡ് ഹാഷ് ചെയ്യാൻ അറിയാമെങ്കിൽ, ഇതിൽ എത്രത്തോളം പ്രയാസമുണ്ടാകാനാണ്? ഞാൻ ഒരു Node ബാക്കെൻഡ് തയ്യാറാക്കി, ടോക്കണുകൾ ഇഷ്യൂ ചെയ്തു, എല്ലാം കഴിഞ്ഞു എന്ന് കരുതി. കോഡ് പ്രവർത്തിച്ചു. ടെസ്റ്റുകൾ പാസായി. എന്നാൽ യഥാർത്ഥ ആക്രമണങ്ങൾ എങ്ങനെയാണ് നടക്കുന്നത് എന്നതിനെക്കുറിച്ച് വായിക്കാൻ തുടങ്ങിയപ്പോൾ, എന്റെ അടിത്തറ ഇളകിപ്പോയി. ഇൻജക്ഷൻ (injection), എന്യൂമറേഷൻ (enumeration), സൈഡ്-ചാനൽ ലീക്കുകൾ (side-channel leaks) എന്നിവയെക്കുറിച്ചുള്ള ഓരോ അധ്യായവും എന്നെ വലിയ ആശങ്കയിലാക്കി. എന്റെ ഓത്ത് സിസ്റ്റത്തിൽ വെറും വിടവുകൾ മാത്രമല്ല ഉണ്ടായിരുന്നത്; ഞാൻ തന്നെ സ്ഥാപിച്ച നാല് തുറന്ന വാതിലുകൾ അതിലുണ്ടായിരുന്നു. ഞാൻ കണ്ടെത്തിയ കാര്യങ്ങളും അവ പരിഹരിക്കാൻ ഞാൻ ചെയ്ത മാറ്റങ്ങളും താഴെ പറയുന്നവയാണ്.
സ്ട്രിംഗ് കോൺകാറ്റനേഷൻ വഴിയുള്ള SQL ഇൻജക്ഷൻ
ആദ്യത്തെ ബഗ് വളരെ പഴയൊരു തന്ത്രമായിരുന്നു. ഉപയോക്താവിൽ നിന്ന് ലഭിക്കുന്ന ഇൻപുട്ടുകൾ ഞാൻ നേരിട്ട് SQL സ്ട്രിംഗുകളിലേക്ക് ചേർക്കുകയായിരുന്നു. എന്റെ ലോഗിൻ റൂട്ടിൽ, റിക്വസ്റ്റ് ബോഡിയിൽ നിന്നുള്ള ഇമെയിൽ എടുത്ത് SELECT * FROM users WHERE email = '${email}' എന്ന രീതിയിൽ ഒരു ക്വറിയിലേക്ക് കോൺകാറ്റനേറ്റ് ചെയ്യുകയായിരുന്നു ഞാൻ ചെയ്തത്. ഫ്രണ്ട്എൻഡ് എന്റെ നിയന്ത്രണത്തിലായതുകൊണ്ട് ഇത് അപകടമില്ലാത്തതാണെന്ന് ഞാൻ കരുതി. എന്നാൽ അതൊരു അപകടകരമായ അനുമാനമാണ്. ഒരു അറ്റാക്കർക്ക് നിങ്ങളുടെ ഫ്രണ്ട്എൻഡിന്റെ ആവശ്യമില്ല. കൃത്യമായി തയ്യാറാക്കിയ ഒരു POST ബോഡിയിലൂടെ ആ ലോഗിൻ ചെക്കിനെ ഒരു ഡാറ്റാ ബ്രീച്ചാക്കി മാറ്റാനോ അല്ലെങ്കിൽ ഡാറ്റാബേസ് പൂർണ്ണമായും നശിപ്പിക്കാനോ സാധിക്കും. ' OR '1'='1 പോലെയുള്ള ഒരു പേലോഡോ അല്ലെങ്കിൽ ടേബിളുകൾ ഡ്രോപ്പ് ചെയ്യുന്ന സ്റ്റാക്ക്ഡ് ക്വറിയോ (stacked query) അയച്ചാൽ, ആ സ്ട്രിംഗ് നേരിട്ട് എക്സിക്യൂട്ട് ചെയ്യപ്പെടുകയാണെങ്കിൽ നിങ്ങളുടെ ഡാറ്റ നഷ്ടപ്പെടും. എന്റെ കോഡും അറ്റാക്കറുടെ ഡാറ്റയും തമ്മിൽ വേർതിരിച്ചറിയാൻ ഡാറ്റാബേസിന് ഒരു വഴിയും ഞാൻ നൽകിയിരുന്നില്ല.
ഇൻപുട്ട് വാലിഡേഷൻ കൂട്ടുന്നതോ സ്ട്രിംഗുകൾ കൈകൊണ്ട് എസ്കേപ്പ് (escaping) ചെയ്യുന്നതോ ആയിരുന്നില്ല ഇതിനുള്ള പരിഹാരം. പാരാമീറ്ററൈസ്ഡ് ക്വറികൾ (parameterized queries) ആയിരുന്നു യഥാർത്ഥ പരിഹാരം. ഞാൻ node-postgres-ലേക്ക് മാറുകയും $1 പോലുള്ള പ്ലേസ്ഹോൾഡറുകൾ ഉപയോഗിക്കാൻ തുടങ്ങുകയും ചെയ്തു. ഇപ്പോൾ ക്വറി ഒരു ടെംപ്ലേറ്റ് പോലെയായി: SELECT * FROM users WHERE email = $1. ഡ്രൈവർ SQL-ഉം വാല്യൂസും വേറിട്ട ചാനലുകളിലൂടെയാണ് അയക്കുന്നത്. ഇൻപുട്ടിൽ ഏത് ക്യാരക്ടറുകൾ ഉണ്ടെങ്കിലും ഡാറ്റാബേസ് അതിനെ വെറും ഡാറ്റയായി മാത്രമേ പരിഗണിക്കൂ. ഈ ഒരു മാറ്റം ഇൻജക്ഷൻ ആക്രമണങ്ങളെ പൂർണ്ണമായും തടയുന്നു. ഇത് വായിക്കാൻ എളുപ്പമാണ്, മെയിന്റൈൻ ചെയ്യാൻ എളുപ്പമാണ്, കൂടാതെ ഓരോ തവണ WHERE ക്ലോസ് എഴുതുമ്പോഴും ഒരു regex മാന്ത്രികനാകേണ്ടി വരുന്ന ബുദ്ധിമുട്ടും ഇത് ഒഴിവാക്കുന്നു.
എറർ മെസ്സേജുകളിലൂടെയുള്ള ഇമെയിൽ എന്യൂമറേഷൻ
എന്റെ രണ്ടാമത്തെ തെറ്റ് നല്ലൊരു UX (User Experience) ആണെന്ന് തോന്നിപ്പിച്ചു. ഒരു ഉപയോക്താവ് തെറ്റായ ഇമെയിൽ ടൈപ്പ് ചെയ്യുമ്പോൾ ഞാൻ User not found എന്ന് മറുപടി നൽകി. ഇമെയിൽ ശരിയാണെങ്കിലും പാസ്വേഡ് തെറ്റാണെങ്കിൽ Incorrect password എന്ന് നൽകി. ഇത് ഉപയോക്താവിന് സഹായകരമാണെന്ന് എനിക്ക് തോന്നി. എന്നാൽ ഇത് അറ്റാക്കർമാർക്ക് വിവരങ്ങൾ ശേഖരിക്കാനുള്ള ഒരു ഉപകരണമായി മാറി. എന്യൂമറേഷൻ സ്ക്രിപ്റ്റുകൾക്ക് ആയിരക്കണക്കിന് ഇമെയിൽ അഡ്രസ്സുകൾ ഉപയോഗിച്ച് നിങ്ങളുടെ ലോഗിൻ എൻഡ്പോയിന്റിൽ ആക്രമണം നടത്താൻ കഴിയും. അക്കൗണ്ട് ഉണ്ടോ ഇല്ലയോ എന്നതിനെ ആശ്രയിച്ച് റെസ്പോൺസ് ബോഡിയോ സ്റ്റാറ്റസ് കോഡോ മാറുന്നുണ്ടെങ്കിൽ, നിങ്ങളുടെ ഉപയോക്താക്കളുടെ ഒരു ലിസ്റ്റ് നിർമ്മിക്കാൻ ആ സ്ക്രിപ്റ്റിന് സാധിക്കും. ആ ലിസ്റ്റ് ക്രെഡൻഷ്യൽ സ്റ്റഫിംഗ് (credential stuffing), ടാർഗെറ്റഡ് ഫിഷിംഗ് (targeted phishing), ബ്രൂട്ട്-ഫോഴ്സ് ശ്രമങ്ങൾ എന്നിവയ്ക്കുള്ള അടിസ്ഥാനമായി മാറും.
സുരക്ഷയ്ക്ക് വേണ്ടി ചിലപ്പോൾ യൂസർ-ഫ്രണ്ട്ലിനസ്സ് (user-friendliness) ബലികഴിക്കപ്പെടേണ്ടി വരുമെന്ന് എനിക്ക് അംഗീകരിക്കേണ്ടി വന്നു. പരാജയപ്പെട്ട എല്ലാ ലോഗിൻ പാതകളും ഒരേ സ്ട്രിംഗ് നൽകുന്ന രീതിയിലേക്ക് ഞാൻ മാറ്റി: Invalid credentials. മറ്റ് സൂചനകളില്ല. എറർ റെസ്പോൺസിൽ പ്രത്യേക ലോജിക്കുകളില്ല. ഇമെയിൽ ഇല്ലെങ്കിലും, പാസ്വേഡ് തെറ്റാണെങ്കിലും, അല്ലെങ്കിൽ അക്കൗണ്ട് ലോക്ക് ആണെങ്കിലും, ടെക്സ്റ്റ് ഒന്നുതന്നെയായിരിക്കും. ഇത് രജിസ്ട്രേഷൻ, പാസ്വേഡ് റീസെറ്റ് പ്രക്രിയകൾക്കും ബാധകമാണ്; ഒരു ഇമെയിൽ അഡ്രസ്സ് നിങ്ങളുടെ സിസ്റ്റത്തിൽ ഉണ്ടോ ഇല്ലയോ എന്ന് വെളിപ്പെടുത്തരുത്. അറ്റാക്കർമാർ ആശ്രയിക്കുന്ന ഇത്തരം വിവരങ്ങൾ ചോരുന്നത് (information leak) തടയാൻ ഒരു ജനറിക് മെസ്സേജ് മതിയാകും.
പാസ്വേഡ് താരതമ്യത്തിലെ ടൈമിംഗ് അറ്റാക്കുകൾ
മൂന്നാമത്തെ ബഗ് അദൃശ്യമായിരുന്നു. ഞാൻ
