कैसे तैयार किए गए बयान SQL इंजेक्शन के हमलों से बचा सकते हैं?


172

कैसे तैयार बयान हमें SQL इंजेक्शन के हमलों को रोकने में मदद करते हैं ?

विकिपीडिया कहता है:

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

मैं इसका कारण बहुत अच्छी तरह नहीं देख सकता। आसान अंग्रेजी और कुछ उदाहरणों में एक सरल व्याख्या क्या होगी?

जवाबों:


290

विचार बहुत सरल है - क्वेरी और डेटा को अलग से डेटाबेस सर्वर पर भेजा जाता है ।
बस इतना ही।

SQL इंजेक्शन समस्या की जड़ कोड और डेटा के मिश्रण में है।

वास्तव में, हमारी SQL क्वेरी एक वैध कार्यक्रम है । और हम इस तरह के एक कार्यक्रम को गतिशील रूप से बना रहे हैं, मक्खी पर कुछ डेटा जोड़ रहे हैं। इस प्रकार, डेटा प्रोग्राम कोड में हस्तक्षेप कर सकता है और यहां तक ​​कि इसे बदल भी सकता है, क्योंकि हर SQL इंजेक्शन उदाहरण इसे दिखाता है (सभी उदाहरण PHP / मैसूर में):

$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

एक नियमित क्वेरी का उत्पादन करेगा

SELECT * FROM users where id=1

इस कोड के दौरान

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

एक दुर्भावनापूर्ण अनुक्रम का उत्पादन करेगा

SELECT * FROM users where id=1; DROP TABLE users;

यह काम करता है क्योंकि हम डेटा को सीधे प्रोग्राम बॉडी में जोड़ रहे हैं और यह प्रोग्राम का एक हिस्सा बन जाता है, इसलिए डेटा प्रोग्राम को बदल सकता है, और पास किए गए डेटा के आधार पर, हम या तो एक नियमित आउटपुट या एक टेबल usersहटा देंगे ।

जबकि तैयार बयानों के मामले में हम अपने कार्यक्रम में बदलाव नहीं करते हैं, यह बरकरार है
यही बात है।

हम पहले सर्वर को एक प्रोग्राम भेज रहे हैं

$db->prepare("SELECT * FROM users where id=?");

जहां डेटा को पैरामीटर या प्लेसहोल्डर नामक कुछ चर द्वारा प्रतिस्थापित किया जाता है।

ध्यान दें कि बिल्कुल उसी क्वेरी को सर्वर में भेजा जाता है, जिसमें कोई डेटा न हो! और फिर हम दूसरे अनुरोध के साथ डेटा भेज रहे हैं, अनिवार्य रूप से क्वेरी से ही अलग हो गए हैं:

$db->execute($data);

इसलिए यह हमारे कार्यक्रम को बदल नहीं सकता है और कोई नुकसान नहीं पहुंचा सकता है।
काफी आसान है - यह नहीं है?

केवल एक चीज मुझे जोड़ना है जो हमेशा हर मैनुअल में छोड़ी जाती है:

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


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

3
@ zaq178m मियामी: "पीडीओ केवल उन ड्राइवरों के लिए तैयार किए गए बयानों का अनुकरण करता है जो सुविधा का समर्थन नहीं करते हैं" - बिल्कुल सच नहीं है। MySQL ने काफी समय से तैयार बयानों का समर्थन किया है। पीडीओ चालक के पास भी है। लेकिन फिर भी, MySQL के प्रश्नों को पीडीओ द्वारा डिफ़ॉल्ट रूप से तैयार किया गया था, पिछली बार जब मैंने जाँच की थी।
cHao

9
की तुलना में $spoiled_data = "1; DROP TABLE users;"-> के बीच क्या अंतर $query = "SELECT * FROM users where id=$spoiled_data";है: $db->prepare("SELECT * FROM users where id=?");-> $data = "1; DROP TABLE users;"-> $db->execute($data);। क्या वे एक ही काम नहीं करेंगे?
जूहा अनटाइनन

14
@ जूहा अन्टिनन डेटा कुछ भी हो सकता है। यह डेटा को पार्स नहीं करेगा। यह DATA नहीं है। इसलिए यदि $ डेटा में sql कमांड शामिल हैं, तो भी इसे निष्पादित नहीं किया जाएगा। इसके अलावा, अगर आईडी एक संख्या है, तो स्ट्रिंग सामग्री एक रिपोर्ट या मूल्य शून्य उत्पन्न करेगी।
सॉली

21

यहाँ एक उदाहरण स्थापित करने के लिए SQL है:

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);

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

import java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

इसे चलाना, पहला मामला सामान्य उपयोग के साथ है, और दूसरा दुर्भावनापूर्ण इंजेक्शन के साथ है:

c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

आपको उपयोगकर्ता इनपुट के स्ट्रिंग संघनन के साथ अपने SQL बयानों का निर्माण नहीं करना चाहिए। यह न केवल इंजेक्शन के लिए कमजोर है, बल्कि इसके सर्वर पर निहितार्थ भी हैं (बयान बदलता है, इसलिए एसक्यूएल स्टेटमेंट कैश हिट होने की संभावना कम होती है जबकि बाइंड उदाहरण हमेशा एक ही कथन चल रहा है)।

यहाँ इस तरह के इंजेक्शन से बचने के लिए बाइंडिंग का एक उदाहरण दिया गया है:

import java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

पिछले उदाहरण के समान इनपुट के साथ इसे चलाने से पता चलता है कि दुर्भावनापूर्ण कोड काम नहीं करता है क्योंकि उस स्ट्रिंग से मेल खाता कोई भुगतान टाइप नहीं है:

c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?

डेटाबेस से कनेक्ट होने वाले प्रोग्राम से तैयार स्टेटमेंट का उपयोग करने का वही प्रभाव है जो डीबी के हिस्से के तैयार किए गए स्टेटमेंट का उपयोग करने का है? उदाहरण के लिए Postgres के पास स्वयं का तैयार किया गया स्टेटमेंट है और क्या यह SQL इंजेक्शन को रोकने का उपयोग करेगा? postgresql.org/docs/9.2/static/sql-prepare.html
Celeritas

@Celeritas Postgresql के लिए मेरे पास कोई निश्चित उत्तर नहीं है। डॉक्स को देखते हुए, ऐसा लगता है कि प्रभाव समान है। PREPAREएक निश्चित नाम का स्टेटमेंट बनाता है जिसे पहले से ही पार्स किया गया है (यानी स्टेटमेंट इनपुट की परवाह किए बिना किसी भी तरह से बदलने वाला नहीं है) जबकि EXECUTEमापदंडों को बांधने वाले नामित स्टेटमेंट को चलाएगा। चूंकि PREPAREकेवल सत्र की अवधि है, यह वास्तव में ऐसा लगता है कि यह प्रदर्शन कारणों के लिए है, न कि psql स्क्रिप्ट के माध्यम से इंजेक्शन को रोकने के लिए। Psql पहुंच के लिए, संग्रहीत कार्यविधियों को अनुमति दे सकता है और पैरामीटर के भीतर मापदंडों को बाँध सकता है।
ग्लेन

@Celeritas मैंने x86_64 पर PostgreSQL 11.1 का उपयोग करके उपरोक्त कोड की कोशिश की और SQLi उदाहरण के ऊपर काम किया।
कृष्ण पांडेय

15

मूल रूप से, तैयार किए गए बयानों के साथ एक संभावित हैकर से आने वाले डेटा को डेटा के रूप में माना जाता है - और इसका कोई तरीका नहीं है कि इसे आपके एप्लिकेशन SQL के साथ इंटरमिक्स किया जा सकता है और / या SQL के रूप में व्याख्या की जा सकती है (जो तब हो सकता है जब डेटा सीधे आपके में रखा जाता है। अनुप्रयोग SQL)।

इसका कारण यह है कि पहले से तैयार किए गए कथन "क्विक क्वेरी" को एक कुशल क्वेरी प्लान खोजने के लिए तैयार करते हैं, और वास्तविक मानों को भेजें जो संभवतः बाद में एक फॉर्म से आते हैं - उस समय क्वेरी वास्तव में निष्पादित होती है।

अधिक महान जानकारी यहाँ:

तैयार बयान और एसक्यूएल इंजेक्शन


6

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

Naive दृष्टिकोण

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

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

उदाहरण के लिए, दुर्भावनापूर्ण उपयोगकर्ता इनपुट के SQLStringबराबर हो सकता है"SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'

दुर्भावनापूर्ण उपयोगकर्ता के कारण, SQLString2 कथन शामिल हैं, जहां 2 वें ( "DROP TABLE CUSTOMERS") नुकसान पहुंचाएगा।

तैयार विवरण

इस स्थिति में, क्वेरी और डेटा के अलग होने के कारण, उपयोगकर्ता इनपुट को कभी भी SQL कथन के रूप में नहीं माना जाता है, और इस प्रकार कभी भी निष्पादित नहीं किया जाता है । यह इस कारण से है, कि किसी भी दुर्भावनापूर्ण SQL कोड को इंजेक्ट करने से कोई नुकसान नहीं होगा। इसलिए "DROP TABLE CUSTOMERS"उपरोक्त मामले में कभी भी निष्पादित नहीं किया जाएगा।

संक्षेप में, तैयार किए गए बयानों के साथ उपयोगकर्ता इनपुट के माध्यम से पेश किए गए दुर्भावनापूर्ण कोड को निष्पादित नहीं किया जाएगा!


वास्तव में? स्वीकृत उत्तर वास्तव में ऐसा नहीं बताता है?
आपका कॉमन सेंस

@ आपका कॉमन सेंस स्वीकृत उत्तर बहुत सारी बहुमूल्य जानकारी से भरा है, लेकिन इसने मुझे आश्चर्यचकित कर दिया कि डेटा और क्वेरी के पृथक्करण का कार्यान्वयन विवरण क्या है। जबकि इस बात पर ध्यान केंद्रित किया जाता है कि दुर्भावनापूर्ण रूप से इंजेक्ट किया गया डेटा (यदि कोई था) तो कभी भी सिर पर कील नहीं मारा जाएगा।
22. 22 बजे N.Vegeta

और कौन से "कार्यान्वयन विवरण" आपके उत्तर में दिए गए हैं जो वहां मौजूद नहीं हैं?
आपका कॉमन सेंस

यदि आप यह देखने का प्रयास करते हैं कि मैं कहाँ से आ रहा हूँ, तो आपको पता चलेगा कि मेरी बात इस प्रकार है: कार्यान्वयन के विवरण को देखने की संक्षिप्त इच्छा से स्पष्ट कारण समझने की आवश्यकता है कि दुर्भावनापूर्ण उपयोगकर्ता इनपुट किसी भी कारण से नहीं होगा नुकसान। कार्यान्वयन विवरण देखने की इतनी आवश्यकता नहीं है। यही कारण है कि कार्यान्वयन विवरण इस तरह थे कि, किसी भी बिंदु पर दुर्भावनापूर्ण रूप से एसक्यूएल में प्रवेश नहीं किया जाएगा, घर संदेश भेजा। आपकी प्रतिक्रिया इस सवाल का जवाब देती है कि कैसे (जैसा कि अनुरोध किया गया है?), लेकिन मैं अन्य लोगों (मेरी तरह) की कल्पना करूंगा कि वह एक आत्मघाती प्रतिक्रिया से संतुष्ट क्यों होंगे?
N.Vegeta

इस संवर्धन पर विचार करें जो क्रूक्स को स्पष्ट करता है, न कि एक निहित आलोचना के रूप में (पुनरावृत्ति ने महसूस किया कि स्वीकृत प्रतिक्रिया के लेखक कौन थे)।
N.Vegeta

5

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

आप बाद में अपने डेटा को क्वेरी से जोड़ते हैं जैसे कि DBMS निष्पादन के लिए क्वेरी पैरामीटर (पैरामीटराइजेशन) के रूप में उस डेटा का उपयोग करता है। DBMS आपके द्वारा पहले से संकलित SQL क्वेरी के पूरक के रूप में बाइंड डेटा का उपयोग नहीं करता है; यह केवल डेटा है।

इसका मतलब यह है कि तैयार किए गए बयानों का उपयोग करके SQL इंजेक्शन प्रदर्शन करना मौलिक रूप से असंभव है। तैयार कथनों की बहुत प्रकृति और DBMS के साथ उनका संबंध इसे रोकता है।


4

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

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

अब यदि inoutusername वैरिएबल का मान कुछ 'या 1 = 1 - जैसा है, तो यह क्वेरी अब बन जाती है:

select * from table where username='a' or 1=1 -- and password=asda

और बाकी के बाद टिप्पणी की जाती है --, इसलिए इसे कभी भी निष्पादित नहीं किया जाता है और नीचे दिए गए बयान के उदाहरण का उपयोग करके बाईपास किया जाता है।

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

इसलिए प्रभाव में आप एक और पैरामीटर नहीं भेज सकते हैं, इस प्रकार एसक्यूएल इंजेक्शन से बचना ...


3

प्रमुख वाक्यांश है need not be correctly escaped। इसका मतलब है कि आप डैश, एपोस्ट्रोफ, उद्धरण, आदि में फेंकने की कोशिश कर रहे लोगों के बारे में चिंता करने की ज़रूरत नहीं है ...

यह सब आपके लिए है।


2
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

चलो मान लेते हैं कि आपके पास सर्वलेट में है। यदि किसी पुरुषवादी व्यक्ति ने 'फ़िल्टर' के लिए एक खराब मान पारित किया है तो आप अपना डेटाबेस हैक कर सकते हैं।


0

मूल कारण # 1 - Delimiter समस्या

Sql इंजेक्शन संभव है क्योंकि हम उद्धरण चिह्नों का उपयोग करके तारों का परिसीमन करते हैं और तारों के कुछ हिस्सों का भी उपयोग करते हैं, जिससे उन्हें कभी-कभी व्याख्या करना असंभव हो जाता है। यदि हमारे पास ऐसे सीमांकक होते हैं जिनका उपयोग स्ट्रिंग डेटा में नहीं किया जा सकता है, तो sql इंजेक्शन कभी नहीं होता। सीमांत समस्या को हल करने से एसक्यूएल इंजेक्शन समस्या समाप्त हो जाती है। संरचना प्रश्न ऐसा करते हैं।

मूल कारण # 2 - मानव प्रकृति, लोग चालाक हैं और कुछ चालाक लोग दुर्भावनापूर्ण हैं और सभी लोग गलतियाँ करते हैं

Sql इंजेक्शन का दूसरा मूल कारण मानव प्रकृति है। प्रोग्रामर सहित लोग गलतियां करते हैं। जब आप एक संरचित क्वेरी पर गलती करते हैं, तो यह आपके सिस्टम को sql इंजेक्शन के लिए असुरक्षित नहीं बनाता है। यदि आप संरचित प्रश्नों का उपयोग नहीं कर रहे हैं, तो गलतियाँ sql इंजेक्शन भेद्यता उत्पन्न कर सकती हैं।

कैसे संरचित क्वेरीज़ SQL इंजेक्शन के रूट कारणों का समाधान करती हैं

संरचित क्वेरी एक कथन में sql कमांड डालकर और डेटा को एक अलग प्रोग्रामिंग स्टेटमेंट में डालकर, Delimiter समस्या को हल करती है। प्रोग्रामिंग स्टेटमेंट आवश्यक अलगाव बनाते हैं।

संरचित प्रश्न महत्वपूर्ण सुरक्षा छेद बनाने से मानवीय त्रुटि को रोकने में मदद करते हैं। मानव द्वारा गलतियाँ करने के संबंध में, संरचना संबंधी प्रश्नों का उपयोग करने पर sql इंजेक्शन नहीं हो सकता है। एसक्यूएल इंजेक्शन को रोकने के तरीके हैं जो संरचित प्रश्नों को शामिल नहीं करते हैं, लेकिन उस दृष्टिकोण में सामान्य मानवीय त्रुटि आमतौर पर एसक्यूएल इंजेक्शन के लिए कम से कम कुछ जोखिम की ओर जाता है। एसक्यूएल इंजेक्शन से संरचित क्वेरी सुरक्षित नहीं हैं। आप दुनिया में सभी गलतियों को लगभग संरचित प्रश्नों के साथ, किसी भी अन्य प्रोग्रामिंग के समान कर सकते हैं, लेकिन आप जो भी बना सकते हैं, वह एसक्यूएल इंजेक्शन द्वारा उठाए गए ssstem में नहीं बदला जा सकता है। यही कारण है कि लोग यह कहना पसंद करते हैं कि यह सही तरीका है ताकि एसक्यूएल इंजेक्शन को रोका जा सके।

इसलिए, आपके पास यह है, sql इंजेक्शन के कारण और प्रकृति संरचित प्रश्न जो उन्हें उपयोग किए जाने पर असंभव बनाते हैं।

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