Using ईवैल ’का उपयोग एक बुरा अभ्यास क्यों है?


138

मैं अपने गीतों के डेटा को आसानी से संग्रहीत करने के लिए निम्न वर्ग का उपयोग कर रहा हूं।

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            exec 'self.%s=None'%(att.lower()) in locals()
    def setDetail(self, key, val):
        if key in self.attsToStore:
            exec 'self.%s=val'%(key.lower()) in locals()

मुझे लगता है कि यह एक if/elseब्लॉक को लिखने की तुलना में बहुत अधिक व्यापक है । हालांकि, evalऐसा लगता है कि एक बुरा अभ्यास और उपयोग करने के लिए असुरक्षित माना जाता है। यदि हां, तो क्या कोई मुझे समझा सकता है कि मुझे उपरोक्त वर्ग को परिभाषित करने का एक बेहतर तरीका क्यों दिखा?


40
आपने कैसे सीखा exec/evalऔर अभी भी नहीं पता है setattr?
u0b34a0f6ae 14

3
मेरा मानना ​​है कि यह एक लेख से था, जो अजगर और लिस्प की तुलना करता था, जैसा कि मैंने निष्कासन के बारे में सीखा था।
निकिन

जवाबों:


194

हाँ, eval का उपयोग करना एक बुरा अभ्यास है। बस कुछ कारण बताएं:

  1. इसे करने का लगभग हमेशा एक बेहतर तरीका है
  2. बहुत खतरनाक और असुरक्षित
  3. डिबगिंग को मुश्किल बनाता है
  4. धीरे

आपके मामले में आप इसके बजाय सेटट्रा का उपयोग कर सकते हैं :

class Song:
    """The class to store the details of each song"""
    attsToStore=('Name', 'Artist', 'Album', 'Genre', 'Location')
    def __init__(self):
        for att in self.attsToStore:
            setattr(self, att.lower(), None)
    def setDetail(self, key, val):
        if key in self.attsToStore:
            setattr(self, key.lower(), val)

संपादित करें:

कुछ मामले हैं जहां आपको eval या execute का उपयोग करना है। लेकिन वे दुर्लभ हैं। अपने मामले में eval का उपयोग करना सुनिश्चित करने के लिए एक बुरा अभ्यास है। मैं बुरे अभ्यास पर जोर दे रहा हूं क्योंकि अक्सर गलत जगह पर निष्कासन और निष्पादन का उपयोग किया जाता है।

संपादित करें 2:

यह कुछ असहमति की तरह लग रहा है कि ओपी मामले में eval 'बहुत खतरनाक और असुरक्षित' है। यह इस विशिष्ट मामले के लिए सही हो सकता है लेकिन सामान्य तौर पर नहीं। प्रश्न सामान्य था और मैंने जिन कारणों को सूचीबद्ध किया था वे सामान्य मामले के लिए भी सही हैं।

EDIT 3: बिंदु 1 और 4 को फिर से व्यवस्थित किया गया


23
-1: "बहुत खतरनाक और असुरक्षित" गलत है। अन्य तीन उत्कृष्ट रूप से स्पष्ट हैं। कृपया उन्हें पुनः व्यवस्थित करें ताकि 2 और 4 पहले दो हों। यह केवल असुरक्षित है यदि आप बुरी सोशियोपैथ से घिरे हुए हैं जो आपके आवेदन को खत्म करने के तरीकों की तलाश कर रहे हैं।
S.Lott

51
@ S.Lott, सामान्य रूप से eval / execute से बचने के लिए असुरक्षा एक बहुत ही महत्वपूर्ण कारण है। वेबसाइटों जैसे कई अनुप्रयोगों को अतिरिक्त देखभाल करनी चाहिए। एक वेबसाइट में ओपी उदाहरण लें जो उपयोगकर्ताओं से गीत के नाम में प्रवेश करने की उम्मीद करता है। इसका जल्द या बाद में शोषण होना तय है। यहां तक ​​कि एक निर्दोष इनपुट जैसे: चलो मज़े करते हैं। एक वाक्यविन्यास त्रुटि का कारण होगा और भेद्यता को उजागर करेगा।
नादिया अलरामली

18
@ नादिया अल्रामली: उपयोगकर्ता इनपुट और evalएक-दूसरे से कोई लेना-देना नहीं है। मूल रूप से गलत डिज़ाइन किया गया एक एप्लिकेशन मौलिक रूप से गलत डिज़ाइन किया गया है। evalशून्य से खराब डिज़ाइन का मूल कारण शून्य से अधिक नहीं है या एक मॉड्यूल को आयात करने का प्रयास करना है जो मौजूद नहीं है। evalअसुरक्षित नहीं है। एप्लिकेशन असुरक्षित हैं।
एस.लॉट

17
@ जैफजोस: वास्तव में, यह मूलभूत रूप से बुरा / बुरा है क्योंकि यह कोड के रूप में अनिर्दिष्ट डेटा का इलाज कर रहा है (यही कारण है कि XSS, SQL इंजेक्शन और स्टैक स्मैश मौजूद हैं)। @ एस.लॉट: "यह केवल असुरक्षित है यदि आप बुरी सोशियोपैथ से घिरे हैं जो आपके आवेदन को वापस करने के तरीके की तलाश कर रहे हैं।" कूल, इसलिए कहते हैं कि आप एक कार्यक्रम बनाते हैं calc, और संख्याओं को जोड़ने के लिए इसे निष्पादित करते हैं print(eval("{} + {}".format(n1, n2)))और बाहर निकलते हैं। अब आप इस प्रोग्राम को कुछ OS के साथ वितरित करते हैं। फिर कोई व्यक्ति एक बैश स्क्रिप्ट बनाता है जो स्टॉक साइट से कुछ नंबर लेता है और उनका उपयोग करके जोड़ता है calc। तेज़ी से?
L --o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e

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

32

उपयोग evalकमजोर है, स्पष्ट रूप से बुरा अभ्यास नहीं है।

  1. यह "सॉफ्टवेयर के मौलिक सिद्धांत" का उल्लंघन करता है। आपका स्रोत निष्पादन योग्य क्या है, इसका कुल योग नहीं है। आपके स्रोत के अलावा, वहाँ तर्क हैं eval, जिन्हें स्पष्ट रूप से समझा जाना चाहिए। इस कारण से, यह अंतिम उपाय का उपकरण है।

  2. यह आमतौर पर विचारहीन डिजाइन का संकेत है। डायनामिक सोर्स कोड का शायद ही कोई अच्छा कारण हो, जो ऑन-द-फ्लाई बनाया गया हो। लगभग कुछ भी प्रतिनिधिमंडल और अन्य OO डिजाइन तकनीकों के साथ किया जा सकता है।

  3. यह कोड के छोटे टुकड़ों के फ्लाई-ऑन-द-फ्लाई संकलन के लिए धीमी गति से होता है। एक ओवरहेड जो बेहतर डिजाइन पैटर्न का उपयोग करके बचा जा सकता है।

एक फुटनोट के रूप में, विक्षिप्त समाजोपथ के हाथों में, यह अच्छी तरह से काम नहीं कर सकता है। हालाँकि, जब विक्षिप्त सोशियोपैथिक उपयोगकर्ताओं या प्रशासकों के साथ सामना किया जाता है, तो उन्हें पहले स्थान पर पायथन की व्याख्या नहीं करना सबसे अच्छा है। वास्तव में बुराई के हाथों में, पायथन एक दायित्व कर सकता है; evalजोखिम बिल्कुल नहीं बढ़ाता है।


7
@ ओवेन एस। बिंदु यह है। फोल्क्स आपको बताएंगे कि evalकिसी प्रकार की "सुरक्षा भेद्यता" है। जैसे कि पायथन - ही - केवल व्याख्या किए गए स्रोत का एक गुच्छा नहीं था जिसे कोई भी संशोधित कर सकता है। जब "eval एक सुरक्षा छेद है" के साथ सामना किया जाता है, तो आप केवल यह मान सकते हैं कि यह समाजशास्त्रियों के हाथों में एक सुरक्षा छेद है। साधारण प्रोग्रामर केवल मौजूदा पायथन स्रोत को संशोधित करते हैं और सीधे उनकी समस्याओं का कारण बनते हैं। evalजादू के माध्यम से अप्रत्यक्ष रूप से नहीं ।
एस.लॉट

14
ठीक है, मैं आपको बता सकता हूं कि मैं क्यों कहूंगा कि eval एक सुरक्षा भेद्यता है, और इसे इनपुट के रूप में दिए गए स्ट्रिंग की विश्वसनीयता के साथ करना होगा। यदि वह स्ट्रिंग बाहरी दुनिया से, पूरे या आंशिक रूप में आती है, तो आपके प्रोग्राम पर स्क्रिप्टिंग हमले की संभावना है यदि आप सावधान नहीं हैं। लेकिन यह बाहरी हमलावर का अपमान है, उपयोगकर्ता या व्यवस्थापक का नहीं।
ओवेन एस।

6
@OwenS .: "यदि वह स्ट्रिंग आती है, पूरी या आंशिक रूप से, बाहरी दुनिया से" अक्सर झूठी होती है। यह "सावधान" बात नहीं है। यह काला और सफेद है। यदि पाठ उपयोगकर्ता से आता है, तो उस पर कभी भरोसा नहीं किया जा सकता है। देखभाल वास्तव में इसका हिस्सा नहीं है, यह बिल्कुल अविश्वसनीय है। अन्यथा, टेक्स्ट डेवलपर, इंस्टॉलर या एडमिन से आता है, और उस पर भरोसा किया जा सकता है।
S.Lott

8
@ ओवेन: अविश्वासित पायथन कोड की एक स्ट्रिंग के लिए कोई संभव नहीं है जो इसे विश्वसनीय बना देगा। "सावधान" भाग को छोड़कर आप जो कुछ भी कह रहे हैं, उससे मैं सहमत हूं। यह बहुत कुरकुरा भेद है। बाहरी दुनिया से कोड अविश्वसनीय है। AFAIK, बचने या फ़िल्टर करने की कोई राशि इसे साफ नहीं कर सकती है। यदि आपके पास किसी प्रकार का भागने का कार्य है जो कोड को स्वीकार्य करेगा, तो कृपया साझा करें। मुझे नहीं लगता था कि ऐसा संभव है। उदाहरण के लिए while True: passकिसी तरह के बच के साथ सफाई करना कठिन होगा।
22. 22

2
@ ओवेन: "एक स्ट्रिंग के रूप में अभिप्रेत है, मनमाना कोड नहीं"। वह असंबंधित है। यह सिर्फ एक स्ट्रिंग मूल्य है, जिसे आप कभी नहीं से गुजरेंगे eval(), क्योंकि यह एक स्ट्रिंग है। "बाहरी दुनिया" से कोड को पवित्र नहीं किया जा सकता है। बाहर की दुनिया के तार सिर्फ तार हैं। मैं इस बारे में स्पष्ट नहीं हूँ कि आप किस बारे में बात कर रहे हैं। शायद आपको अधिक संपूर्ण ब्लॉग पोस्ट प्रदान करना चाहिए और इसे यहां लिंक करना चाहिए।
एस.लॉट

23

इस मामले में, हाँ। के बजाय

exec 'self.Foo=val'

आपको बिलिन फ़ंक्शन का उपयोग करना चाहिए setattr:

setattr(self, 'Foo', val)

16

हाँ यही है:

अजगर का उपयोग कर हैक:

>>> eval(input())
"__import__('os').listdir('.')"
...........
...........   #dir listing
...........

नीचे दिया गया कोड विंडोज मशीन पर चलने वाले सभी कार्यों को सूचीबद्ध करेगा।

>>> eval(input())
"__import__('subprocess').Popen(['tasklist'],stdout=__import__('subprocess').PIPE).communicate()[0]"

लिनक्स में:

>>> eval(input())
"__import__('subprocess').Popen(['ps', 'aux'],stdout=__import__('subprocess').PIPE).communicate()[0]"

7

यह ध्यान देने योग्य है कि प्रश्न में विशिष्ट समस्या के लिए, उपयोग करने के लिए कई विकल्प हैं eval:

सबसे सरल, जैसा कि उल्लेख किया गया है, उपयोग कर रहा है setattr:

def __init__(self):
    for name in attsToStore:
        setattr(self, name, None)

एक कम स्पष्ट दृष्टिकोण ऑब्जेक्ट के __dict__ऑब्जेक्ट को सीधे अपडेट कर रहा है। यदि आप सभी करना चाहते हैं None, तो विशेषताओं को इनिशियलाइज़ करना है , तो यह ऊपर की तुलना में कम सीधा है। लेकिन इस पर विचार करें:

def __init__(self, **kwargs):
    for name in self.attsToStore:
       self.__dict__[name] = kwargs.get(name, None)

इससे आप कंस्ट्रक्टर को कीवर्ड तर्क पास कर सकते हैं, जैसे:

s = Song(name='History', artist='The Verve')

यह आपको अपने उपयोग को locals()और अधिक स्पष्ट करने की अनुमति देता है , जैसे:

s = Song(**locals())

... और, यदि आप वास्तव में Noneउन विशेषताओं को निर्दिष्ट करना चाहते हैं जिनके नाम निम्नलिखित हैं locals():

s = Song(**dict([(k, None) for k in locals().keys()]))

गुण की सूची के लिए डिफ़ॉल्ट मानों के साथ एक वस्तु प्रदान करने के लिए एक और दृष्टिकोण वर्ग की __getattr__विधि को परिभाषित करना है :

def __getattr__(self, name):
    if name in self.attsToStore:
        return None
    raise NameError, name

यह विधि तब प्राप्त होती है जब नामित विशेषता सामान्य तरीके से नहीं मिलती है। यह सीधे तौर पर कंस्ट्रक्टर में विशेषताओं को सेट करने या अपडेट करने की तुलना में कुछ कम सीधा है __dict__, लेकिन इसमें वास्तव में विशेषता के निर्माण की योग्यता नहीं है जब तक कि यह मौजूद नहीं है, जो क्लास की मेमोरी उपयोग को काफी हद तक कम कर सकता है।

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


1
एक और तरीका जो यकीनन अधिक (या कम) पायथोनिक है: ऑब्जेक्ट का __dict__सीधे उपयोग करने के बजाय , ऑब्जेक्ट को एक वास्तविक शब्दकोश ऑब्जेक्ट दें, या तो विरासत या एक विशेषता के रूप में।
जोश ली

1
"एक कम स्पष्ट दृष्टिकोण वस्तु के अपडेट कर रहा है dict वस्तु सीधे" => ध्यान दें कि किसी भी वर्णनकर्ता (संपत्ति या अन्य) या बाईपास इस वसीयत __setattr__अप्रत्याशित परिणाम को ओवरराइड, जन्म दे सकता है जो। setattr()यह समस्या नहीं है।
ब्रूनो डेथिलियर्स

5

अन्य उपयोगकर्ताओं ने बताया कि आपका कोड कैसे बदल सकता है, यह निर्भर नहीं करता है eval; मैं उपयोग करने के लिए एक वैध उपयोग के मामले की पेशकश करूंगा eval, एक जो कि सीपीथॉन में भी पाया जाता है: परीक्षण

यहाँ एक उदाहरण है test_unary.pyजहाँ मैंने एक परीक्षा में पाया कि क्या (+|-|~)b'a'उठाता है TypeError:

def test_bad_types(self):
    for op in '+', '-', '~':
        self.assertRaises(TypeError, eval, op + "b'a'")
        self.assertRaises(TypeError, eval, op + "'a'")

उपयोग स्पष्ट रूप से यहाँ बुरा अभ्यास नहीं है; आप इनपुट को परिभाषित करते हैं और केवल व्यवहार का निरीक्षण करते हैं। evalपरीक्षण के लिए आसान है।

evalसीपीथॉन गिट रिपॉजिटरी पर प्रदर्शन के लिए इस खोज पर एक नज़र डालें ; eval के साथ परीक्षण का भारी उपयोग किया जाता है।


2

जब eval()उपयोगकर्ता द्वारा प्रदान किए गए इनपुट को संसाधित करने के लिए उपयोग किया जाता है, तो आप उपयोगकर्ता को कुछ इस तरह प्रदान करने के लिए ड्रॉप-टू-आरईपीएल सक्षम करते हैं :

"__import__('code').InteractiveConsole(locals=globals()).interact()"

आप इससे दूर हो सकते हैं, लेकिन आम तौर पर आप अपने अनुप्रयोगों में मनमाने कोड निष्पादन के लिए वैक्टर नहीं चाहते हैं ।


1

@ नादिया अल्रामली जवाब के अलावा, चूंकि मैं पायथन के लिए नया हूं और यह जांचने के लिए उत्सुक था evalकि समय का उपयोग कैसे प्रभावित करेगा , मैंने एक छोटे से कार्यक्रम की कोशिश की और नीचे अवलोकन थे:

#Difference while using print() with eval() and w/o eval() to print an int = 0.528969s per 100000 evals()

from datetime import datetime
def strOfNos():
    s = []
    for x in range(100000):
        s.append(str(x))
    return s

strOfNos()
print(datetime.now())
for x in strOfNos():
    print(x) #print(eval(x))
print(datetime.now())

#when using eval(int)
#2018-10-29 12:36:08.206022
#2018-10-29 12:36:10.407911
#diff = 2.201889 s

#when using int only
#2018-10-29 12:37:50.022753
#2018-10-29 12:37:51.090045
#diff = 1.67292
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.