क्यों न केवल गैर-मानकीकृत प्रश्नों को एक त्रुटि लौटाएं?


22

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

मुझे लगता है कि इसे ठीक करने का स्पष्ट तरीका स्पष्ट (लेकिन गलत) विकल्प को बंद करना होगा: डेटाबेस इंजन को ठीक करें ताकि प्राप्त कोई भी क्वेरी जो मापदंडों के बजाय अपने WHERE खंड में हार्ड-कोडित मूल्यों का उपयोग करता है, एक अच्छा, वर्णनात्मक रिटर्न देता है त्रुटि संदेश आपको इसके बजाय मापदंडों का उपयोग करने का निर्देश देता है। यह स्पष्ट रूप से एक ऑप्ट-आउट विकल्प की आवश्यकता होगी ताकि प्रशासनिक उपकरणों से तदर्थ प्रश्नों जैसे सामान अभी भी आसानी से चलेंगे, लेकिन इसे डिफ़ॉल्ट रूप से सक्षम किया जाना चाहिए।

ऐसा करने से SQL इंजेक्शन ठंड बंद हो जाएगा, लगभग रात भर, लेकिन जहाँ तक मुझे पता है, कोई RDBMS वास्तव में ऐसा नहीं करता है। क्या कोई अच्छा कारण है क्यों नहीं?


22
bad_ideas_sql = 'SELECT title FROM idea WHERE idea.status == "bad" AND idea.user == :mwheeler'एक ही क्वेरी में हार्ड-कोडित और पैरामीटर किए गए मान दोनों होंगे - जो पकड़ने की कोशिश करें! मुझे लगता है कि इस तरह के मिश्रित प्रश्नों के लिए वैध उपयोग के मामले हैं।
amon

6
कैसे के बारे में आज से रिकॉर्ड का चयनSELECT * FROM jokes WHERE date > DATE_SUB(NOW(), INTERVAL 1 DAY) ORDER BY score DESC;
जेडी

10
@MasonWheeler क्षमा करें, मेरा मतलब था "अनुमति देने का प्रयास करें"। ध्यान दें कि यह पूरी तरह से मानकीकृत है और SQL इंजेक्शन से ग्रस्त नहीं है। हालाँकि, डेटाबेस ड्राइवर यह नहीं बता सकता है कि क्या शाब्दिक "bad"शाब्दिक है या स्ट्रिंग संघनन से उत्पन्न हुआ है। मेरे द्वारा देखे जाने वाले दो समाधान या तो एसक्यूएल और अन्य स्ट्रिंग-एम्बेडेड डीएसएल (हाँ कृपया) से छुटकारा पा रहे हैं, या उन भाषाओं को बढ़ावा दे रहे हैं, जहां पैरामीटर क्वेरीज़ (umm, no) का उपयोग करने की तुलना में स्ट्रिंग कॉन्सेन्टेशन अधिक कष्टप्रद है।
आमोन

4
और RDBMS यह कैसे पता लगाएगा कि क्या करना है? यह रातोंरात RDBMS को एक इंटरैक्टिव SQL प्रॉम्प्ट का उपयोग करके असंभव बना देगा ... अब आप किसी भी टूल का उपयोग करके DDL या DML कमांड दर्ज नहीं कर पाएंगे।
jwenting

8
एक अर्थ में आप ऐसा कर सकते हैं: रनटाइम पर SQL प्रश्नों का निर्माण बिल्कुल न करें, इसके बजाय एक ORM या कुछ अन्य अमूर्त परत का उपयोग करें जो आपको SQL प्रश्नों के निर्माण की आवश्यकता से बचाती है। ORM के पास आपके लिए आवश्यक सुविधाएँ नहीं हैं? तब SQL उन लोगों के लिए एक भाषा है जो SQL लिखना चाहते हैं, यही वजह है कि पूरे पर यह उन्हें SQL लिखने देता है। मौलिक मुद्दा यह है कि गतिशील रूप से उत्पन्न कोड देखने में कठिन है, लेकिन लोग इसे वैसे भी करना चाहते हैं और उन उत्पादों से असंतुष्ट होंगे जो उन्हें नहीं होने देंगे।
स्टीव जेसप

जवाबों:


45

ऐसे कई मामले हैं जहां शाब्दिक का उपयोग करना सही दृष्टिकोण है।

एक प्रदर्शन के दृष्टिकोण से, कई बार आप अपने प्रश्नों में शाब्दिक चाहते हैं। कल्पना कीजिए कि मेरे पास एक बग ट्रैकर है जहां एक बार यह प्रदर्शन के बारे में चिंता करने के लिए काफी बड़ा हो जाता है मुझे उम्मीद है कि सिस्टम में 70% कीड़े "बंद" होंगे, 20% "खुले" होंगे, 5% "सक्रिय" और 5 होंगे % कुछ अन्य स्थिति में होगा। मैं यथोचित प्रश्न करना चाहता हूं जो सभी सक्रिय बगों को लौटाता है

SELECT *
  FROM bug
 WHERE status = 'active'

statusएक बाँध चर के रूप में पारित करने के बजाय । में पारित मूल्य के आधार पर मुझे एक अलग क्वेरी योजना चाहिए status- मैं बंद बग्स को वापस करने के लिए एक टेबल स्कैन करना चाहता हूं और एक इंडेक्स स्कैन परstatusसक्रिय ऋण वापस करने के लिए कॉलम। अब, अलग-अलग डेटाबेस और अलग-अलग संस्करणों में अलग-अलग दृष्टिकोण हैं (अधिक या कम सफलतापूर्वक) एक ही क्वेरी को बाइंड चर के मूल्य के आधार पर एक अलग क्वेरी योजना का उपयोग करने की अनुमति देता है। लेकिन यह एक जटिल जटिलता का परिचय देता है जिसे एक क्वेरी को फिर से पार्स करने या एक नए बाइंड चर मान के लिए मौजूदा योजना का पुन: उपयोग करने के लिए परेशान करने के निर्णय को संतुलित करने के लिए प्रबंधित करने की आवश्यकता है। एक डेवलपर के लिए, इस जटिलता से निपटने के लिए समझदारी हो सकती है। या यह एक अलग रास्ते को मजबूर करने के लिए समझ में आ सकता है जब मुझे इस बारे में अधिक जानकारी होती है कि मेरा डेटा आशावादी की तुलना में कैसा दिखने वाला है।

एक कोड जटिलता दृष्टिकोण से, कई बार यह भी सही है कि यह एसक्यूएल बयानों में शाब्दिक अर्थ है। उदाहरण के लिए, यदि आपके पास एक zip_codeकॉलम है जिसमें 5 वर्ण ज़िप कोड है और कभी-कभी अतिरिक्त 4 अंक हैं, तो यह कुछ करने के लिए सही अर्थ बनाता है

SELECT substr( zip_code, 1, 5 ) zip,
       substr( zip_code, 7, 4 ) plus_four

सांख्यिक मूल्यों के लिए 4 अलग-अलग मापदंडों में पास होने के बजाय। ये ऐसी चीजें नहीं हैं जो कभी भी बदल जाएंगी जिससे उन्हें बांधने वाले चर केवल कोड को पढ़ने के लिए और अधिक कठिन बनाने और संभावित बनाने के लिए कार्य करते हैं कि कोई गलत क्रम में मापदंडों को बांध देगा और एक बग के साथ समाप्त हो जाएगा।


12

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

यह महत्वपूर्ण नहीं है कि एसक्यूएल स्टेटमेंट में स्ट्रिंग शाब्दिक शामिल हैं या नहीं, बल्कि यह भी है कि क्या किसी स्ट्रिंग में अविश्वासित स्रोतों के पात्रों का कोई अनुक्रम होता है , और इसके लिए मान्यता पुस्तकालय में सबसे अच्छी तरह से संभाला जाएगा जो प्रश्न बनाता है। कोड लिखने के लिए आमतौर पर C # का कोई तरीका नहीं है जो एक स्ट्रिंग शाब्दिक की अनुमति देगा लेकिन अन्य प्रकार की स्ट्रिंग अभिव्यक्ति की अनुमति नहीं देगा, लेकिन एक कोडिंग-प्रथा नियम हो सकता है जिसके लिए क्वेरी-बिल्डिंग क्लास का उपयोग करके प्रश्नों का निर्माण करना आवश्यक है स्ट्रिंग संयोजन, और क्वेरी बिल्डर को गैर-शाब्दिक स्ट्रिंग पास करने वाले किसी व्यक्ति को इस तरह की कार्रवाई को सही ठहराना होगा।


1
"यह एक शाब्दिक है" के लिए एक सन्निकटन के रूप में आप यह जांच सकते हैं कि क्या स्ट्रिंग इंटर्न है।
कोडइन्चोस

1
@CodesInChaos: यह सच है, और इस तरह का परीक्षण इस उद्देश्य के लिए पर्याप्त सटीक हो सकता है, बशर्ते कि कोई भी व्यक्ति जिसके पास रनटाइम पर एक स्ट्रिंग पैदा करने का कारण था, ने एक ऐसी विधि का इस्तेमाल किया, जो रनटाइम-जनरेटेड स्ट्रिंग को इंटर्न करने और उपयोग करने के बजाय गैर-शाब्दिक स्ट्रिंग को स्वीकार करता है। गैर-शाब्दिक-स्ट्रिंग पद्धति को एक अलग नाम देने से कोड समीक्षकों को इसके सभी उपयोगों का निरीक्षण करने में आसानी होगी।
सुपरकैट

ध्यान दें कि जबकि C # में ऐसा करने का कोई तरीका नहीं है, कुछ अन्य भाषाओं में ऐसी सुविधाएं हैं जो इसे संभव बनाती हैं (उदाहरण के लिए पर्ल के दागदार स्ट्रिंग मॉड्यूल)।
जूल्स

अधिक संक्षेप में, यह एक क्लाइंट समस्या है, न कि सर्वर समस्या।
Blrfl

7
SELECT count(ID)
FROM posts
WHERE deleted = false

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

अब आप कह सकते हैं कि आप enums के लिए एक अपवाद जोड़ देंगे, लेकिन यह बस छेद को फिर से खोलता है (हालांकि छोटा है)। लोगों का उल्लेख नहीं करने के लिए सबसे पहले उन्हें शिक्षित करने की आवश्यकता है varchars

इंजेक्शन की वास्तविक समस्या वैकल्पिक रूप से क्वेरी स्ट्रिंग का निर्माण है। इसके लिए समाधान एक संग्रहीत प्रक्रिया तंत्र है और इसके उपयोग या अनुमत प्रश्नों के श्वेतसूची को लागू करना है।


2
यदि आपके समाधान के लिए "यह सब भूल जाना बहुत आसान है - या पहली जगह में पता नहीं है - पैराड्राइज्ड प्रश्नों का उपयोग करने के लिए" "हर किसी को याद रखें - और पहली जगह में जानें - संग्रहीत सहारा का उपयोग करने के लिए", तो आप 'सवाल का पूरा बिंदु याद आ रहा है।
मेसन व्हीलर

5
मैंने अपने काम में संग्रहीत प्रक्रियाओं के माध्यम से एसक्यूएल इंजेक्शन देखा है। यह सब कुछ बीएडी के लिए संग्रहीत प्रक्रियाओं को अनिवार्य करता है। वहाँ हमेशा 0.5% है कि सच गतिशील प्रश्न हैं (आप एक पूरे जहां खंड, किसी भी तालिका में शामिल होने के लिए अकेले नहीं कर सकते हैं)।
जोशुआ

इस जवाब आप बदल सकते में उदाहरण में deleted = falseसे NOT deletedहै, जो शाब्दिक बचा जाता है। लेकिन बिंदु सामान्य रूप से मान्य है।
psmears

5

TL; DR : आपको केवल खंडों में ही नहीं, सभी शाब्दिकों को प्रतिबंधित करना होगा WHERE। जिन कारणों से वे नहीं करते हैं, यह डेटाबेस को अन्य प्रणालियों से अलग रहने की अनुमति देता है।

सबसे पहले, आपका आधार त्रुटिपूर्ण है। आप केवल WHEREक्लॉस को प्रतिबंधित करना चाहते हैं, लेकिन यह एकमात्र स्थान नहीं है जहां उपयोगकर्ता इनपुट जा सकता है। उदाहरण के लिए,

SELECT
    COUNT(CASE WHEN item_type = 'blender' THEN 1 END) as type1_count,
    COUNT(CASE WHEN item_type = 'television' THEN 1 END) AS type2_count)
FROM item

यह SQL इंजेक्शन के लिए समान रूप से कमजोर है:

SELECT
    COUNT(CASE WHEN item_type = 'blender' THEN 1 END) FROM item; DROP TABLE user_info; SELECT CASE(WHEN item_type = 'blender' THEN 1 END) as type1_count,
    COUNT(CASE WHEN item_type = 'television' THEN 1 END) AS type2_count)
FROM item

इसलिए आप केवल WHEREखंड में शाब्दिक को प्रतिबंधित नहीं कर सकते । आपको सभी शाब्दिक को प्रतिबंधित करना होगा

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

एक आम उदाहरण के रूप में, लिस्टल्स अक्सर लिस्ट-ऑफ-वैल्यू / लुक-अप टेबल की आबादी में उपयोग किए जाते हैं:

CREATE TABLE user_roles (role_id INTEGER, role_name VARCHAR(50));
INSERT INTO user_roles (1, 'normal');
INSERT INTO user_roles (2, 'admin');
INSERT INTO user_roles (3, 'banned');

उनके बिना, आपको इस तालिका को आबाद करने के लिए किसी अन्य प्रोग्रामिंग भाषा में कोड लिखना होगा । SQL में सीधे ऐसा करने की क्षमता मूल्यवान है

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

SELECT $lit1$I'm a literal$lit1$||$lit2$I'm another literal $$ with nested string delimiters$$ $lit2$||'I''m ANOTHER literal'||$$I'm the last literal$$;

ऐसा करने की कोशिश करना एक रखरखाव दुःस्वप्न होगा, खासकर जब से वैध सिंटैक्स अक्सर डेटाबेस के प्रमुख रिलीज के बीच बदल जाता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.