सुरक्षा सर्वोपरि

SQL इंजेक्शन रोकथाम

उपयोगकर्ता द्वारा दिए गए मान कभी भी SQL टेक्स्ट में इंटरपोलेट नहीं किए जाते — मान पैरामीटर प्लेसहोल्डर के ज़रिए बाइंड किए जाते हैं (डायलेक्ट-विशिष्ट, जैसे $1 या ?)

पैरामीटर बाउंड
100%
उपयोगकर्ता द्वारा दिए गए सभी मान प्लेसहोल्डर का उपयोग करते हैं
मान इंटरपोलेशन
शून्य
उपयोगकर्ता डेटा कभी SQL टेक्स्ट में नहीं आता
क्रम संरक्षण
गारंटीड
प्लेसहोल्डर params से 1:1 मैप होते हैं
सुरक्षा टेस्ट
मौजूदा
इंजेक्शन प्रयासों का कवरेज

SQL इंजेक्शन को समझना

SQL इंजेक्शन आज भी वेब एप्लिकेशन की सबसे गंभीर कमज़ोरियों में से एक है

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

आम हमलों के पैटर्न

ऑथेंटिकेशन बायपास

admin' OR '1'='1

WHERE शर्त को हमेशा सत्य बनाकर लॉगिन बायपास करने का प्रयास

डेटा एक्सफ़िल्ट्रेशन

' UNION SELECT password FROM users--

दूसरी टेबल से डेटा निकालने के लिए UNION का उपयोग करने का प्रयास

विनाशकारी कमांड

'; DROP TABLE users; --

डेटा हटाने के लिए अतिरिक्त स्टेटमेंट जोड़ने का प्रयास

पैरामीटर क्रम बग का दुरुपयोग

एक्सप्लॉइट: यदि प्लेसहोल्डर और params मेल नहीं खाते, तो ऑथराइज़ेशन जाँच गलत मानों का उपयोग कर सकती है

उदाहरण: WHERE userId=$1 AND isAdmin=$2, लेकिन [123, true] के बजाय params=[true, 123] होने पर गलती से admin एक्सेस मिल सकता है

बहु-स्तरीय सुरक्षा रणनीति

हर क्वेरी कई सुरक्षा सत्यापन लेयर से गुज़रती है

  1. लेयर 1: स्वचालित पैरामीटर बाइंडिंग

    उपयोगकर्ता द्वारा दिए गए सभी मान बाउंड पैरामीटर में बदले जाते हैं। मान SQL टेक्स्ट में लिटरल के रूप में प्रवेश नहीं करते।

    • उपयोगकर्ता मान डायलेक्ट-विशिष्ट प्लेसहोल्डर (जैसे $1 या ?) से दर्शाए जाते हैं
    • ऐरे इनपुट पैरामीटराइज़ किए जाते हैं (डायलेक्ट/रणनीति के अनुसार विस्तारित प्लेसहोल्डर या ऐरे पैरामीटर)
    • NULL और वैकल्पिक मान उपयोगकर्ता इनपुट को SQL टेक्स्ट में जोड़े बिना संभाले जाते हैं
    • Date/time मान पैरामीटराइज़ किए जाते हैं
    • JSON मान पैरामीटराइज़ किए जाते हैं
  2. लेयर 2: फ़ील्ड नाम सत्यापन

    क्वेरी जनरेशन से पहले हर फ़ील्ड नाम को Prisma स्कीमा मेटाडेटा के विरुद्ध सत्यापित किया जाता है।

    • केवल स्कीमा में परिभाषित फ़ील्ड की अनुमति है
    • रिलेशन फ़िल्टर स्कीमा मेटाडेटा के ज़रिए सत्यापित होते हैं
    • कोई मनमाना फ़ील्ड नाम स्वीकार नहीं किया जाता
    • जहाँ लागू हो, असमर्थित/कंप्यूटेड फ़ील्ड अस्वीकार किए जाते हैं
    • प्रोटोटाइप पॉल्यूशन पेलोड अस्वीकार किए जाते हैं (जैसे __proto__, constructor)
  3. लेयर 3: आइडेंटिफ़ायर सैनिटाइज़ेशन

    टेबल नाम, कॉलम नाम और एलियास सत्यापित किए जाते हैं और आवश्यकता के अनुसार सुरक्षित रूप से क्वोट/एस्केप किए जाते हैं।

    • कंट्रोल कैरेक्टर अस्वीकार किए जाते हैं
    • आवश्यकता होने पर आरक्षित कीवर्ड क्वोट किए जाते हैं
    • जहाँ कॉन्फ़िगर हो, स्कीमा क्वालिफ़िकेशन समर्थित है
    • आइडेंटिफ़ायर के लिए डबल-क्वोट एस्केपिंग (या डायलेक्ट का समतुल्य)
    • हर डायलेक्ट के अनुसार अधिकतम आइडेंटिफ़ायर लंबाई लागू
  4. लेयर 4: ऑपरेटर सत्यापन

    हर फ़ील्ड टाइप के लिए केवल ज्ञात, सुरक्षित ऑपरेटर की अनुमति है।

    • स्ट्रिंग ऑपरेटर: contains, startsWith, endsWith
    • न्यूमेरिक ऑपरेटर: lt, lte, gt, gte
    • ऐरे ऑपरेटर: टाइप सत्यापन के साथ in, notIn
    • लॉजिकल ऑपरेटर: AND, OR, NOT संरचनात्मक रूप से सत्यापित
    • अज्ञात ऑपरेटर तुरंत अस्वीकार
  5. लेयर 5: पैरामीटर क्रम की गारंटी

    सख़्त क्रम सुनिश्चित करता है कि हर प्लेसहोल्डर इंसर्शन क्रम में ठीक एक पैरामीटर से मैप हो, जिससे बेमेल की स्थिति नहीं बनती।

    • क्रमिक काउंटर क्रम बदलने से रोकता है
    • SQL निर्माण और param ऐरे निर्माण साथ-साथ (lockstep) होते हैं
    • कोई मध्यवर्ती बफ़र या क्रम बदलने वाला ऑपरेशन नहीं
    • टेस्ट सत्यापन: जनरेट किया गया SQL और params हमेशा मेल खाते हैं
    • एक-से-एक संबंध: प्लेसहोल्डर N → params[N-1] (या डायलेक्ट के लिए समतुल्य मैपिंग)

औपचारिक सुरक्षा गारंटी

बताई गई डिज़ाइन मान्यताओं (पैरामीटर बाइंडिंग, कोई raw-SQL बायपास नहीं, ड्राइवर का सही उपयोग) के तहत इंजेक्शन प्रतिरोध के प्रमाण-रेखाचित्र

प्रमेय 1: मान पृथक्करण

उपयोगकर्ता द्वारा दिए गए सभी मानों V के लिए, ऐसा कोई निष्पादन पथ नहीं है जिसमें V जनरेट की गई क्वेरी स्ट्रिंग में SQL टेक्स्ट के रूप में दिखाई दे।

निर्माण के अनुसार, उपयोगकर्ता मानों को संभालने वाले सभी कोड पथ addParameter(value) को कॉल करते हैं, जो: 1. मान को एक अलग params ऐरे में संग्रहीत करता है 2. SQL टेक्स्ट के लिए एक प्लेसहोल्डर टोकन लौटाता है 3. SQL स्ट्रिंग में केवल प्लेसहोल्डर टोकन जोड़ा जाता है 4. डेटाबेस ड्राइवर SQL टेक्स्ट और पैरामीटर मान अलग-अलग प्राप्त करता है ∴ उपयोगकर्ता मानों को SQL सिंटैक्स के रूप में पार्स नहीं किया जाता

const addParameter = (params: any[], value: any) => {
  params.push(value)
  const index = params.length
  return `$${index}`
}

प्रमेय 2: फ़ील्ड नाम क्लोज़र

किसी भी जनरेट की गई क्वेरी में फ़ील्ड नामों का समुच्चय F, स्कीमा में परिभाषित फ़ील्ड S का उपसमुच्चय है, अर्थात F ⊆ S।

हर फ़ील्ड संदर्भ के लिए: 1. क्वेरी ऑब्जेक्ट से फ़ील्ड नाम निकाला जाता है 2. स्कीमा मेटाडेटा में लुकअप किया जाता है 3. यदि फ़ील्ड स्कीमा में मौजूद नहीं है, तो एरर थ्रो की जाती है 4. केवल सत्यापित फ़ील्ड SQL जनरेशन तक पहुँचते हैं ∴ मनमाने फ़ील्ड नाम SQL में नहीं आ सकते

const validateField = (field: string, model: Model) => {
  if (!model.fields.has(field)) {
    throw new Error(`Field ${field} does not exist`)
  }
  return model.fields.get(field)
}

प्रमेय 3: ऑपरेटर सुरक्षा

फ़ील्ड F पर लागू किसी भी ऑपरेटर O के लिए, O फ़ील्ड F के टाइप T के लिए अनुमत ऑपरेटरों में से एक होता है।

ऑपरेटर सत्यापन एल्गोरिदम: 1. स्कीमा से फ़ील्ड टाइप T निकालें 2. allowed_ops(T) = { टाइप T के लिए मान्य ऑपरेटर } परिभाषित करें 3. क्वेरी में ऑपरेटर O के लिए: - यदि O ∉ allowed_ops(T), तो एरर थ्रो करें - अन्यथा पैरामीटर बाइंडिंग के साथ ऑपरेटर लागू करें ∴ केवल टाइप के अनुरूप ऑपरेटर उपयोग किए जा सकते हैं

const ALLOWED_OPS: Record<string, string[]> = {
  String: ['contains', 'startsWith', 'endsWith'],
  Int: ['lt', 'lte', 'gt', 'gte']
}
if (!ALLOWED_OPS[fieldType]?.includes(operator)) {
  throw new Error('Invalid operator')
}

प्रमेय 4: आइडेंटिफ़ायर सुरक्षा

सभी SQL आइडेंटिफ़ायर I को सत्यापित किया जाता है ताकि कंट्रोल कैरेक्टर रोके जा सकें और हर डायलेक्ट के अनुसार सुरक्षित क्वोटिंग/एस्केपिंग सुनिश्चित हो।

आइडेंटिफ़ायर प्रोसेसिंग: 1. कंट्रोल कैरेक्टर होने पर अस्वीकार करें 2. डायलेक्ट के नियमों के अनुसार आंतरिक क्वोट कैरेक्टर एस्केप करें 3. आवश्यकता होने पर (आरक्षित कीवर्ड या विशेष कैरेक्टर) आइडेंटिफ़ायर क्वोट करें 4. हर डायलेक्ट की आइडेंटिफ़ायर लंबाई सीमा लागू करें ∴ आइडेंटिफ़ायर SQL सिंटैक्स नहीं तोड़ सकते और न ही इंजेक्ट किए गए टोकन जोड़ सकते हैं

const quoteIdentifier = (id: string) => {
  if (/[\x00-\x1F]/.test(id)) {
    throw new Error('Invalid characters')
  }
  const escaped = id.replace(/"/g, '""')
  const needsQuoting = /[^a-z0-9_]/i.test(id) || isReservedKeyword(id)
  return needsQuoting ? `"${escaped}"` : id
}

प्रमेय 5: पैरामीटर क्रम की सुसंगतता

SQL टेक्स्ट में जोड़ी गई हर प्लेसहोल्डर स्थिति N के लिए, params[N-1] में उसी स्थिति के लिए अभिप्रेत सटीक मान होता है, बिना किसी क्रम-परिवर्तन या बेमेल के।

पैरामीटर क्रम की गारंटी: 1. एक ही ट्रैकर इंसर्शन क्रम बनाए रखता है 2. हर add() कॉल मान को params में जोड़ती है और तुरंत अगला प्लेसहोल्डर टोकन देती है 3. SQL टेक्स्ट निर्माण और params निर्माण साथ-साथ (lockstep) चलते हैं 4. कोई मध्यवर्ती क्रम-परिवर्तन ऑपरेशन नहीं होता ∴ शुरू से अंत तक एक-से-एक संबंध बना रहता है

class ParameterTracker {
  private params: any[] = []

  add(value: any): string {
    this.params.push(value)
    return `$${this.params.length}`
  }

  getParams(): any[] {
    return this.params
  }
}

const tracker = new ParameterTracker()
const sql = `WHERE email = ${tracker.add(email)} AND age > ${tracker.add(age)}`
const params = tracker.getParams()

व्यापक सुरक्षा टेस्ट कवरेज

मौजूदा सूट आम SQL इंजेक्शन वेक्टर और एज केस के विरुद्ध सुरक्षा को सत्यापित करता है

मान पैरामीटराइज़ेशन (basic.test.ts)

  • कोट वाली स्ट्रिंग: user'with'quotes
  • सेमीकोलन वाली स्ट्रिंग: user;extra
  • मान के रूप में SQL कीवर्ड: DROP TABLE users
  • जटिल इंजेक्शन: '; DROP TABLE users; --
  • Union हमले: ' UNION SELECT * FROM users--
  • Boolean हमले: admin' OR '1'='1
  • कमेंट इंजेक्शन: test@example.com' -- comment

फ़ील्ड नाम सत्यापन (identifiers.test.ts)

  • SELECT में दुर्भावनापूर्ण फ़ील्ड नाम अस्वीकार करें
  • WHERE में दुर्भावनापूर्ण फ़ील्ड नाम अस्वीकार करें
  • ORDER BY में दुर्भावनापूर्ण फ़ील्ड नाम अस्वीकार करें
  • फ़ील्ड नामों में SQL इंजेक्शन अस्वीकार करें
  • अस्तित्वहीन फ़ील्ड अस्वीकार करें
  • प्रोटोटाइप पॉल्यूशन अस्वीकार करें: __proto__, constructor

LIKE पैटर्न सुरक्षा (like-patterns.test.ts)

  • वाइल्डकार्ड इंजेक्शन: %' OR '1'='1
  • अंडरस्कोर इंजेक्शन: test_' OR '1'='1
  • बैकस्लैश प्रबंधन: test\\'; DROP--
  • एकाधिक वाइल्डकार्ड: %_%'; DROP--
  • केस-इनसेंसिटिव इंजेक्शन: '; UNION SELECT--

ऐरे ऑपरेटर सुरक्षा (array-operators.test.ts)

  • दुर्भावनापूर्ण ऐरे के साथ IN: ['; DROP--', 'UNION SELECT--']
  • इंजेक्शन के साथ NOT IN: ['; TRUNCATE--', 'DELETE FROM--']
  • खाली ऐरे प्रबंधन
  • बड़े ऐरे का सत्यापन (100+ आइटम)
  • मिश्रित टाइप वाले ऐरे का प्रबंधन

एज केस (edge-cases.test.ts)

  • Unicode इंजेक्शन: \u0027 OR \u00271\u0027=\u00271
  • Hex-एन्कोडेड इंजेक्शन: 0x31=0x31--
  • URL एन्कोडेड: %27%3B%20DROP%20TABLE
  • स्टैक्ड क्वेरी: '; DROP TABLE users; SELECT
  • टाइम-आधारित ब्लाइंड (डायलेक्ट-विशिष्ट): WAITFOR DELAY '00:00:05'--
  • UNION-आधारित: UNION ALL SELECT null, password
  • सेकंड-ऑर्डर इंजेक्शन प्रयास

पैरामीटर क्रम सत्यापन (basic.test.ts)

  • क्रमिक प्लेसहोल्डर स्थितियाँ
  • पैरामीटर ऐरे प्लेसहोल्डर क्रम से मेल खाता है
  • जटिल क्वेरी सभी शर्तों में क्रम बनाए रखती हैं
  • नेस्टेड OR/AND शर्तें पैरामीटर अनुक्रम सुरक्षित रखती हैं
  • रिलेशन फ़िल्टर सही पैरामीटर मैपिंग बनाए रखते हैं

सुरक्षित बनाम असुरक्षित: कोड तुलना

❌ असुरक्षित: स्ट्रिंग कॉनकैटिनेशन
unsafe.ts
const email = req.body.email
const sql = "SELECT * FROM users WHERE email = '" + email + "'"
✅ सुरक्षित: स्वचालित पैरामीटर बाइंडिंग
safe.ts
const email = req.body.email
const { sql, params } = toSQL('User', 'findMany', {
  where: { email }
})

const result = { sql, params }

कार्यान्वयन विवरण

पैरामीटर प्रबंधन

केंद्रीकृत पैरामीटर ट्रैकिंग सुनिश्चित करती है कि उपयोगकर्ता द्वारा दिया गया हर मान पैरामीटर के रूप में बाउंड हो और उसका क्रम स्थिर रहे

class ParameterTracker {
  private params: any[] = []

  add(value: any): string {
    this.params.push(value)
    return `$${this.params.length}`
  }

  getParams(): any[] {
    return this.params
  }
}

फ़ील्ड सत्यापन

स्कीमा-आधारित सत्यापन मनमाने फ़ील्ड एक्सेस को रोकता है

function validateField(
  fieldName: string,
  model: ModelInfo
): FieldInfo {
  const field = model.fields.get(fieldName)
  if (!field) {
    throw new Error(
      `Field "${fieldName}" does not exist`
    )
  }
  return field
}

आइडेंटिफ़ायर क्वोटिंग

डायलेक्ट-अवेयर आइडेंटिफ़ायर सत्यापन और क्वोटिंग, आइडेंटिफ़ायर-आधारित सिंटैक्स इंजेक्शन को रोकते हैं

function quoteIdentifier(identifier: string): string {
  if (/[\x00-\x1F]/.test(identifier)) {
    throw new Error('Invalid control characters')
  }

  const escaped = identifier.replace(/"/g, '""')
  const needsQuoting = /[^a-z0-9_]/i.test(identifier) || isReservedKeyword(identifier)

  if (needsQuoting) {
    return `"${escaped}"`
  }

  return identifier
}

सुरक्षा के सर्वोत्तम तरीके

सत्यापन कभी बंद न करें

स्कीमा सत्यापन या फ़ील्ड जाँच को बायपास न करें। ये महत्वपूर्ण सुरक्षा लेयर हैं।

करें
लाइब्रेरी को पूरे सत्यापन के साथ उसी तरह उपयोग करें जैसे उसे डिज़ाइन किया गया है
न करें
स्कीमा सत्यापन को बायपास करना या क्वेरी टेक्स्ट में raw SQL इंजेक्ट करना

स्कीमा अपडेट रखें

सुनिश्चित करें कि आपका Prisma स्कीमा आपके डेटाबेस की संरचना को सटीक रूप से दर्शाता है।

करें
स्कीमा बदलने के बाद prisma db pull और prisma generate चलाएँ
न करें
पुराने DMMF या स्कीमा परिभाषाओं का उपयोग करना

इनपुट टाइप सत्यापित करें

TypeScript कंपाइल-टाइम सुरक्षा देता है, लेकिन रनटाइम सत्यापन सुरक्षा की एक अतिरिक्त परत (defense in depth) जोड़ता है।

करें
क्वेरी आर्गुमेंट के लिए Prisma के जनरेट किए गए टाइप का उपयोग करें
न करें
क्वेरी में भेजने से पहले उपयोगकर्ता इनपुट को any में कास्ट करना

क्वेरी पैटर्न की निगरानी करें

विसंगतियों और दुरुपयोग का पता लगाने के लिए प्रोडक्शन में जनरेट किए गए SQL और पैरामीटर को लॉग करें और उनकी निगरानी करें।

करें
क्वेरी लॉगिंग चालू करें और पैटर्न की समीक्षा करें
न करें
बिना निगरानी के प्रोडक्शन में चलाना

डिज़ाइन से सुरक्षा

SQL इंजेक्शन से सुरक्षा इस लाइब्रेरी की हर लेयर में शामिल है। सुरक्षा टेस्ट और कार्यान्वयन विवरण की सीधे समीक्षा करें।