flawopen.com/sql-injection/Javascript
JavaScript में SQL इंजेक्शन (mysql2, pg, Prisma, Knex)
pool.query() के अंदर template literal या + रिक्वेस्ट पैरामीटर को आपका SQL बदलने और हर पंक्ति लौटाने देता है (CWE-89)। mysql2, node-postgres, Prisma और Knex में सुरक्षित कॉल, Prisma के एक मेथड में ${} सुरक्षित और अगले में ख़तरनाक क्यों है, और orderByRaw के बिना सॉर्ट कैसे करें।[1], [2], [3], [4], [5], [6]
एक पार्सल छँटाई केंद्र की कल्पना करें: पार्सल आने से पहले मार्ग छापा और लॉक किया जाता है। लेबल का नाम तय खाने में जाता है; वह मार्ग में नया पड़ाव नहीं जोड़ सकता। SQL पैरामीटर भी ऐसे ही काम करते हैं: mysql2 execute(sql, [userId]) और pg query(sql, [status]) मानों को बाँधते हैं, जबकि Prisma का टैग किया हुआ $queryRaw हर ${} स्थान को बाँधता है। यदि पहले template literal या + से SQL जोड़ें, या $queryRawUnsafe चलाएँ, तो ग्राहक का पाठ सीधे मार्ग में लिख जाता है। Knex orderByRaw में छँटाई के साथ यही जोखिम है; माँगी गई कुंजी को तय कॉलम से मिलाकर orderBy चलाएँ।[1], [2], [3], [4], [5], [6]
इस पेज के मुख्य शब्द
Template literals in query()${userId}वाली बैकटिक स्ट्रिंग को JavaScript ड्राइवर बुलाए जाने से पहले ही हल कर देता है, इसलिए ड्राइवर को तैयार SQL मिलता है जिसमें मान पहले से मौजूद है। यह सिंटैक्स प्लेसहोल्डर जैसा दिखता है, पर कुछ भी बाइंड नहीं करता।[7], [1], [6]mysql2 execute() vs query()execute(sql, values)सर्वर-साइड prepared statement इस्तेमाल करता है।query(sql, values)क्लाइंट पर मानों को एस्केप करके?मार्कर में भरता है। मान array में हों तो दोनों सुरक्षित हैं; अगर SQL स्ट्रिंग+या template literal से बनी हो तो कोई मदद नहीं करता। mysql2 मेंmultipleStatementsडिफ़ॉल्ट रूप से बंद है।[3], [6], [9]pg query(text, values)- node-postgres
valuesकोtextसे अलग भेजता है, इसलिए$1PostgreSQL भरता है। मान न हों तो pg simple query protocol इस्तेमाल करता है, जो एक ही कॉल में सेमीकोलन से अलग कई स्टेटमेंट भी स्वीकार करता है।[2], [6]
फ्रेमवर्क मैपिंग
- इस गाइड में बताई गई कमजोरी के लिए CWE संदर्भ: CWE-89
- OWASP Top 10:2025 — A05:2025 Injection
हमले का चरण-दर-चरण प्रवाह
सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन
// users.js: नीचे की हर क्वेरी रिक्वेस्ट डेटा से SQL टेक्स्ट बनाती है
// mysql2: ड्राइवर बुलाए जाने से पहले ही template literal हल हो जाता है
export async function getUser(pool, userId) {
const [rows] = await pool.query(`SELECT id, email FROM users WHERE id = ${userId}`);
return rows[0];
}
// pg: स्ट्रिंग जोड़ना, और बाइंड करने को कोई values array नहीं
export async function findOrders(pg, status) {
const { rows } = await pg.query("SELECT id FROM orders WHERE status = '" + status + "'");
return rows;
}
// Prisma: Unsafe वाला मेथड तैयार स्ट्रिंग लेता है
export function findByEmail(prisma, email) {
return prisma.$queryRawUnsafe(`SELECT id FROM "User" WHERE email = '${email}'`);
}
// Knex: orderByRaw रिक्वेस्ट के मान को कच्चे SQL के रूप में डालता है
export function listUsers(knex, sortColumn) {
return knex('users').select('id', 'email').orderByRaw(sortColumn);
}
// users.js: मान values array में जाते हैं, कॉलम अनुमति-सूची से आते हैं
const SORTABLE = new Map([['id', 'id'], ['email', 'email'], ['created', 'created_at']]);
// mysql2: सर्वर-साइड prepared statement, मान ? से बँधा
export async function getUser(pool, userId) {
const [rows] = await pool.execute('SELECT id, email FROM users WHERE id = ?', [userId]);
return rows[0];
}
// pg: PostgreSQL values array से $1 भरता है
export async function findOrders(pg, status) {
const { rows } = await pg.query('SELECT id FROM orders WHERE status = $1', [status]);
return rows;
}
// Prisma: tagged template, हर ${} बाइंड पैरामीटर बनता है
export function findByEmail(prisma, email) {
return prisma.$queryRaw`SELECT id FROM "User" WHERE email = ${email}`;
}
// Knex: orderBy अनुमति-सूची वाले कॉलम को आइडेंटिफ़ायर की तरह कोट करता है
export function listUsers(knex, sortKey) {
const column = SORTABLE.get(sortKey) ?? 'id';
return knex('users').select('id', 'email').orderBy(column);
}
इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट
- ✓CI में ESLint के
no-restricted-syntaxनियम या Semgrep से$queryRawUnsafe,$executeRawUnsafe, interpolation वालेwhereRaw/orderByRawऔरquery()/execute()के पहले आर्ग्युमेंट में template literal या+पर रोक लगाएँ।[1], [3], [2], [4], [5], [6] - ✓हर मान ड्राइवर के values array में भेजें (mysql2 के लिए
?, pg के लिए$1) या Prisma के tagged$queryRawमें${}के रूप में।[3], [2], [4], [6] - ✓रूट पर schema (zod, Joi) से इनपुट जाँचें: ID संख्या बनती है, अनजान कुंजियाँ ठुकराई जाती हैं, जहाँ स्ट्रिंग चाहिए वहाँ array स्वीकार नहीं होता।[8], [6]
- ✓सॉर्ट कुंजियों को
Mapअनुमति-सूची से मिलाएँ और नतीजा Knex केorderBy()या Prisma केorderByको दें;req.queryको कभी कच्चे ORDER BY में न दें।[5], [4], [6] - ✓mysql2 का
multipleStatementsबंद रखें और बिना DDL अधिकार वाले डेटाबेस यूज़र से कनेक्ट करें।[3], [9], [6]
स्रोत
- CWE-89: Improper Neutralization of Special Elements used in an SQL Command
- node-postgres — Parameterized query
- mysql2 — Using prepared statements
- Prisma — Raw queries and SQL injection
- Knex.js — Raw parameter binding
- OWASP SQL Injection Prevention Cheat Sheet
- MDN — Template literals (JavaScript)
- Express 4 API — req.query
- MySQL C API — Multiple statement execution support