क्या htmlspecialchars और mysql_real_escape_string मेरे PHP कोड को इंजेक्शन से सुरक्षित रखते हैं?


116

इससे पहले आज वेब ऐप्स में इनपुट सत्यापन रणनीतियों के बारे में एक सवाल पूछा गया था ।

लेखन के समय शीर्ष उत्तर, PHPकेवल उपयोग करने में htmlspecialcharsऔर सुझाव देता है mysql_real_escape_string

मेरा सवाल है: क्या यह हमेशा पर्याप्त है? क्या और भी बहुत कुछ हमें पता होना चाहिए? ये कार्य कहां-कहां टूटते हैं?

जवाबों:


241

जब डेटाबेस प्रश्नों की बात आती है, तो हमेशा तैयार किए गए प्रश्नों का उपयोग करें। mysqliऔर PDOपुस्तकालयों इस समर्थन करते हैं। यह इस तरह के भागने के कार्यों का उपयोग करने से अधिक सुरक्षित है mysql_real_escape_string

हाँ, mysql_real_escape_stringप्रभावी रूप से सिर्फ एक स्ट्रिंग भागने का कार्य है। यह कोई जादू की गोली नहीं है। यह सब करना खतरनाक पात्रों से बच जाएगा ताकि वे एक ही क्वेरी स्ट्रिंग में उपयोग करने के लिए सुरक्षित हो सकें। हालांकि, यदि आप पहले से अपने इनपुट्स को सैनिटाइज नहीं करते हैं, तो आप कुछ अटैक वैक्टर के लिए कमजोर होंगे।

निम्नलिखित एसक्यूएल की कल्पना करें:

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);

आपको यह देखने में सक्षम होना चाहिए कि यह शोषण की चपेट में है।
कल्पना कीजिए कि idपैरामीटर में आम अटैक वेक्टर है:

1 OR 1=1

सांकेतिक शब्दों में बदलना करने के लिए कोई जोखिम भरा चार्ट नहीं है, इसलिए यह सीधे भागने वाले फिल्टर से होकर गुजरेगा। हमें छोड़कर:

SELECT fields FROM table WHERE id= 1 OR 1=1

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

1 or is_admin=1 order by id limit 1

जो पैदा करता है

SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1

जो हमलावर को इस पूरी तरह से काल्पनिक उदाहरण में पहले प्रशासक के विवरण को वापस करने की अनुमति देता है।

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

के लिए के रूप में htmlspecialchars()। वह अपनी खुद की एक खान है।

PHP में एक वास्तविक समस्या यह है कि इसमें अलग-अलग HTML से संबंधित भागने वाले कार्यों का एक पूरा चयन है, और वास्तव में कोई स्पष्ट मार्गदर्शन नहीं है कि क्या कार्य करता है।

सबसे पहले, यदि आप एक HTML टैग के अंदर हैं, तो आप वास्तविक परेशानी में हैं। की ओर देखें

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';

हम पहले से ही एक HTML टैग के अंदर हैं, इसलिए हमें कुछ भी खतरनाक करने के लिए <या> की आवश्यकता नहीं है। हमारा हमला वेक्टर सिर्फ हो सकता हैjavascript:alert(document.cookie)

अब परिणामी HTML जैसा दिखता है

<img src= "javascript:alert(document.cookie)" />

हमले के माध्यम से सीधे हो जाता है।

ये और ख़राब हो जाता है। क्यों? क्योंकि htmlspecialchars(जब इस तरह कहा जाता है) केवल दोहरे उद्धरण चिह्नों और एकल नहीं को कूटबद्ध करता है। तो अगर हमारे पास था

echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";

हमारा दुष्ट हमलावर अब पूरे नए मापदंडों को इंजेक्ट कर सकता है

pic.png' onclick='location.href=xxx' onmouseover='...

हमें देता है

<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />

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

यहां तक ​​कि अगर आप htmlspecialchars($string)HTML टैग के बाहर का उपयोग करते हैं, तो भी आप मल्टी-बाइट चारसेट अटैक वैक्टर की चपेट में हैं।

सबसे प्रभावी आप mb_convert_encoding और htmlentities के संयोजन का उपयोग इस प्रकार कर सकते हैं।

$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');

यहां तक ​​कि यह IE6 को कमजोर बनाता है, क्योंकि यह जिस तरह से UTF को संभालता है। हालाँकि, आप IE6 उपयोग बंद होने तक ISO-8859-1 जैसे अधिक सीमित एन्कोडिंग पर वापस गिर सकते हैं।

मल्टीबीट समस्याओं के लिए अधिक गहराई से अध्ययन के लिए, https://stackoverflow.com/a/12118602/1820 देखें


24
यहाँ केवल एक ही चीज़ चूक गई, वह यह है कि DB क्वेरी के लिए पहला उदाहरण ... एक सरल इंटेवल () इंजेक्शन को हल करेगा। हमेशा mysqlescape के स्थान पर intval () का उपयोग करें ... (जब नंबर की आवश्यकता हो और स्ट्रिंग की न हो।
रॉबर्ट के

11
और याद रखें कि पैरामीटर प्रश्नों का उपयोग करने से आपको डेटा को हमेशा डेटा माना जाएगा और कोड नहीं। एक पुस्तकालय जैसे पीडीओ का उपयोग करें और जब भी संभव हो पैरामीटर किए गए प्रश्नों का उपयोग करें।
Cheekysoft

9
दो टिप्पणियां: 1. पहले उदाहरण में, आप सुरक्षित होंगे यदि आप पैरामीटर के आसपास उद्धरण भी डालते हैं, जैसे $result = "SELECT fields FROM table WHERE id = '".mysql_real_escape_string($_POST['id'])."'";2. दूसरे मामले में (URL युक्त विशेषता), इसका कोई फायदा नहीं है htmlspecialchars; इन मामलों में, आपको URL एन्कोडिंग योजना, जैसे, का उपयोग करके इनपुट को सांकेतिक शब्दों में बदलना चाहिए rawurlencode। इस तरह, एक उपयोगकर्ता javascript:एट अल नहीं डाल सकता है ।
मार्सेल कोर्पल

7
"Htmlspecialchars केवल दोहरे उद्धरण चिह्नों और एकल नहीं को एन्कोड करता है": यह सच नहीं है, यह सेट किए जा रहे झंडे पर निर्भर करता है, इसके मापदंडों को देखें ।
मार्सेल कोर्पेल

2
यह बोल्ड किया जाना चाहिए: Take a whitelist approach and only let through the chars which are good.एक ब्लैकलिस्ट हमेशा कुछ याद करेगा। +1
जो स्मो

10

चेकिसॉफ्ट के उत्कृष्ट उत्तर के अलावा:

  • हां, वे आपको सुरक्षित रखेंगे, लेकिन केवल तभी जब वे बिल्कुल सही तरीके से उपयोग किए जाएं। उन्हें गलत तरीके से उपयोग करें और आप अभी भी कमजोर होंगे, और अन्य समस्याएं हो सकती हैं (उदाहरण के लिए डेटा भ्रष्टाचार)
  • कृपया इसके बजाय पैरामीटर किए गए प्रश्नों का उपयोग करें (जैसा कि ऊपर कहा गया है)। आप उन्हें उदाहरण के लिए PDO के माध्यम से या PEAR DB जैसे आवरण के माध्यम से उपयोग कर सकते हैं
  • सुनिश्चित करें कि Magic_quotes_gpc और Magic_quotes_runtime हर समय बंद रहते हैं, और कभी गलती से चालू नहीं होते, संक्षेप में भी नहीं। ये PHP के डेवलपर्स द्वारा सुरक्षा समस्याओं (जो डेटा को नष्ट कर देता है) को रोकने के लिए एक प्रारंभिक और गहरा पथभ्रष्ट प्रयास है

HTML इंजेक्शन (जैसे क्रॉस साइट स्क्रिप्टिंग) को रोकने के लिए वास्तव में एक चांदी की गोली नहीं है, लेकिन यदि आप HTML का उत्पादन करने के लिए लाइब्रेरी या टेम्प्लेटिंग सिस्टम का उपयोग कर रहे हैं तो आप इसे आसानी से प्राप्त कर सकते हैं। इसके लिए प्रलेखन पढ़ें कि कैसे चीजों को उचित तरीके से बचाना है।

HTML में, संदर्भ के आधार पर चीजों को अलग-अलग तरीके से बचाना होगा। यह विशेष रूप से स्ट्रैस को जावास्क्रिप्ट में रखा जा रहा है।


3

मैं उपरोक्त पोस्टों से निश्चित रूप से सहमत हूँ, लेकिन मेरे पास चेकिसॉफ्ट के उत्तर में जोड़ने के लिए एक छोटी सी बात है, विशेष रूप से:

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

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

निम्नलिखित एसक्यूएल की कल्पना करें:

$ परिणाम = "तालिका से फ़ील्ड का चयन करें जहां आईडी =" .mysql_real_escape_string ($ _ POST ['आईडी']);

आपको यह देखने में सक्षम होना चाहिए कि यह शोषण की चपेट में है। आईडी पैरामीटर की कल्पना करें जिसमें आम अटैक वेक्टर हो:

1 या 1 = 1

सांकेतिक शब्दों में बदलना करने के लिए कोई जोखिम भरा चार्ट नहीं है, इसलिए यह सीधे भागने वाले फिल्टर से होकर गुजरेगा। हमें छोड़कर:

तालिका से फ़ील्ड का चयन करें जहां आईडी = 1 या 1 = 1 है

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

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

इसलिए उपयोग करने के बजाय

$ परिणाम = "तालिका से फ़ील्ड का चयन करें जहां आईडी =" .mysqlrealescapestestring ("1 या 1 = 1");

मै इस्तेमाल करूंगा

$ परिणाम = "तालिका से फ़ील्ड का चयन करें जहां आईडी ="। नट ("1 या 1 = 1");

और यह सुरक्षित रूप से क्वेरी चलाएगा

तालिका से फ़ील्ड का चयन करें जहां आईडी = 111 है

ज़रूर, कि बस इसे सही पंक्ति प्रदर्शित करने से रोका जाए, लेकिन मुझे नहीं लगता कि जो कोई भी आपकी साइट में sql को इंजेक्ट करने की कोशिश कर रहा है, उसके लिए यह एक बड़ा मुद्दा है;)


1
उत्तम! यह ठीक उसी तरह का sanitisation है जिसकी आपको जरूरत है। प्रारंभिक कोड विफल हो गया क्योंकि यह मान्य नहीं था कि एक संख्या संख्यात्मक थी। आपका कोड ऐसा करता है। आपको उन सभी पूर्णांक-उपयोग वाले संस्करणों पर नंबर () कॉल करना चाहिए जिनके मूल्य कोडबेस के बाहर से उत्पन्न होते हैं।
Cheekysoft

1
यह ध्यान देने योग्य है कि intval () इसके लिए पूरी तरह से ठीक काम करेगा, क्योंकि PHP स्वतः ही आपके लिए पूर्णांक को पूर्णांक बनाता है।
एडम अर्नस्ट

11
मैं इंटल पसंद करता हूं। यह 1abc2 से 1 तक जाता है, 12. नहीं
jmucchiello

1
पहचान बेहतर है, पूरी तरह से आईडी पर। अधिकांश समय, यदि यह दूषित हो गया है, तो इसका ठीक ऊपर, 1 या 1 = 1 है। आपको वास्तव में अन्य लोगों की आईडी लीक नहीं करनी चाहिए। इसलिए इंटेवल सही आईडी लौटाएगा। उसके बाद, आपको जांचना चाहिए कि क्या मूल और साफ किए गए मान समान हैं। यह न केवल हमलों को रोक रहा है, बल्कि हमलावरों को ढूंढ रहा है।
26

2
गलत पंक्ति विनाशकारी होगी यदि आप व्यक्तिगत डेटा दिखा रहे हैं, तो आप किसी अन्य उपयोगकर्ता की जानकारी देखेंगे! इसके बजाय जाँच करना बेहतर होगाreturn preg_match('/^[0-9]+$/',$input) ? $input : 0;
फ्रैंक फोर्ट

2

इस पहेली का एक महत्वपूर्ण हिस्सा संदर्भ है। आईडी के रूप में "1 या 1 = 1" भेजने वाला कोई व्यक्ति समस्या नहीं है यदि आप अपनी क्वेरी में हर तर्क को उद्धृत करते हैं:

SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"

जिसके परिणामस्वरूप:

SELECT fields FROM table WHERE id='1 OR 1=1'

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


15
और फिर मैं मल्टी-बाइट चार 0xbf27 के साथ अपने हमले के वेक्टर को शुरू करूंगा जो कि आपके लैटिन 1 डेटाबेस में फ़िल्टर fuction द्वारा 0xbf5c27 के रूप में परिवर्तित किया जाएगा - जो कि एक एकल मल्टीबाइट चरित्र है जो एक एकल उद्धरण द्वारा पीछा किया जाता है।
Cheekysoft

8
एक भी ज्ञात हमले-वेक्टर के खिलाफ सुरक्षा की कोशिश न करें। जब तक आप पैच को अपने कोड पर लागू नहीं करते तब तक आप अपनी पूंछ का पीछा करते रहेंगे। पीछे खड़े होकर सामान्य मामलों को देखना सुरक्षित कोड और एक बेहतर सुरक्षा-केंद्रित मानसिकता को लीक कर देगा।
Cheekysoft

मैं सहमत हूँ; आदर्श रूप से, ओपी तैयार बयानों का उपयोग करेगा।
लुकास ओमान

1
हालांकि इस पोस्ट द्वारा सुझाए गए तर्कों का उद्धरण मूर्खतापूर्ण नहीं है लेकिन यह आम 1 या 1 = 1 प्रकार के कई हमलों को कम करेगा, इसलिए यह उल्लेख के योग्य है।
नाइट उल्लू

2
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

64 बिट सिस्टम पर भी बेहतर काम करता है। हालांकि बड़ी संख्या को संबोधित करने पर अपने सिस्टम की सीमाओं से सावधान रहें, लेकिन डेटाबेस आईडी के लिए यह समय के 99% महान काम करता है।

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


-3

क्यों, ओह क्यों, क्या आप अपने sql स्टेटमेंट में यूजर इनपुट के आसपास उद्धरण शामिल नहीं करेंगे ? काफी मूर्खतापूर्ण लगता है कि नहीं! अपने sql स्टेटमेंट में उद्धरण सहित "1 या 1 = 1" एक निरर्थक प्रयास होगा, नहीं?

तो अब, आप कहेंगे, "क्या होगा यदि उपयोगकर्ता इनपुट में एक उद्धरण (या दोहरे उद्धरण) शामिल है?"

ठीक है, उस के लिए आसान तय: बस उपयोगकर्ता input'd उद्धरण हटा दें। उदाहरण के लिए: input =~ s/'//g;। अब, यह मुझे वैसे भी लगता है, कि उपयोगकर्ता इनपुट सुरक्षित होगा ...


"क्यों, ओह क्यों, क्या आप अपने sql स्टेटमेंट में उपयोगकर्ता इनपुट के आसपास उद्धरण शामिल नहीं करेंगे?" - सवाल उपयोगकर्ता इनपुट का हवाला नहीं देने के बारे में कुछ नहीं कहता है।
क्वेंटिन

1
"ठीक है, उस के लिए आसान तय" - उस के लिए भयानक तय। जो डेटा को फेंक देता है। प्रश्न में वर्णित समाधान ही बेहतर दृष्टिकोण है।
क्वेंटिन

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

"जबकि मैं मानता हूं कि प्रश्न उपयोगकर्ता इनपुट को उद्धृत नहीं करता है, यह अभी भी लगता है कि इनपुट को उद्धृत नहीं करना है।" - नहीं, यह नहीं है। प्रश्न इसे एक तरह से या दूसरे तरीके से प्रदर्शित नहीं करता है।
क्वेंटिन

1
@JarettL या तो तैयार बयानों का उपयोग करने की आदत डालें या हर मंगलवार को आपके डेटा को नष्ट करने वाले बॉबी टेबल्स की आदत डालें । Parameterized SQL अपने आप को SQL injection से बचाने का सबसे अच्छा तरीका है। यदि आप तैयार कथन का उपयोग कर रहे हैं तो आपको "SQL इंजेक्शन जाँच" करने की आवश्यकता नहीं है। वे लागू करने के लिए बेहद आसान हैं (और मेरी राय में, कोड को पढ़ने में आसान बनाते हैं), स्ट्रिंग संघनन और एसक्यूएल इंजेक्शन की विभिन्न विभेदकों से रक्षा करें, और सभी के सर्वश्रेष्ठ, आपको इसे लागू करने के लिए पहिया को सुदृढ़ करने की आवश्यकता नहीं है। ।
सियुएल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.