प्रतिनिधि इंटरव्यू विषय

बैकएंड इंटरव्यू: आप SQL इंजेक्शन को कैसे रोकते हैं?

बैकएंडकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

एक मल्टी-टेनेंट ऑर्डर-सर्च API ग्राहक ईमेल, ऑर्डर स्टेटस, सॉर्ट फ़ील्ड, सॉर्ट दिशा और रिज़ल्ट लिमिट स्वीकार करती है। एक रिव्यू में पाया गया कि ये वैल्यूज़ एक रॉ SQL स्ट्रिंग में इंटरपोलेट की गई हैं। बताएं कि आप SQL इंजेक्शन के जोखिम को कैसे हटाएंगे, डाइनैमिक ORDER BY और लिस्ट फिल्टर को कैसे संभालेंगे, टेनेंट आइसोलेशन को कैसे बनाए रखेंगे, डिफेंस इन डेप्थ कैसे जोड़ेंगे, और समाधान को कैसे साबित करेंगे।

समस्या और दायरा

एक मल्टी-टेनेंट ऑर्डर प्लेटफ़ॉर्म एक इंटरनल सर्च एंडपॉइंट एक्सपोज़ करता है। ऑथेंटिकेटेड यूज़र का टेनेंट सर्वर सेशन से उपलब्ध है। रिक्वेस्ट में ग्राहक ईमेल, ऑर्डर स्टेटस की सूची, सॉर्ट फ़ील्ड, सॉर्ट दिशा और रिज़ल्ट लिमिट शामिल हो सकती है। एक रिव्यू में इस तरह का कोड मिलता है:

typescript
const sql = `
  SELECT id, customer_email, status, total_cents, created_at
  FROM orders
  WHERE tenant_id = '${tenantId}'
    AND customer_email = '${input.email}'
    AND status IN (${input.statuses.join(",")})
  ORDER BY ${input.sort} ${input.direction}
  LIMIT ${input.limit}
`

बताएं कि आप इस क्वेरी बाउंड्री को कैसे नया रूप देंगे ताकि अटैकर-नियंत्रित टेक्स्ट SQL ग्रामर को न बदल सके। वैल्यूज़, वैकल्पिक फ़िल्टर, लिस्ट पैरामीटर, सॉर्ट कॉलम जैसे आइडेंटिफायर, स्टोर्ड प्रोसीजर, ORM रॉ क्वेरी एस्केप हैच, डेटाबेस प्रिविलेज, लॉगिंग, टेस्ट और पहले से स्टोर किए गए डेटा जिसे बाद में डाइनैमिक SQL में दोबारा उपयोग किया जाता है, को कवर करें।

मुख्य नियम यह है कि SQL कोड विश्वसनीय एप्लिकेशन कोड द्वारा निर्धारित होना चाहिए, जबकि बाहरी डेटा सर्वर-साइड पैरामीटर-बाइंडिंग इंटरफ़ेस के माध्यम से डेटाबेस तक पहुँचना चाहिए। इनपुट वैलिडेशन और न्यूनतम विशेषाधिकार (least privilege) उपयोगी अवरोध जोड़ते हैं, लेकिन वे स्ट्रिंग कॉनकेटनेशन को सुरक्षित क्वेरी में नहीं बदलते हैं।

यह एक बैकएंड प्रश्न है क्योंकि निर्णायक कौशल क्वेरी निर्माण, डेटाबेस-ड्राइवर व्यवहार, सर्विस बाउंड्री पर ऑथराइजेशन, डेटाबेस अनुमतियाँ और प्रोडक्शन सत्यापन हैं। कार्यान्वयन भाषा गौण है।

इंटरव्यूअर क्या मूल्यांकन कर रहा है

पहला संकेत यह है कि क्या उम्मीदवार केवल "prepared statement का उपयोग करें" कहने के बजाय प्रत्येक ग्रामर बाउंड्री की पहचान करता है। टेनेंट ID, ईमेल एड्रेस, स्टेटस और लिमिट जैसी वैल्यूज़ को डेटा के रूप में बाउंड किया जाना चाहिए। टेबल नाम, कॉलम नाम, SQL कीवर्ड और सॉर्ट दिशाओं को आमतौर पर एक साधारण वैल्यू प्लेसहोल्डर के माध्यम से आपूर्ति नहीं की जा सकती है, इसलिए उन्हें क्वेरी रीडिज़ाइन या सर्वर-स्वामित्व वाली अलाउलिस्ट की आवश्यकता होती है जो एक छोटे पब्लिक इनम (enum) को फिक्स्ड SQL टोकन में मैप करती है।

दूसरा संकेत इंजेक्शन रोकथाम को ऑथराइजेशन से अलग करना है। एक पैरामीटरयुक्त tenant_id अभी भी टेनेंट आइसोलेशन के लिए असुरक्षित है यदि यह रिक्वेस्ट बॉडी से आता है और कॉलर किसी अन्य टेनेंट को चुन सकता है। सर्विस को ऑथेंटिकेटेड प्रिंसिपल से टेनेंट प्राप्त करना चाहिए और इसे प्रत्येक प्रासंगिक क्वेरी में शामिल करना चाहिए। पैरामीटराइजेशन किसी वैल्यू को क्वेरी संरचना बदलने से रोकता है; यह यह साबित नहीं करता कि कॉलर उस वैल्यू तक पहुँच सकता है।

तीसरा संकेत छिपी हुई निष्पादन सीमाओं (execution boundaries) को पहचानना है। एक ORM केवल तभी सुरक्षित होता है जब कोड उसके पैरामीटरयुक्त API का सही उपयोग करता है। रॉ-क्वेरी हेल्पर्स, स्ट्रिंग-निर्मित फ़िल्टर, माइग्रेशन टूल्स, रिपोर्टिंग जॉब्स और डाइनैमिक SQL निष्पादित करने वाले स्टोर्ड प्रोसीजर्स उसी खामी को फिर से पैदा कर सकते हैं। आज सुरक्षित रूप से स्टोर किया गया डेटा सेकंड-ऑर्डर इंजेक्शन स्रोत भी बन सकता है यदि कोई अन्य जॉब बाद में इसे SQL में कॉनकेटनेेट करता है।

अंतिम संकेत लेयर्ड सत्यापन है। एक मजबूत उत्तर कोड रिव्यू, सर्वर-साइड बाइंडिंग, अलाउलिस्टेड आइडेंटिफायर्स, न्यूनतम-विशेषाधिकार डेटाबेस रोल्स, सुरक्षित एरर हैंडलिंग, हॉस्टाइल स्ट्रिंग्स के साथ इंटीग्रेशन टेस्ट, टेनेंट-आइसोलेशन एसरशन्स और डेटाबेस एरर्स की मॉनिटरिंग को जोड़ता है। यह वेब एप्लिकेशन फ़ायरवॉल या मैन्युअल एस्केपिंग को प्राथमिक सुधार के रूप में प्रस्तुत नहीं करता है।

उत्तर देने से पहले स्पष्ट करने वाले प्रश्न

  • कौन सा डेटाबेस और ड्राइवर तैनात है? प्लेसहोल्डर सिंटैक्स, ऐरे बाइंडिंग, आइडेंटिफायर-कोटिंग यूटिलिटीज़ और प्रिपेयर्ड-स्टेटमेंट व्यवहार भिन्न होते हैं। डिज़ाइन सिद्धांत पोर्टेबल है, लेकिन सटीक API तैनात ड्राइवर से मेल खाना चाहिए।
  • क्या रिक्वेस्ट tenantId की आपूर्ति करती है? यदि ऐसा है, तो ऑथराइजेशन के लिए उस फ़ील्ड को अनदेखा करें और ऑथेंटिकेटेड सर्वर संदर्भ से टेनेंट प्राप्त करें। प्रशासनिक क्रॉस-टेनेंट एक्सेस के लिए एक अलग, स्पष्ट रूप से ऑथराइज्ड पाथ की आवश्यकता होती है।
  • कौन से फ़िल्टर वैकल्पिक हैं? वैकल्पिक प्रेडिकेट्स को फिक्स्ड SQL अंशों से जोड़ा जाना चाहिए जबकि उनकी वैल्यूज़ पैरामीटर बनी रहें। एक सामान्य “किसी भी फ़ील्ड और ऑपरेटर को जोड़ें” API ग्रामर सतह का बहुत विस्तार करता है।
  • वास्तव में कौन से सॉर्ट फ़ील्ड आवश्यक हैं? यदि उत्पाद को केवल निर्माण समय और कुल राशि की आवश्यकता है, तो उन दो सार्वजनिक कुंजियों (public keys) को एक्सपोज़ करें। मनमाने कॉलम एक्सप्रेशन्स, फ़ंक्शन्स, कोलेशन्स या कॉमा-सेपरेटेड ऑर्डर क्लॉज़ स्वीकार न करें।
  • क्या एंडपॉइंट कई स्टेटस या IDs खोज सकता है? डेटाबेस ड्राइवर की ऐरे सुविधा, एक टाइप्ड ऐरे पैरामीटर, या प्लेसहोल्डर्स के एक जनरेटेड सेट का उपयोग करें। कभी भी रॉ वैल्यूज़ को SQL टेक्स्ट में न जोड़ें।
  • क्या पाथ में कोई ORM रॉ-क्वेरी API दिखाई देता है? यह पुष्टि करने के लिए कि वैल्यूज़ ड्राइवर द्वारा बाउंड हैं या पहले स्ट्रिंग में इंटरपोलेट की गई हैं, स्पष्ट रूप से असुरक्षित तरीकों और "सुरक्षित" टेम्पलेट हेल्पर्स दोनों का निरीक्षण करें।
  • क्या स्टोर्ड प्रोसीजर्स डाइनैमिक SQL का निर्माण करते हैं? एक प्रोसीजर अपने आप सुरक्षित नहीं होता है। जब प्रोसीजर SQL को इन्वोक करता है तो उसके पैरामीटर्स को डेटा बने रहना चाहिए; डाइनैमिक EXEC-शैली के पाथ को उसी रिव्यू की आवश्यकता होती है।
  • एप्लिकेशन रोल के पास क्या विशेषाधिकार हैं? एक रीड एंडपॉइंट को आम तौर पर स्कीमा स्वामित्व, DDL या असंबंधित टेबल अनुमतियों के साथ कनेक्ट नहीं होना चाहिए। रनटाइम क्रेडेंशियल्स से ऑपरेशनल और माइग्रेशन क्रेडेंशियल्स को अलग करें।
  • रिलीज़ से पहले क्या साक्ष्य आवश्यक हैं? कोड बदलने से पहले हॉस्टाइल-इनपुट कॉर्पस, टेनेंट-आइसोलेशन चेक, रॉ-क्वेरी इन्वेंट्री, डेटाबेस-रोल सत्यापन और प्रोडक्शन एरर सिग्नल्स को परिभाषित करें।

30-सेकंड उत्तर रूपरेखा

“मैं ऑथेंटिकेटेड सर्वर संदर्भ को tenantId का स्रोत बनाऊंगा, फिर डेटाबेस ड्राइवर के माध्यम से प्रत्येक डेटा वैल्यू को बाउंड करूँगा: टेनेंट, ईमेल, स्टेटस ऐरे और लिमिट। सॉर्ट कॉलम और दिशाएं ग्रामर हैं, इसलिए मैं दो पब्लिक इनम वैल्यूज़ को सर्वर के स्वामित्व वाले फिक्स्ड SQL टोकन में मैप करूँगा और बाकी सब कुछ अस्वीकार कर दूँगा। मैं ORM रॉ-क्वेरी कॉल्स और स्टोर्ड प्रोसीजर्स की इन्वेंट्री बनाऊंगा क्योंकि वे स्ट्रिंग-निर्मित SQL को फिर से पेश कर सकते हैं, और जब भी स्टोर किया गया डेटा बाद की निष्पादन सीमा तक पहुँचता है, तो मैं उसे फिर से पैरामीटरयुक्त करूँगा। फिर मैं रनटाइम डेटाबेस रोल को कम करूँगा, जेनेरिक क्लाइंट एरर लौटाऊँगा, विस्तृत आंतरिक विफलताओं की निगरानी करूँगा, और यह साबित करने वाले इंटीग्रेशन टेस्ट चलाऊँगा कि हॉस्टाइल स्ट्रिंग्स लिटरल्स बनी रहें और कभी भी किसी अन्य टेनेंट की पंक्तियाँ वापस न कर सकें।”

स्टेप-बाय-स्टेप डीप डाइव

स्टेप 1: कोड, डेटा और ऑथराइजेशन स्रोतों को चिह्नित करें

मूल क्वेरी के प्रत्येक भाग को वर्गीकृत करें:

क्वेरी भागप्रकारसुरक्षित स्रोत
SELECT, टेबल, प्रेडिकेट्सSQL ग्रामरस्टैटिक एप्लिकेशन कोड
टेनेंट IDडेटा और ऑथराइजेशन स्कोपऑथेंटिकेटेड सर्वर संदर्भ
ईमेल, स्टेटस, लिमिटडेटाबाउंड ड्राइवर पैरामीटर्स
सॉर्ट कॉलमSQL आइडेंटिफायरसर्वर अलाउलिस्ट
सॉर्ट दिशाSQL कीवर्डसर्वर अलाउलिस्ट

असुरक्षित उदाहरण रिक्वेस्ट-व्युत्पन्न स्ट्रिंग्स को पार्सिंग में भाग लेने की अनुमति देता है। कोट्स, कमेंट्स, ऑपरेटर्स या अतिरिक्त एक्सप्रेशन्स स्टेटमेंट को इससे पहले बदल सकते हैं कि डेटाबेस को पता चले कि कौन से कैरेक्टर डेटा होने चाहिए थे। कुछ संदिग्ध सबस्ट्रिंग्स की जाँच करने से सीमा बहाल नहीं होती है क्योंकि SQL में डायलेक्ट-विशिष्ट सिंटैक्स, एन्कोडिंग, कमेंट्स, फ़ंक्शन्स और पार्सर व्यवहार होते हैं।

प्रत्येक उस स्थान से शुरुआत करें जो SQL निष्पादित करता है, न केवल इस एंडपॉइंट से। रॉ-क्वेरी APIs, टेम्पलेट स्ट्रिंग्स, क्वेरी मेथड्स के आसपास स्ट्रिंग कॉनकेटनेशन, डाइनैमिक प्रोसीजर निष्पादन, रिपोर्टिंग फ़िल्टर और फ़ील्ड या ऑपरेटर नाम स्वीकार करने वाले क्वेरी बिल्डर्स खोजें। रैपर्स को तब तक ट्रेस करें जब तक आप यह न दिखा सकें कि SQL टेक्स्ट के रूप में ड्राइवर तक क्या पहुँचता है और पैरामीटर संग्रह के रूप में क्या पहुँचता है।

स्टेप 2: सर्वर पर प्रत्येक वैल्यू को बाउंड करें

एक PostgreSQL-शैली का TypeScript कार्यान्वयन वैल्यूज़ को अलग रख सकता है:

typescript
const SORT_COLUMNS = {
  createdAt: "o.created_at",
  total: "o.total_cents",
} as const

const SORT_DIRECTIONS = {
  asc: "ASC",
  desc: "DESC",
} as const

interface OrderSearchInput {
  email: string | null
  statuses: string[] | null
  sort: string
  direction: string
  limit: number
}

async function findOrders(authenticatedTenantId: string, input: OrderSearchInput) {
  if (
    !Object.hasOwn(SORT_COLUMNS, input.sort) ||
    !Object.hasOwn(SORT_DIRECTIONS, input.direction)
  ) {
    throw new Error("Unsupported sort option")
  }

  const sortColumn =
    SORT_COLUMNS[input.sort as keyof typeof SORT_COLUMNS]
  const sortDirection =
    SORT_DIRECTIONS[input.direction as keyof typeof SORT_DIRECTIONS]

  const sql = `
    SELECT o.id, o.customer_email, o.status, o.total_cents, o.created_at
    FROM orders AS o
    WHERE o.tenant_id = $1
      AND ($2::text IS NULL OR o.customer_email = $2)
      AND ($3::text[] IS NULL OR o.status = ANY($3))
    ORDER BY ${sortColumn} ${sortDirection}
    LIMIT $4
  `

  return db.query(sql, [
    authenticatedTenantId,
    input.email,
    input.statuses,
    input.limit,
  ])
}

जो इंटरपोलेशन शेष रहता है वह रनटाइम मेंबरशिप चेक के बाद केवल कॉन्स्टेंट मैप्स से चुनी गई वैल्यूज़ का उपयोग करता है। कोई भी रिक्वेस्ट स्ट्रिंग एक आइडेंटिफायर या कीवर्ड नहीं बनती है। टेनेंट, ईमेल, स्टेटस ऐरे और लिमिट ड्राइवर के पैरामीटर संग्रह में रहते हैं।

निष्पादन से पहले limit को उत्पाद की अनुमत सीमा के भीतर एक इंटीजर के रूप में मान्य (validate) करें। यह रिसोर्स उपयोग और API सेमांटिक्स की सुरक्षा करता है। यह अभी भी एक पैरामीटर के रूप में बाउंड है क्योंकि व्यावसायिक सत्यापन और कोड/डेटा पृथक्करण विभिन्न उद्देश्यों की पूर्ति करते हैं।

सुविधाजनक ऐरे पैरामीटर के बिना डेटाबेस के लिए, ऐरे की लंबाई से प्लेसहोल्डर्स जनरेट करें और प्रत्येक तत्व को बाउंड करें:

text
status IN ($3, $4, $5)
values = [tenantId, email, status1, status2, status3]

प्लेसहोल्डर टेक्स्ट विश्वसनीय कोड द्वारा जनरेट किया जा सकता है; स्टेटस वैल्यूज़ को SQL टेक्स्ट में नहीं जोड़ा जाना चाहिए। खाली-ऐरे व्यवहार को स्पष्ट रूप से परिभाषित करें। इसका अर्थ "कोई स्टेटस फ़िल्टर नहीं" या "कुछ भी मैच न करें" हो सकता है, और क्वेरी-बिल्डर व्यवहार के साथ उन विकल्पों को गलती से नहीं बदलना चाहिए।

स्टेप 3: डाइनैमिक ग्रामर को छोटा और सर्वर-स्वामित्व वाला रखें

सामान्य प्लेसहोल्डर्स वैल्यूज़ का प्रतिनिधित्व करते हैं, मनमाने आइडेंटिफायर्स या कीवर्ड्स का नहीं। "created_at" को $1 के रूप में पास करना आमतौर पर कॉलम का चयन करने के बजाय स्ट्रिंग लिटरल द्वारा सॉर्ट करता है, और वैल्यू API के साथ एक अविश्वसनीय आइडेंटिफायर का इलाज उत्पाद की आवश्यकता को हल नहीं करता है।

एक छोटी सार्वजनिक शब्दावली को एक्सपोज़ करें और इसे फिक्स्ड आंतरिक टोकन में मैप करें:

text
createdAt -> o.created_at
total     -> o.total_cents
asc       -> ASC
desc      -> DESC

अज्ञात कुंजियों को अस्वीकार करें। रिक्वेस्ट से कोटेड आइडेंटिफायर्स, SQL फ़ंक्शन्स, JSON पाथ्स, कोलेशन्स, नल-ऑर्डर क्लॉज़ या एक्सप्रेशन्स को पास न करें। यदि उत्पाद आवश्यकताएं बाद में एक परिकलित (computed) सॉर्ट जोड़ती हैं, तो उस एक्सप्रेशन को सर्वर कोड में लागू करें और इसमें एक नई सार्वजनिक कुंजी मैप करें।

एक आइडेंटिफायर-कोटिंग हेल्पर तब उपयुक्त हो सकता है जब विश्वसनीय प्रशासनिक कोड को वास्तव में एक डाइनैमिक आइडेंटिफायर की आवश्यकता हो, लेकिन मनमाने उपयोगकर्ता इनपुट को कोट करना अक्सर एंडपॉइंट की क्षमता से अधिक व्यापक क्षमता को सुरक्षित रखता है। एक सामान्य API के लिए, एक संकीर्ण मैपिंग की समीक्षा और परीक्षण करना आसान होता है।

स्टेप 4: टेनेंट आइसोलेशन को स्वतंत्र रूप से सुरक्षित रखें

सत्यापित सेशन, टोकन या सर्विस पहचान से authenticatedTenantId प्राप्त करें। JSON बॉडी, क्वेरी स्ट्रिंग या ब्राउज़र स्थिति में डुप्लिकेट टेनेंट वैल्यू पर भरोसा न करें। टेनेंट-स्वामित्व वाली पंक्तियों को छूने वाले प्रत्येक रीड और राइट में ऑथराइज्ड स्कोप शामिल होना चाहिए या रिपॉजिटरी एब्स्ट्रैक्शन का उपयोग करना चाहिए जिसके लिए इसकी आवश्यकता होती है।

टेस्ट मैट्रिक्स में किसी अन्य टेनेंट से संबंधित एक वैध ऑर्डर ID, ईमेल या स्टेटस शामिल होना चाहिए। क्वेरी को ऐसी कोई पंक्ति नहीं लौटानी चाहिए, भले ही सभी SQL वैल्यू सिंटैक्स के अनुसार हानिरहित हों। यह एक ऐसी ऑथराइजेशन विफलता को पकड़ता है जिसका केवल इंजेक्शन टेस्ट पता नहीं लगा सकते हैं।

डेटाबेस रो-लेवल सिक्योरिटी एक और सीमा जोड़ सकती है जब इसे एक भरोसेमंद सेशन पहचान के आसपास कॉन्फ़िगर किया जाता है और कनेक्शन पूलिंग के माध्यम से परीक्षण किया जाता है। यह गायब सर्विस ऑथराइजेशन, असुरक्षित सेशन-वेरिएबल हैंडलिंग या SQL कॉनकेटनेशन को माफ़ नहीं करता है। इसे अपने स्वयं के कॉन्फ़िगरेशन और परीक्षणों के साथ एक अतिरिक्त प्रवर्तन परत के रूप में मानें।

स्टेप 5: ORMs, स्टोर्ड प्रोसीजर्स और सेकंड-ऑर्डर पाथ्स का ऑडिट करें

एक ORM का सामान्य फ़िल्टर API आमतौर पर वैल्यूज़ को बाउंड करता है, लेकिन रॉ निष्पादन विधियाँ सुरक्षित पैरामीटरयुक्त रूप और स्पष्ट रूप से असुरक्षित स्ट्रिंग रूप दोनों प्रदान कर सकती हैं। सटीक मेथड, फ्रेमवर्क संस्करण और ड्राइवर पाथ की समीक्षा करें। queryRaw जैसा मेथड नाम अपने आप में अपर्याप्त प्रमाण है; साबित करें कि अंतिम SQL और वैल्यूज़ अलग से भेजे जाते हैं या नहीं।

स्टोर्ड प्रोसीजर्स केवल तभी सुरक्षित होते हैं जब उनके इनपुट पैरामीटर बने रहें। एक प्रोसीजर जो एक पैरामीटर को डाइनैमिक SQL में कॉनकेटनेेट करता है और इसे निष्पादित करता है, उसने संवेदनशील सीमा को डेटाबेस में स्थानांतरित कर दिया है। डाइनैमिक निष्पादन के लिए प्रोसीजर बॉडीज खोजें और एप्लिकेशन रोल को दिए गए विशेषाधिकारों की समीक्षा करें।

सेकंड-ऑर्डर इंजेक्शन तब होता है जब हॉस्टाइल टेक्स्ट को सामान्य डेटा के रूप में स्टोर किया जाता है और बाद में SQL ग्रामर के रूप में पुन: उपयोग किया जाता है। उदाहरण के लिए, एक इम्पोर्ट रिपोर्ट नाम को सुरक्षित रूप से सम्मिलित कर सकता है, जबकि एक नाइटली रिपोर्ट जॉब बाद में उस नाम को एक क्वेरी में कॉनकेटनेेट करता है। पहला राइट पैरामीटरयुक्त हो सकता है और फिर भी बाद के निष्पादन को असुरक्षित छोड़ सकता है। प्रत्येक निष्पादन सीमा पर पैरामीटराइजेशन या एक फिक्स्ड मैपिंग लागू करें, भले ही वैल्यू सीधे किसी रिक्वेस्ट, किसी अन्य सर्विस, फ़ाइल या किसी मौजूदा डेटाबेस पंक्ति से आई हो।

स्टेप 6: दोष को छिपाए बिना डिफेंस इन डेप्थ जोड़ें

केवल आवश्यक टेबल्स और ऑपरेशन्स के साथ एक रनटाइम डेटाबेस रोल का उपयोग करें। केवल-पढ़ने के लिए खोज सर्विस को स्कीमा का स्वामी नहीं होना चाहिए या टेबल्स को ड्रॉप करने, एक्सटेंशन बनाने या असंबंधित डेटा को अपडेट करने की अनुमति नहीं होनी चाहिए। माइग्रेशन और प्रशासन को अलग-अलग क्रेडेंशियल्स का उपयोग करना चाहिए। यदि कोई इंजेक्शन या कोई अन्य एप्लिकेशन समझौता बच जाता है तो न्यूनतम विशेषाधिकार नुकसान को सीमित करता है; यह एक असुरक्षित क्वेरी को स्वीकार्य नहीं बनाता है।

इनपुट वैलिडेशन को व्यावसायिक प्रकारों, लंबाई, इनम मेंबरशिप और सीमाओं को लागू करना चाहिए। यह मैलफ़ॉर्मड रिक्वेस्ट्स को जल्दी अस्वीकार कर सकता है और दुरुपयोग को कम कर सकता है। यह द्वितीयक बना रहता है क्योंकि एक वैल्यू जो वैलिडेशन पास करती है वह अभी भी गलत SQL संदर्भ में खतरनाक हो सकती है, और नाम जैसे मुक्त-रूप (free-form) फ़ील्ड में वैध रूप से विराम चिह्न होते हैं।

मैन्युअल एस्केपिंग डेटाबेस-विशिष्ट और नाजुक है। एक सामान्य escapeSql हेल्पर न बनाएं और इसके आउटपुट को कॉनकेटनेेट न करें। यदि किसी लीगेसी पाथ को तुरंत बदला नहीं जा सकता है, तो इसे अलग करें, डेटाबेस वेंडर की प्रलेखित सुविधा का उपयोग करें, विशेषाधिकारों को प्रतिबंधित करें, टेस्ट जोड़ें और निष्कासन को ट्रैक करें। एक वेब एप्लिकेशन फ़ायरवॉल किसी घटना के दौरान अस्थायी पहचान या वर्चुअल पैचिंग प्रदान कर सकता है, लेकिन यह साबित नहीं कर सकता कि प्रत्येक डेटाबेस निष्पादन पाथ सुरक्षित है।

क्लाइंट को एक जेनेरिक विफलता लौटाएं और रूट, ऑपरेशन, परिनियोजन (deployment) और डेटाबेस एरर क्लास के साथ एक संरचित आंतरिक इवेंट लॉग करें। कॉलर को SQL टेक्स्ट, स्टैक ट्रेस, कनेक्शन विवरण या संवेदनशील पैरामीटर वैल्यूज को प्रतिध्वनित (echo) न करें। जांच के लिए पर्याप्त आइडेंटिफायर्स को संरक्षित करते हुए सीक्रेट्स या पूर्ण व्यक्तिगत डेटा को लॉग करने से बचें।

स्टेप 7: क्वेरी बाउंड्री पर सुधार को सत्यापित करें

वास्तविक ड्राइवर और डेटाबेस डायलेक्ट के विरुद्ध इंटीग्रेशन टेस्ट बनाएं। ऐसे स्ट्रिंग्स शामिल करें जिनमें कोट्स, कमेंट मार्कर्स, सेमीकोलन, यूनिकोड, वाइल्डकार्ड कैरेक्टर और टेक्स्ट शामिल हों जो SQL जैसा दिखता हो। दावा केवल यह नहीं है कि "रिक्वेस्ट क्रैश नहीं हुई।" वैल्यू को शाब्दिक रूप से माना जाना चाहिए, क्वेरी का आकार फिक्स्ड रहना चाहिए, और एंडपॉइंट को केवल ऑथराइज्ड पंक्तियों को वापस करना चाहिए।

कम से कम निम्नलिखित को कवर करें:

  1. कोट्स या कमेंट जैसे वर्णों वाला ईमेल टेक्स्ट एक लिटरल तुलना बना रहता है।
  2. प्रत्येक अनुमत सॉर्ट कुंजी अपेक्षित फिक्स्ड ORDER BY क्लॉज़ बनाती है।
  3. अज्ञात सॉर्ट कुंजियों, दिशाओं, स्टेटस और सीमा से बाहर की लिमिट्स को क्वेरी करने से पहले अस्वीकार कर दिया जाता है।
  4. खाली, एक-तत्व और कई-तत्व वाले ऐरे का व्यवहार परिभाषित है और वे बाउंड वैल्यूज़ का उपयोग करते हैं।
  5. कोई भी कॉलर किसी भी रिक्वेस्ट फ़ील्ड को बदलकर किसी अन्य टेनेंट की पंक्ति प्राप्त नहीं कर सकता है।
  6. ORM रॉ-क्वेरी और स्टोर्ड-प्रोसीजर पाथ्स समान हॉस्टाइल कॉर्पस प्राप्त करते हैं।
  7. रिपोर्टिंग या मेंटेनेंस जॉब्स द्वारा बाद में उपयोग की जाने वाली स्टोर की गई स्ट्रिंग्स दूसरी निष्पादन सीमा पर डेटा बनी रहती हैं।
  8. रनटाइम डेटाबेस रोल स्कीमा परिवर्तन नहीं कर सकता है या असंबंधित टेबल्स तक नहीं पहुँच सकता है।

स्टैटिक एनालिसिस और कोड रिव्यू नए रॉ इंटरपोलेशन पाथ्स को रोक सकते हैं। असुरक्षित रॉ-क्वेरी विधियों और SQL-सन्निकट स्ट्रिंग निर्माण के लिए एक नियम या रिव्यू चेकपॉइंट कॉन्फ़िगर करें, लेकिन इंटीग्रेशन टेस्ट बनाए रखें क्योंकि रैपर्स, जनरेटेड कोड, प्रोसीजर्स और ड्राइवर व्यवहार एक साधारण टेक्स्ट खोज के लिए दृश्यमान नहीं हो सकते हैं।

स्टेप 8: रिलीज़, निरीक्षण और प्रतिक्रिया

डेटाबेस एरर-दर, एंडपॉइंट लेटेंसी, अस्वीकृत-इनपुट काउंट्स और क्वेरी-शेप मॉनिटरिंग के साथ रोल आउट करें। सिंटैक्स एरर्स, अनुमति विफलताओं या अस्वीकृत सॉर्ट कुंजियों में अचानक वृद्धि छूटे हुए क्लाइंट्स या सक्रिय प्रोबिंग को प्रकट कर सकती है। केवल इसे गिनने के लिए पूरे हॉस्टाइल पेलोड को लॉग न करें।

यदि वास्तविक इंजेक्शन का संदेह है, तो असुरक्षित एंडपॉइंट को अक्षम या संकीर्ण करें, प्रासंगिक परिनियोजन और ऑडिट साक्ष्य सुरक्षित रखें, उजागर हुए डेटाबेस क्रेडेंशियल्स को रोटेट करें, और यह निर्धारित करें कि रनटाइम रोल क्या पढ़ या बदल सकता है। अनधिकृत पहुँच के लिए डेटाबेस लॉग्स और व्यावसायिक रिकॉर्ड्स की समीक्षा करें, प्रत्येक समकक्ष निष्पादन पाथ की मरम्मत करें, और सामान्य पहुँच बहाल करने से पहले खोजी गई क्लास को रिग्रेशन कॉर्पस में जोड़ें।

पूर्णता का अर्थ है कि असुरक्षित ग्रामर पाथ चला गया है, टेनेंट ऑथराइजेशन लागू रहता है, रनटाइम रोल सीमित है, सभी क्वेरी एक्ज़ीक्यूटर्स की इन्वेंट्री की गई थी, और टेस्ट साबित करते हैं कि हॉस्टाइल इनपुट्स तत्काल और बाद के निष्पादन दोनों में डेटा बने रहते हैं।

उच्च-गुणवत्ता नमूना उत्तर

“मैं पहले एंडपॉइंट द्वारा उपयोग किए जाने वाले प्रत्येक SQL निष्पादन पाथ की इन्वेंट्री बनाऊंगा, जिसमें ORM रॉ मेथड्स और स्टोर्ड प्रोसीजर्स शामिल हैं। मूल क्वेरी पाँच अलग-अलग चिंताओं को मिलाती है। टेनेंट, ईमेल, स्टेटस वैल्यूज़ और लिमिट डेटा हैं; सॉर्ट कॉलम और दिशा SQL ग्रामर हैं; और टेनेंट स्कोप भी एक ऑथराइजेशन निर्णय है।

मैं ऑथेंटिकेटेड सर्वर संदर्भ से टेनेंट प्राप्त करूँगा और इसे ड्राइवर के माध्यम से ईमेल, टाइप्ड स्टेटस ऐरे और सीमित इंटीजर लिमिट के साथ बाउंड करूँगा। मैं केवल createdAt और total को सॉर्ट कुंजियों के रूप में और asc और desc को दिशाओं के रूप में एक्सपोज़ करूँगा, फिर रनटाइम सदस्यता जांच के बाद उन्हें कॉन्स्टेंट SQL टोकन में मैप करूँगा। अज्ञात वैल्यूज़ को अस्वीकार कर दिया जाएगा। यदि ड्राइवर में ऐरे बाइंडिंग का अभाव है, तो मैं केवल प्लेसहोल्डर सूची जनरेट करूँगा और प्रत्येक तत्व को अलग से बाउंड करूँगा।

मैं यह मानने के बजाय कि सभी ORM कॉल्स सुरक्षित हैं, ORM के सटीक रॉ-क्वेरी API को सत्यापित करूँगा। मैं डाइनैमिक निष्पादन के लिए स्टोर्ड प्रोसीजर्स का भी निरीक्षण करूँगा। पहले से स्टोर की गई वैल्यूज तब अविश्वसनीय रहती हैं जब कोई रिपोर्ट या बैच जॉब बाद में SQL बनाता है, इसलिए उन्हें उस निष्पादन सीमा पर फिर से पैरामीटरयुक्त किया जाना चाहिए।

डिफेंस इन डेप्थ के लिए, सर्च सर्विस एक ऐसे डेटाबेस रोल का उपयोग करेगी जो केवल आवश्यक कॉलम या व्यूज को पढ़ सकता है और स्कीमा को बदल नहीं सकता है। व्यावसायिक सत्यापन अनुमत इनम्स, लंबाई और लिमिट्स को लागू करेगा, जबकि पैरामीटराइजेशन इंजेक्शन सुरक्षा बना रहेगा। क्लाइंट प्रतिक्रियाएं जेनेरिक होंगी; आंतरिक लॉग SQL टेक्स्ट या संवेदनशील मापदंडों के बिना ऑपरेशन और एरर क्लास को कैप्चर करेंगे।

अंत में, मैं वास्तविक ड्राइवर का उपयोग करके इंटीग्रेशन टेस्ट चलाऊँगा। कोट्स, कमेंट्स, SQL जैसा दिखने वाला टेक्स्ट, यूनिकोड और ऐरे तत्व लिटरल वैल्यू बने रहने चाहिए। टेस्ट प्रत्येक अनुमत और अस्वीकृत सॉर्ट विकल्प, खाली और बड़ी सूचियों, ORM और प्रोसीजर पाथ्स, स्टोर किए गए डेटा के पुन: उपयोग और क्रॉस-टेनेंट आइडेंटिफायर्स को कवर करेंगे। सुधार केवल तभी पूरा होता है जब क्वेरी संरचना फिक्स्ड रहती है और कोई भी अनुरोध किसी अन्य टेनेंट की पंक्तियों को वापस नहीं कर सकता है।”

सामान्य गलतियाँ

  • केवल ईमेल को पैरामीटरयुक्त करना → टेनेंट, सूची तत्व, लिमिट या कोई अन्य फ़िल्टर अभी भी SQL को बदल सकता है →

प्रत्येक वैल्यू को बाउंड करें और संपूर्ण निष्पादन पाथ की इन्वेंट्री बनाएं।

  • अनुरोधित कॉलम नाम को एक सामान्य वैल्यू के रूप में बाउंड करना → एक वैल्यू प्लेसहोल्डर एक आइडेंटिफायर का प्रतिनिधित्व नहीं करता है →

एक छोटे पब्लिक इनम को फिक्स्ड सर्वर-स्वामित्व वाले SQL टोकन में मैप करें।

  • किसी भी अनुरोधित आइडेंटिफायर को कोट करना → एंडपॉइंट अभी भी कॉलर्स को एक विस्तृत ग्रामर सतह पर नियंत्रण प्रदान करता है →

केवल उत्पाद-आवश्यक सॉर्ट कुंजियों को एक्सपोज़ करें और बाकी को अस्वीकार करें।

  • एक मान्य स्थिति सूची को IN (...) में जोड़ना → वैलिडेशन ड्रिफ्ट हो सकता है और प्रत्येक तत्व SQL टेक्स्ट में फिर से प्रवेश करता है →

एक टाइप्ड ऐरे पैरामीटर का उपयोग करें या प्लेसहोल्डर जनरेट करें और प्रत्येक तत्व को बाउंड करें।

  • रिक्वेस्ट से tenantId लेना क्योंकि यह पैरामीटरयुक्त है → कोड/डेटा पृथक्करण चयनित टेनेंट को ऑथराइज नहीं करता है →

ऑथेंटिकेटेड सर्वर संदर्भ से टेनेंट स्कोप प्राप्त करें।

  • यह मान लेना कि ORM सभी इंजेक्शन को रोकता है → रॉ या असुरक्षित तरीके सामान्य बाइंडिंग को बायपास कर सकते हैं →

सटीक API और अंतिम ड्राइवर कॉल को सत्यापित करें।

  • स्ट्रिंग निर्माण को स्टोर्ड प्रोसीजर में ले जाना → प्रोसीजर के अंदर डाइनैमिक SQL इंजेक्टेबल बना रहता है →

अंतिम निष्पादन के माध्यम से प्रोसीजर इनपुट को पैरामीटरयुक्त रखें।

  • केवल मूल राइट पर सैनिटाइज़ करना → स्टोर किया गया हॉस्टाइल टेक्स्ट बाद में एक रिपोर्ट जॉब में SQL ग्रामर बन सकता है →

सेकंड-ऑर्डर इंजेक्शन के खिलाफ प्रत्येक निष्पादन सीमा को सुरक्षित रखें।

  • एक कस्टम हेल्पर के साथ कोट्स को एस्केप करना → डायलेक्ट, एन्कोडिंग और संदर्भ अंतर मैन्युअल एस्केपिंग को नाजुक बनाते हैं →

ड्राइवर के सर्वर-साइड पैरामीटर-बाइंडिंग इंटरफ़ेस का उपयोग करें।

  • एप्लिकेशन स्वामी को विशेषाधिकार देना क्योंकि क्वेरी फिक्स्ड है → किसी अन्य दोष या क्रेडेंशियल लीक में अनावश्यक प्रभाव दायरा (blast radius) होता है →

एक न्यूनतम-विशेषाधिकार रनटाइम रोल और अलग माइग्रेशन क्रेडेंशियल्स का उपयोग करें।

  • डीबगिंग के लिए डेटाबेस एरर और SQL लौटाना → कॉलर्स स्कीमा और क्वेरी विवरण सीखते हैं, जबकि लॉग व्यक्तिगत डेटा को उजागर कर सकते हैं →

एक सामान्य एरर लौटाएं और संरचित, रिडैक्टेड आंतरिक साक्ष्य रिकॉर्ड करें।

  • एक प्रसिद्ध पेलोड का परीक्षण करना और रुक जाना → ऑथराइजेशन, ऐरे, स्टोर्ड प्रोसीजर, वैकल्पिक रॉ पाथ और सेकंड-ऑर्डर निष्पादन अप्रयुक्त रहते हैं →

एक विविध कॉर्पस में क्वेरी आकार और टेनेंट परिणामों को सत्यापित करें।

फॉलो-अप प्रश्न

फॉलो-अप 1: क्या प्रिपेयर्ड स्टेटमेंट्स डाइनैमिक टेबल या कॉलम नामों की रक्षा कर सकते हैं?

सामान्य बाइंड पैरामीटर वैल्यूज़ का प्रतिनिधित्व करते हैं। वे आम तौर पर टेबल के नाम, कॉलम के नाम, ऑपरेटर्स या SQL कीवर्ड्स की जगह नहीं लेते हैं। API को फिर से डिज़ाइन करें ताकि कॉलर्स एक छोटे इनम में से चुनें, फिर प्रत्येक अनुमत कुंजी को एप्लिकेशन के स्वामित्व वाले फिक्स्ड SQL अंश में मैप करें। यदि विश्वसनीय प्रशासनिक टूलिंग को वास्तव में डाइनैमिक आइडेंटिफायर्स की आवश्यकता है, तो डेटाबेस वेंडर की आइडेंटिफायर सुविधा और बहुत संकीर्ण ऑथराइजेशन सीमा का उपयोग करें; उस क्षमता को एक सामान्य उपयोगकर्ता-सामना करने वाले सर्च एंडपॉइंट के माध्यम से एक्सपोज़ न करें।

फॉलो-अप 2: क्या SQL इंजेक्शन को रोकने के लिए एक ORM पर्याप्त है?

केवल तभी जब चयनित API अंतिम ड्राइवर कॉल के माध्यम से पैरामीटर बाइंडिंग को सुरक्षित रखता है। सामान्य समानता और फ़िल्टर के तरीके अक्सर ऐसा करते हैं। रॉ स्ट्रिंग मेथड्स, असुरक्षित वेरिएंट्स, डाइनैमिक फ़ील्ड नाम, कस्टम ऑपरेटर्स और एक्सटेंशन ऐसा नहीं कर सकते हैं। परिनियोजित फ्रेमवर्क संस्करण की समीक्षा करें, एक सुरक्षित परीक्षण परिवेश में जनरेट किए गए SQL और वैल्यूज़ का निरीक्षण करें, और हॉस्टाइल-इनपुट इंटीग्रेशन टेस्ट चलाएं। ORM गलतियों के अवसरों को कम करता है; यह इसके एस्केप हैच को समझने की आवश्यकता को दूर नहीं करता है।

फॉलो-अप 3: सेकंड-ऑर्डर SQL इंजेक्शन क्या है?

अटैकर-नियंत्रित वैल्यू को पहले मूल स्टेटमेंट को बदले बिना डेटा के रूप में स्टोर किया जाता है। बाद की प्रक्रिया उस वैल्यू को पढ़ती है और इसे डाइनैमिक SQL में कॉनकेटनेेट करती है, जहाँ यह नए स्टेटमेंट के ग्रामर को बदल देती है। सुधार बाद की निष्पादन सीमा पर होता है: स्टोर की गई वैल्यू को डेटा के रूप में बाउंड करें, या यदि इसे ग्रामर का चयन करना है तो इसे एक फिक्स्ड टोकन में मैप करें। डेटाबेस पंक्तियों, फ़ाइलों, कतारों और आंतरिक सेवाओं को अविश्वसनीय स्रोतों के रूप में मानें जब वे निष्पादन योग्य SQL को प्रभावित करते हैं।

फॉलो-अप 4: क्या एप्लिकेशन को प्रत्येक कोट, सेमीकोलन या SQL कीवर्ड को अस्वीकार करना चाहिए?

नहीं। नाम, सर्च टेक्स्ट और अन्य वैध डेटा में विराम चिह्न या ऐसे शब्द हो सकते हैं जो SQL से मिलते जुलते हों। व्यावसायिक सत्यापन को वास्तविक डोमेन प्रकार, लंबाई, प्रारूप, इनम और सीमा को लागू करना चाहिए। पैरामीटराइजेशन पूर्ण वैल्यू को लिटरल डेटा बनाकर सुरक्षा गुण प्रदान करता है। एक डिनाइलिस्ट (denylist) अपूर्ण है और वैध इनपुट को भी तोड़ सकती है।

फॉलो-अप 5: आप एक बड़ी डाइनैमिक IN सूची को कैसे संभालते हैं?

उपलब्ध होने पर डेटाबेस के टाइप्ड ऐरे या टेबल-वैल्यूड पैरामीटर का उपयोग करें, या प्रति तत्व एक प्लेसहोल्डर जनरेट करें और प्रत्येक वैल्यू को बाउंड करें। रिसोर्स नियंत्रण के लिए अधिकतम सूची आकार परिभाषित करें। बहुत बड़ी सूचियाँ एक अस्थायी टेबल, बल्क-लोड तंत्र, या एक अलग API को सही ठहरा सकती हैं, लेकिन सम्मिलित वैल्यूज़ अभी भी SQL स्ट्रिंग कॉनकेटनेशन के बजाय बाउंड या बल्क प्रोटोकॉल डेटा का उपयोग करती हैं।

फॉलो-अप 6: आप एक पुष्ट प्रोडक्शन इंजेक्शन घटना के दौरान क्या करेंगे?

असुरक्षित पाथ को प्रतिबंधित करें, परिनियोजन, डेटाबेस और एप्लिकेशन ऑडिट साक्ष्य को सुरक्षित रखें, और उन क्रेडेंशियल्स को रोटेट करें जो उजागर हो सकते हैं। जांच को सीमित करने के लिए रनटाइम रोल की अनुमतियों का उपयोग करें, अनधिकृत रीड और राइट की जांच करें, और सभी समकक्ष क्वेरी बिल्डर्स और प्रोसीजर्स की मरम्मत करें। देखे गए पाथ को रिग्रेशन टेस्ट में जोड़ें, जहाँ संभव हो विशेषाधिकार कम करें, और फिक्स्ड क्वेरी और टेनेंट सीमा सत्यापित होने के बाद ही ट्रैफ़िक बहाल करें।

सार्वजनिक स्रोत

संबंधित प्रश्न