PDO MySQL: PDO :: ATTR_EMULATE_PREPARES का उपयोग करें या नहीं?


117

यह मैंने अब तक के बारे में पढ़ा है PDO::ATTR_EMULATE_PREPARES:

  1. PDO की तैयारी अनुकरण प्रदर्शन के लिए बेहतर है क्योंकि MySQL की मूल तैयारी क्वेरी कैश को बायपास करती है
  2. सुरक्षा के लिए MySQL की मूल तैयारी बेहतर है (SQL इंजेक्शन को रोकना)
  3. त्रुटि रिपोर्टिंग के लिए MySQL की मूल तैयारी बेहतर है

मुझे नहीं पता कि इनमें से कोई भी कथन कितना सही है। MySQL इंटरफ़ेस चुनने में मेरी सबसे बड़ी चिंता SQL इंजेक्शन को रोकना है। दूसरी चिंता प्रदर्शन की है।

मेरा आवेदन वर्तमान में प्रक्रियात्मक MySQLi (बिना तैयार किए गए स्टेटमेंट) का उपयोग करता है, और क्वेरी कैश का काफी उपयोग करता है। यह एक अनुरोध में शायद ही कभी तैयार किए गए बयानों का फिर से उपयोग करेगा। मैंने तैयार किए गए बयानों के नामित मापदंडों और सुरक्षा के लिए पीडीओ के लिए कदम शुरू किया।

मैं उपयोग कर रहा हूं MySQL 5.1.61औरPHP 5.3.2

मुझे PDO::ATTR_EMULATE_PREPARESसक्षम होना चाहिए या नहीं? क्या क्वेरी कैश के प्रदर्शन और तैयार बयानों की सुरक्षा दोनों का कोई तरीका है?


3
ईमानदारी से? बस MySQLi का उपयोग करते रहें। यदि यह पहले से ही तैयार कथनों का उपयोग करके काम कर रहा है, तो पीडीओ मूल रूप से अमूर्तता की एक निरर्थक परत है। संपादित करें : पीडीओ हरे क्षेत्र के अनुप्रयोगों के लिए वास्तव में उपयोगी है, जहां आपको यकीन नहीं है कि डेटाबेस बैक-एंड में क्या हो रहा है।
jmkeyes

1
क्षमा करें, मेरा प्रश्न पहले स्पष्ट नहीं था। मैंने इसे संपादित किया है। एप्लिकेशन फिलहाल MySQLi में तैयार कथनों का उपयोग नहीं करता है; बस mysqli_run_query ()। मैंने जो पढ़ा है, उससे MySQLi ने क्वेरी कैश को भी बायपास किया।
एंड्रयू एन्स्ले

जवाबों:


108

अपनी चिंताओं का जवाब देने के लिए:

  1. MySQL> = 5.1.17 (या> PREPAREऔर EXECUTEस्टेटमेंट के लिए 5.1.21 ) क्वेरी कैश में तैयार स्टेटमेंट का उपयोग कर सकते हैं । तो MySQL + PHP का आपका संस्करण क्वेरी कैश के साथ तैयार स्टेटमेंट का उपयोग कर सकता है। हालाँकि, MySQL प्रलेखन में क्वेरी परिणामों के कैशिंग के लिए चेतावनी पर ध्यान दें। कई प्रकार के प्रश्न होते हैं जिन्हें कैश नहीं किया जा सकता है या जो कैश हो जाने के बावजूद बेकार हैं। मेरे अनुभव में क्वेरी कैश अक्सर एक बहुत बड़ी जीत नहीं है। कैश का अधिकतम उपयोग करने के लिए क्वेरी और स्कीमा को विशेष निर्माण की आवश्यकता होती है। लंबे समय में वैसे भी अक्सर एप्लिकेशन-स्तरीय कैशिंग आवश्यक होता है।

  2. देशी तैयारी सुरक्षा के लिए कोई अंतर नहीं रखती है। छद्म-तैयार बयान अभी भी क्वेरी पैरामीटर मूल्यों से बचेंगे, यह बस पीडीओ लाइब्रेरी में किया जाएगा जो बाइनरी प्रोटोकॉल का उपयोग करके MySQL सर्वर पर इसके बजाय स्ट्रिंग्स के साथ होगा। दूसरे शब्दों में, समान पीडीओ कोड आपकी EMULATE_PREPARESसेटिंग की परवाह किए बिना इंजेक्शन हमलों के लिए समान रूप से असुरक्षित (या कमजोर नहीं) होगा । एकमात्र अंतर वह है जहां पैरामीटर प्रतिस्थापन होता है - साथ EMULATE_PREPARES, यह पीडीओ लाइब्रेरी में होता है; बिना EMULATE_PREPARES, यह MySQL सर्वर पर होता है।

  3. बिना EMULATE_PREPARESआपको निष्पादित-समय के बजाय तैयारी के समय में वाक्यविन्यास त्रुटियां मिल सकती हैं; आपके साथ EMULATE_PREPARESनिष्पादन के समय केवल वाक्यविन्यास त्रुटियां होंगी क्योंकि पीडीओ के पास निष्पादन समय तक MySQL को देने के लिए कोई क्वेरी नहीं है। ध्यान दें कि यह आपके द्वारा लिखे गए कोड को प्रभावित करेगा ! खासकर यदि आप उपयोग कर रहे हैं PDO::ERRMODE_EXCEPTION!

एक अतिरिक्त विचार:

  • एक prepare()(देशी तैयार बयानों का उपयोग करते हुए) के लिए एक निश्चित लागत है, इसलिए prepare();execute()देशी तैयार किए गए बयानों के साथ एक छोटे से धीमी हो सकती है, जो तैयार किए गए बयानों का उपयोग करके एक सादे पाठ्य क्वेरी को जारी करने से हो। कई डेटाबेस सिस्टम पर एक के लिए क्वेरी प्लान prepare()को कैश किया जाता है और कई कनेक्शन के साथ साझा किया जा सकता है, लेकिन मुझे नहीं लगता कि MySQL ऐसा करता है। इसलिए यदि आप कई प्रश्नों के लिए अपने तैयार किए गए कथन ऑब्जेक्ट का पुन: उपयोग नहीं करते हैं, तो आपका संपूर्ण निष्पादन धीमा हो सकता है।

अंतिम सिफारिश के रूप में , मुझे लगता है कि MySQL + PHP के पुराने संस्करणों के साथ, आपको तैयार किए गए बयानों का अनुकरण करना चाहिए, लेकिन अपने हाल के संस्करणों के साथ आपको अनुकरण बंद कर देना चाहिए।

पीडीओ का उपयोग करने वाले कुछ ऐप लिखने के बाद, मैंने एक पीडीओ कनेक्शन फ़ंक्शन किया है, जो मुझे लगता है कि सबसे अच्छी सेटिंग्स हैं। आपको शायद कुछ इस तरह का उपयोग करना चाहिए या अपनी पसंदीदा सेटिंग्स से जुड़ना चाहिए:

/**
 * Return PDO handle for a MySQL connection using supplied settings
 *
 * Tries to do the right thing with different php and mysql versions.
 *
 * @param array $settings with keys: host, port, unix_socket, dbname, charset, user, pass. Some may be omitted or NULL.
 * @return PDO
 * @author Francis Avila
 */
function connect_PDO($settings)
{
    $emulate_prepares_below_version = '5.1.17';

    $dsndefaults = array_fill_keys(array('host', 'port', 'unix_socket', 'dbname', 'charset'), null);
    $dsnarr = array_intersect_key($settings, $dsndefaults);
    $dsnarr += $dsndefaults;

    // connection options I like
    $options = array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    );

    // connection charset handling for old php versions
    if ($dsnarr['charset'] and version_compare(PHP_VERSION, '5.3.6', '<')) {
        $options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES '.$dsnarr['charset'];
    }
    $dsnpairs = array();
    foreach ($dsnarr as $k => $v) {
        if ($v===null) continue;
        $dsnpairs[] = "{$k}={$v}";
    }

    $dsn = 'mysql:'.implode(';', $dsnpairs);
    $dbh = new PDO($dsn, $settings['user'], $settings['pass'], $options);

    // Set prepared statement emulation depending on server version
    $serverversion = $dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
    $emulate_prepares = (version_compare($serverversion, $emulate_prepares_below_version, '<'));
    $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, $emulate_prepares);

    return $dbh;
}

26
# 2: निश्चित रूप से मान जो MySQL को पैरामीटर के रूप में प्राप्त होता है (देशी तैयार कथनों में) SQL के लिए पार्स नहीं किया जाता है ? तो पीडीओ के तैयार एमुलेशन का उपयोग करने से इंजेक्शन का जोखिम कम होना चाहिए, जहां भागने में कोई दोष (जैसे ऐतिहासिक मुद्दे mysql_real_escape_stringमल्टी-बाइट पात्रों के साथ थे) फिर भी इंजेक्शन हमलों के लिए एक खुला छोड़ देंगे?
२०:४०

2
@eggyal, आप इस बारे में धारणा बना रहे हैं कि कैसे तैयार किए गए स्टेटमेंट को लागू किया जाता है। पीडीओ के पास अपने तैयार किए गए भागने में एक बग हो सकता है, लेकिन MySQL में बग भी हो सकते हैं। AFAIK, किसी भी समस्या का पता नहीं चला है जिसके लिए तैयार की गई तैयारियां हैं जो पैरामीटर शाब्दिक के माध्यम से अनसैप्ड हो सकती हैं।
फ्रांसिस अविला

2
बहुत बढ़िया जवाब है, लेकिन मेरे पास एक सवाल है: यदि आप EMULATION को बंद कर देते हैं, तो क्या निष्पादन धीमा नहीं होगा? PHP को सत्यापन के लिए तैयार विवरण MySQL को भेजना होगा और उसके बाद ही मापदंडों को भेजना होगा। इसलिए, यदि आप 5 बार तैयार स्टेटमेंट का उपयोग करते हैं, तो PHP 6 बार (5 के बजाय) MySQL से बात करेगी। क्या यह इसे धीमा नहीं करेगा? इसके अलावा, मुझे लगता है कि MySQL के बजाय पीडीओ सत्यापन प्रक्रिया में बग हो सकता है ...
रादु मर्जिया

6
इस उत्तर में तैयार किए गए बिंदुओं पर ध्यान दें, mysql_real_escape_stringहुड के तहत उपयोग किए गए स्टेटमेंट एमुलेशन और परिणामी कमजोरियां जो उत्पन्न हो सकती हैं (बहुत विशेष बढ़त में)।
इग्निगल

6
+1 अच्छा जवाब! लेकिन रिकॉर्ड के लिए, यदि आप देशी तैयारी का उपयोग करते हैं, तो पैरामीटर कभी भी बच नहीं जाते हैं या SQL क्वेरी में MySQL सर्वर साइड पर भी संयुक्त हो जाते हैं। जब तक आप मापदंडों को निष्पादित और आपूर्ति करते हैं, तब तक क्वेरी को पार्स और MySQL में आंतरिक डेटा संरचनाओं में बदल दिया गया है। MySQL ऑप्टिमाइज़र इंजीनियर द्वारा यह ब्लॉग पढ़ें जो इस प्रक्रिया की व्याख्या करता है: guilhembichot.blogspot.com/2014/05/… मैं यह नहीं कह रहा हूं कि देशी तैयारी बेहतर है, इनफोरर जैसा कि हम पीडीओ कोड को सही ढंग से भागने के लिए भरोसा करते हैं (जिसे मैं) करना)।
बिल करविन

9

PDO::ATTR_EMULATE_PREPARESजब आपके PHP के pdo_mysqlखिलाफ संकलित नहीं किया जाता है, तो निष्क्रिय करने (देशी तैयारी चालू करने ) पर सावधान रहें mysqlnd

क्योंकि पुराना libmysqlकुछ कार्यों के साथ पूरी तरह से संगत नहीं है, इसलिए यह अजीब बग पैदा कर सकता है, उदाहरण के लिए:

  1. 64 बिट पूर्णांक के लिए सबसे महत्वपूर्ण बिट्स को खोने पर PDO::PARAM_INT(0x12345678AB को 64 बिट मशीन पर 0x345678AB पर क्रॉप किया जाएगा)
  2. सरल प्रश्न बनाने में असमर्थता जैसे LOCK TABLES(यह SQLSTATE[HY000]: General error: 2030 This command is not supported in the prepared statement protocol yetअपवाद फेंकता है)
  3. अगली क्वेरी से पहले परिणाम या करीबी कर्सर से सभी पंक्तियों को लाने की आवश्यकता है (इसके साथ mysqlndतैयार या स्वचालित रूप से यह काम आपके लिए करता है और mysql सर्वर के साथ सिंक से बाहर नहीं जाता है)

इन बग्स को मैं अपने साधारण प्रोजेक्ट में लगाता था जब अन्य सर्वर पर माइग्रेट करता था जो मॉड्यूल के libmysqlलिए उपयोग किया जाता था pdo_mysql। शायद बहुत अधिक कीड़े हैं, मुझे नहीं पता। इसके अलावा मैंने ताजा 64 बिट डेबियन जेसी पर परीक्षण किया, सभी सूचीबद्ध कीड़े तब होते हैं जब मैं apt-get install php5-mysql, और जब मैं गायब हो जाता हूं apt-get install php5-mysqlnd

जब PDO::ATTR_EMULATE_PREPARESसही पर सेट किया जाता है (डिफ़ॉल्ट रूप में) - ये बग वैसे भी नहीं होते हैं, क्योंकि पीडीओ इस मोड में तैयार किए गए कथनों का उपयोग नहीं करता है। इसलिए, यदि आप phpinfo में अनुभाग के "क्लाइंट API संस्करण" फ़ील्ड में "mysqlnd" विकल्प के pdo_mysqlआधार पर उपयोग नहीं करते हैं - तो आपको बंद नहीं करना चाहिए ।libmysqlpdo_mysqlPDO::ATTR_EMULATE_PREPARES


3
क्या यह चिंता अभी भी 2019 में वैध है?
ओल्डबॉय

8

जैसा कि आप 5.1 रन कर रहे हैं, जिसका अर्थ है कि पीडीओ देशी तैयार स्टेटमेंट कार्यक्षमता का लाभ उठाएगा।

PDO_MYSQL देशी तैयार स्टेटमेंट सपोर्ट का लाभ MySQL 4.1 और उच्चतर में मौजूद होगा। यदि आप mysql क्लाइंट लाइब्रेरीज़ के पुराने संस्करण का उपयोग कर रहे हैं, तो PDO आपके लिए उनका अनुकरण करेगा।

http://php.net/manual/en/ref.pdo-mysql.php

मैंने तैयार किए गए बयानों और बेहतर एपीआई के लिए पीडीओ के लिए MySQLi खाई।

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


उस सूचना के लिए आपका धन्यवाद। क्वेरी कैश का उपयोग करने में सक्षम नहीं होने से आपके प्रदर्शन पर असर पड़ा है या आप पहले भी इसका उपयोग कर रहे थे?
एंड्रयू एनस्ले

मैं उस रूपरेखा के रूप में नहीं कह सकता जो मैं वैसे भी कई स्तरों पर कैश का उपयोग कर रहा हूं। यद्यपि आप स्पष्ट रूप से SELECT SQL_CACHE <बाकी विवरण> का उपयोग कर सकते हैं।
विल मॉर्गन

यह भी नहीं जानता था कि एक चयन SQL_CACHE विकल्प था। हालाँकि, ऐसा प्रतीत होता है कि अभी भी काम नहीं करेगा। डॉक्स से: "क्वेरी रिजल्ट कैश होने पर कैश्ड होता है ..." dev.mysql.com/doc/refman/5.1/en/query-cache-in-select.html
एंड्रयू

हाँ। यह प्लेटफॉर्म की बारीकियों के बजाय क्वेरी की प्रकृति पर निर्भर करता है।
विल मॉर्गन

मैंने पढ़ा है कि "क्वेरी परिणाम को कैश किया जाता है जब तक कि कोई अन्य चीज इसे अस्वीकार्य होने से नहीं रोकती है ," - जो कि मैंने तब तक पढ़ा था - जिसमें तैयार किए गए बयान शामिल थे। हालाँकि, फ्रांसिस अविला के उत्तर के लिए धन्यवाद, मुझे पता है कि यह MySQL के मेरे संस्करण के लिए सच नहीं है।
एंड्रयू एनस्ले

6

मैं वास्तविक डेटाबेस PREPAREकॉल्स को सक्षम करने की सलाह दूंगा क्योंकि इम्यूलेशन सब कुछ नहीं पकड़ता है .. उदाहरण के लिए, यह तैयार करेगा INSERT;!

var_dump($dbh->prepare('INSERT;'));
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
var_dump($dbh->prepare('INSERT;'));

उत्पादन

object(PDOStatement)#2 (1) {
  ["queryString"]=>
  string(7) "INSERT;"
}
bool(false)

मैं ख़ुशी से कोड के लिए एक प्रदर्शन हिट ले लूँगा जो वास्तव में काम करता है।

FWIW

PHP संस्करण: PHP 5.4.9-4ubuntu2.4 (cli)

MySQL संस्करण: 5.5.34-0ubuntu0


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

1
अगर आप रुचि रखते हैं, तो IDK, लेकिन यहाँ कुछ अन्य व्यवहार पर थोड़ा लिखावट है जो मैंने PDO के साथ देखा है जो मुझे इस खरगोश छेद को शुरू करने के लिए प्रेरित करता है। लगता है कि कई प्रश्नों की हैंडलिंग में कमी है।
क्विकशिफ्टिन

मैंने सिर्फ GitHub पर कुछ माइग्रेशन लाइब्रेरीज़ को देखा ... आप क्या जानते हैं, यह बहुत कुछ मेरे ब्लॉग पोस्ट के समान सटीक काम करता है।
क्विकशिफ्टिन

5

एमुलेशन को 'असत्य' पर क्यों स्विच करें?

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

तैयार बनाम पीडीओ के लिए डेटाबेस इंजन का उपयोग करने के खिलाफ मुख्य तर्क सर्वर के लिए दो यात्राएं हैं - एक तैयारी के लिए, और दूसरा पैरामीटर पास होने के लिए - लेकिन मुझे लगता है कि अतिरिक्त सुरक्षा इसके लायक है। इसके अलावा, कम से कम MySQL के मामले में, क्वेरी कैशिंग संस्करण 5.1 के बाद से कोई समस्या नहीं है।

https://tech.michaelseiler.net/2016/07/04/dont-emulate-prepared-statements-pdo-mysql/


1
क्वेरी कैशिंग वैसे भी चला गया है: क्वेरी कैश MySQL 5.7.20 के रूप में पदावनत है, और MySQL 8.0 में हटा दिया गया है।
अल्वारो गोंजालेज

5

मुझे आश्चर्य है कि किसी ने अनुकरण बंद करने के सबसे बड़े कारणों में से एक का उल्लेख नहीं किया है। एमुलेशन के साथ, पीडीओ सभी पूर्णांक लौटाता है और तार के रूप में तैरता है । जब आप अनुकरण बंद करते हैं, तो पूर्णांक और MySQL में फ्लोट्स पूर्णांक बन जाते हैं और PHP में तैरते हैं।

अधिक जानकारी के लिए, इस प्रश्न के लिए स्वीकृत उत्तर देखें: PHP + PDO + MySQL: मैं PHP में पूर्णांक और संख्यात्मक के रूप में MySQL से पूर्णांक और संख्यात्मक कॉलम कैसे लौटाऊं?


0

रिकार्ड के लिए

पीडीओ :: ATTR_EMULATE_PREPARES = true

यह एक बुरा साइड इफेक्ट उत्पन्न कर सकता है। यह स्ट्रिंग के रूप में अंतर मान लौटा सकता है।

PHP 7.4, mysqlnd के साथ pdo।

PDO के साथ एक क्वेरी चलाना :: ATTR_EMULATE_PREPARES = true

कॉलम: आईडी
प्रकार: पूर्णांक
मूल्य: 1

PDO के साथ एक क्वेरी चलाना :: ATTR_EMULATE_PREPARES = false

कॉलम: आईडी
प्रकार: स्ट्रिंग
मान: "1"

किसी भी मामले में, दशमलव मान हमेशा एक स्ट्रिंग को लौटाया जाता है, भले ही कॉन्फ़िगरेशन:


दशमलव मान हमेशा लौटाए जाते हैं एक स्ट्रिंग एकमात्र सही तरीका है
आपका कॉमन सेंस

MySQL के दृष्टिकोण से हाँ, लेकिन यह PHP की तरफ गलत है। जावा और सी # दोनों दशमलव को एक संख्यात्मक मान मानते हैं।
मैगनेट्स

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