बैश / POSIX गोले में एक चर को भूलने के लिए सुरक्षा निहितार्थ


206

यदि आप कुछ समय के लिए unix.stackexchange.com का अनुसरण कर रहे हैं, तो आपको अब तक यह जान लेना चाहिए कि echo $varबॉर्न / पोसिक्स गोले (अपवाद के रूप में ) के रूप में सूची के संदर्भ में (जैसा कि ) में एक चर को छोड़ दिया गया है और इसका एक विशेष अर्थ है जब तक आपके पास एक बहुत अच्छा कारण नहीं होना चाहिए।

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

70 के दशक के उत्तरार्ध में बॉर्न शेल की प्रारंभिक रिलीज़ के बाद से यह मामला रहा है और कॉर्न शेल ( डेविड कॉर्न का सबसे बड़ा पछतावा (प्रश्न # 7) में से एक ) या bashजिसे ज्यादातर कोर्न शेल की नकल किया गया था, और इसे बदल दिया गया है यह कैसे POSIX / यूनिक्स द्वारा निर्दिष्ट किया गया है।

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

मेरे अनुभव में, मुख्य रूप से 3 प्रकार के लोग हैं जो अपने चर को उद्धृत करने के लिए छोड़ देते हैं:

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

  • भुलक्कड़ लोग।

  • जिन लोगों को बार-बार हथौड़ा मारने के बाद भी यकीन नहीं होता है, जो सोचते हैं कि निश्चित रूप से बॉर्न शेल लेखक ने हमारे सभी चर को उद्धृत करने का इरादा नहीं किया था

शायद हम उन्हें मना सकते हैं यदि हम इस तरह के व्यवहार से जुड़े जोखिम को उजागर करते हैं।

यदि आप अपने चरों को उद्धृत करना भूल जाते हैं, तो संभवतः इससे भी बुरी बात यह हो सकती है। यह वास्तव में है कि बुरा?

हम यहां किस तरह की भेद्यता की बात कर रहे हैं?

किन संदर्भों में यह एक समस्या हो सकती है?


8
BashPitashes कुछ ऐसा है जो आप मुझे पसंद करेंगे।
pawel7318

से backlink इस लेख मैंने लिखा writeup के लिए, धन्यवाद
mirabilos

5
मैं एक चौथे समूह को जोड़ने का सुझाव देना चाहता हूं: जो लोग कई बार अत्यधिक उद्धरण के लिए सिर पर चोट कर चुके हैं, शायद तीसरे समूह के सदस्यों द्वारा दूसरों पर अपनी हताशा (पीड़ित को धमकाने वाली) को निकालकर। निश्चित रूप से दुख की बात यह है कि चौथे समूह के लोग चीजों को उद्धृत करने में विफल हो सकते हैं जब यह सबसे ज्यादा मायने रखता है।
ack

जवाबों:


201

प्रस्तावना

पहले, मैं कहूंगा कि यह समस्या का समाधान करने का सही तरीका नहीं है। यह कहना थोड़ा सा है कि " आपको लोगों की हत्या नहीं करनी चाहिए क्योंकि अन्यथा आप जेल जाएंगे ।"

इसी तरह, आप अपने चर को उद्धृत नहीं करते हैं क्योंकि अन्यथा आप सुरक्षा कमजोरियों का परिचय दे रहे हैं। आप अपने चर को उद्धृत करते हैं क्योंकि यह गलत नहीं है (लेकिन अगर जेल का डर मदद कर सकता है, तो क्यों नहीं)।

उन लोगों के लिए एक छोटा सारांश जो अभी-अभी ट्रेन में कूद गए हैं।

अधिकांश गोले में, एक चर विस्तार को निर्विवाद छोड़ दिया (हालांकि वह (और इस उत्तर के बाकी) भी कमांड प्रतिस्थापन ( `...`या $(...)) और अंकगणितीय विस्तार ( $((...))या $[...]) के लिए लागू होता है एक बहुत ही विशेष अर्थ है। इसका वर्णन करने का सबसे अच्छा तरीका यह है कि यह किसी प्रकार के निहित विभाजन + ग्लोब ऑपरेटर¹ को लागू करने जैसा है ।

cmd $var

किसी अन्य भाषा में कुछ इस तरह लिखा जाएगा:

cmd(glob(split($var)))

$var$IFSविशेष पैरामीटर ( स्प्लिट पार्ट) को शामिल करने वाले जटिल नियमों के अनुसार पहले शब्दों की एक सूची में विभाजित किया जाता है और फिर उस विभाजन के परिणामस्वरूप प्रत्येक शब्द को एक पैटर्न के रूप में माना जाता है जो इसे ( ग्लॉब भाग) से मेल खाने वाली फ़ाइलों की सूची में विस्तारित होता है ।

एक उदाहरण के रूप में, यदि $varइसमें समाहित है *.txt,/var/*.xmlऔर $IFS सम्‍मिलित है ,, cmdतो तमाम तर्कों के साथ बुलाया जाएगा, पहले cmdवाले txt और वर्तमान निर्देशिका में xmlफाइलें और अंदर की फाइलें होने के नाते /var

यदि आप cmdकेवल दो शाब्दिक तर्कों के साथ फोन करना चाहते हैं cmd और *.txt,/var/*.xml, आप लिखेंगे:

cmd "$var"

जो आपकी अन्य परिचित भाषा में होगा:

cmd($var)

शेल में भेद्यता से हमारा क्या मतलब है ?

आखिरकार, यह समय की सुबह से ही ज्ञात है कि शेल लिपियों का उपयोग सुरक्षा-संवेदनशील संदर्भों में नहीं किया जाना चाहिए। निश्चित रूप से, ठीक है, एक चर को छोड़ना एक बग है लेकिन यह इतना नुकसान नहीं कर सकता है, क्या यह कर सकता है?

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

कभी-कभी अनजाने में। मिसाल के तौर पर system('cmd $PATH_INFO') एक में php/ perl/ pythonCGI स्क्रिप्ट कमांड लाइन व्याख्या करने के लिए एक खोल आह्वान करता है (तथ्य यह है कि उल्लेख करने के लिए नहीं cmdअपने आप में एक खोल स्क्रिप्ट और उसके लेखक हो सकता है कभी नहीं की उम्मीद हो सकता है यह एक CGI से कहा जा करने के लिए)।

विशेषाधिकार हनन के लिए एक रास्ता होने पर आपको एक भेद्यता मिल गई है, जब कोई व्यक्ति (उसे हमलावर कहते हैं ) कुछ ऐसा करने में सक्षम है जिसका वह मतलब नहीं है।

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

असल में, आपको एक समस्या है जब आपका छोटी गाड़ी हमलावर के नियंत्रण में डेटा संसाधित करती है ।

अब, यह हमेशा स्पष्ट नहीं होता है कि डेटा कहाँ से आ सकता है, और यह बताना मुश्किल है कि क्या आपका कोड कभी भी अविश्वसनीय डेटा को संसाधित करने के लिए मिलेगा।

जहां तक ​​चरों का संबंध है, एक CGI स्क्रिप्ट के मामले में, यह काफी स्पष्ट है, डेटा CGI GET / POST पैरामीटर और कुकीज, पथ, होस्ट ... पैरामीटर जैसी चीजें हैं।

एक सेटिउड स्क्रिप्ट के लिए (एक उपयोगकर्ता द्वारा दूसरे के रूप में चलाने पर), यह तर्क या पर्यावरण चर हैं।

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

उस संबंध में, यहां तक ​​कि एक इंटरेक्टिव शेल के संकेत पर, आप असुरक्षित हो सकते हैं ( उदाहरण के लिए /tmpया ~/tmpउदाहरण के लिए फ़ाइलों को संसाधित करते समय )।

यहां तक ​​कि एक ~/.bashrcअसुरक्षित भी हो सकता है (उदाहरण के लिए, bashजब ग्राहक के नियंत्रण में कुछ चर के साथ सर्वर परिनियोजन की तरह sshचलाने के लिए आह्वान किया जाएगा )।ForcedCommandgit

अब, एक स्क्रिप्ट को सीधे अविश्वसनीय डेटा को संसाधित करने के लिए नहीं कहा जा सकता है, लेकिन इसे किसी अन्य कमांड द्वारा किया जा सकता है। या आपका गलत कोड उन लिपियों में कॉपी-पेस्ट किया जा सकता है जो आपके द्वारा या आपके किसी सहकर्मी से 3 साल नीचे (करते हैं)। एक जगह जहाँ यह विशेष रूप से महत्वपूर्ण है क्यू एंड ए साइटों के उत्तर में है क्योंकि आपको कभी नहीं पता होगा कि आपके कोड की प्रतियां कहाँ समाप्त हो सकती हैं।

व्यापार के लिए नीचे; यह कितना बुरा है?

एक चर (या कमांड प्रतिस्थापन) को छोड़ना खोल कोड के साथ जुड़े सुरक्षा भेद्यताओं का नंबर एक स्रोत है। आंशिक रूप से क्योंकि वे कीड़े अक्सर कमजोरियों में अनुवाद करते हैं, बल्कि इसलिए भी क्योंकि यह बिना पढ़े हुए चरों को देखना आम है।

दरअसल, जब शेल कोड में कमजोरियों की तलाश की जाती है, तो सबसे पहली बात यह है कि अनक्वेटेड वेरिएबल्स की तलाश करें। स्पॉट करना आसान है, अक्सर एक अच्छा उम्मीदवार, आमतौर पर हमलावर-नियंत्रित डेटा पर वापस ट्रैक करना आसान होता है।

वहाँ एक अनंत चर के तरीके की एक अनंत संख्या एक भेद्यता में बदल सकते हैं। मैं यहां केवल कुछ सामान्य रुझान बताऊंगा।

जानकारी प्रकटीकरण

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

असमान बाहरी इनपुट पर किए गए ग्लोबिंग का मतलब है कि हमलावर आपको किसी भी निर्देशिका की सामग्री को पढ़ सकता है।

में:

echo You entered: $unsanitised_external_input

यदि $unsanitised_external_inputशामिल है /*, तो इसका मतलब है कि हमलावर सामग्री देख सकता है /। कोई बड़ी बात नहीं। हालांकि यह और अधिक दिलचस्प हो जाता है के साथ /home/*जो आप मशीन पर उपयोगकर्ता नामों की सूची देता है, /tmp/*, /home/*/.forwardअन्य खतरनाक प्रथाओं पर संकेत, के लिए /etc/rc*/*समर्थित सेवाओं के लिए ... कोई ज़रूरत नहीं उन्हें व्यक्तिगत नाम है। मान का /* /*/* /*/*/*...केवल संपूर्ण फ़ाइल सिस्टम सूचीबद्ध करेगा।

सेवा कमजोरियों से इनकार।

पिछले मामले को थोड़ा आगे ले जाना और हमें एक DoS मिला है।

वास्तव में, गैर-सूचीबद्ध इनपुट के साथ सूची के संदर्भ में कोई भी निर्विवाद चर कम से कम एक DoS भेद्यता है।

यहां तक ​​कि विशेषज्ञ शेल स्क्रिप्टर्स आमतौर पर चीजों को उद्धृत करना भूल जाते हैं:

#! /bin/sh -
: ${QUERYSTRING=$1}

:नो-ऑप कमांड है। क्या गलत होने की सम्भावना है?

कि अगर अप्रभावित था असाइन $1करने के लिए है । यह कमांड लाइन के साथ-साथ CGI स्क्रिप्ट को कॉल करने योग्य एक त्वरित तरीका है।$QUERYSTRING$QUERYSTRING

यही कारण है कि $QUERYSTRINGअभी भी विस्तार किया जाता है और क्योंकि यह उद्धृत नहीं कर रहा है, विभाजन + ग्लोब ऑपरेटर शुरू हो जाती है।

अब, कुछ ग्लब्स हैं जो विशेष रूप से महंगे हैं। यह /*/*/*/*काफी खराब है क्योंकि इसका मतलब है कि निर्देशिका को 4 स्तर तक नीचे सूचीबद्ध करना। डिस्क और सीपीयू गतिविधि के अलावा, इसका मतलब है कि हजारों फ़ाइल पथों का भंडारण (40k यहां एक न्यूनतम सर्वर वीएम, 10k जिसमें से निर्देशिकाएं)।

अब /*/*/*/*/../../../../*/*/*/*40k x 10k का मतलब है और /*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*सबसे शक्तिशाली मशीन को अपने घुटनों तक लाने के लिए पर्याप्त है।

इसे अपने लिए आज़माएँ (हालाँकि आपकी मशीन क्रैश या लटकने के लिए तैयार हो):

a='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*' sh -c ': ${a=foo}'

बेशक, अगर कोड है:

echo $QUERYSTRING > /some/file

तब आप डिस्क को भर सकते हैं।

बस शेल cgi या bash cgi या ksh cgi पर एक Google खोज करें , और आपको कुछ पेज मिलेंगे जो आपको बताएंगे कि कैसे गोले में CGI लिखना है। ध्यान दें कि प्रक्रिया के मापदंडों में से आधे असुरक्षित हैं।

यहां तक ​​कि डेविड कॉर्न की खुद की चपेट में है (कुकी से निपटने के लिए देखें)।

मनमाना कोड निष्पादन कमजोरियों तक

महत्वाकांक्षी कोड निष्पादन सबसे खराब प्रकार की भेद्यता है, क्योंकि अगर हमलावर किसी भी कमांड को चला सकता है, तो वह क्या कर सकता है, इसकी कोई सीमा नहीं है।

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

awk -v foo=$external_input '$2 == foo'

यहाँ, इरादा $external_inputशेल चर की सामग्री को चर में निर्दिष्ट करना था foo awk

अभी:

$ external_input='x BEGIN{system("uname")}'
$ awk -v foo=$external_input '$2 == foo'
Linux

के विभाजन के कारण दूसरा शब्द $external_input असाइन नहीं किया गया है, fooलेकिन awkकोड के रूप में माना जाता है (यहां जो एक मनमाना आदेश निष्पादित करता है:) uname

यही कारण है कि वे आदेश दिए गए अन्य आदेश पर अमल कर सकते हैं के लिए विशेष रूप से एक समस्या है ( awk, env, sed(जीएनयू एक), perl, find...) विशेष रूप से जीएनयू वेरिएंट (जो तर्क के बाद विकल्पों को स्वीकार) के साथ। कभी कभी, आप आदेशों पर शक नहीं होता जैसे अन्य लोगों को निष्पादित करने में सक्षम हो ksh, bashया zshकी [या printf...

for file in *; do
  [ -f $file ] || continue
  something-that-would-be-dangerous-if-$file-were-a-directory
done

यदि हम नामक एक निर्देशिका बनाते हैं x -o yes, तो परीक्षण सकारात्मक हो जाता है, क्योंकि यह पूरी तरह से अलग सशर्त अभिव्यक्ति है जिसका हम मूल्यांकन कर रहे हैं।

इससे भी बदतर, अगर हम x -a a[0$(uname>&2)] -gt 1कम से कम सभी ksh कार्यान्वयनों (जिसमें sh अधिकांश वाणिज्यिक यूनियनों और कुछ BSDs शामिल हैं) के साथ, नामक एक फ़ाइल बनाते हैं , जो निष्पादित करता है uname क्योंकि उन शेल [कमांड के संख्यात्मक तुलना ऑपरेटरों पर अंकगणितीय मूल्यांकन करते हैं ।

$ touch x 'x -a a[0$(uname>&2)] -gt 1'
$ ksh -c 'for f in *; do [ -f $f ]; done'
Linux

के साथ bashएक फ़ाइल नाम के लिए ही x -a -v a[0$(uname>&2)]

बेशक, अगर वे मनमाना निष्पादन प्राप्त नहीं कर सकते, तो हमलावर कम क्षति के लिए समझौता कर सकता है (जो मनमाना निष्पादन प्राप्त करने में मदद कर सकता है)। कोई भी आदेश जो फाइलें लिख सकता है या अनुमतियाँ बदल सकता है, स्वामित्व या किसी भी मुख्य या साइड इफेक्ट का फायदा उठाया जा सकता है।

फ़ाइल नाम के साथ सभी प्रकार की चीजें की जा सकती हैं।

$ touch -- '-R ..'
$ for file in *; do [ -f "$file" ] && chmod +w $file; done

और आप ..लेखनीय (GNU के साथ पुनरावर्ती chmod) बनाते हैं ।

सार्वजनिक रूप से लिखने योग्य क्षेत्रों में फ़ाइलों के स्वचालित प्रसंस्करण करने वाले लिपियों /tmpको बहुत सावधानी से लिखा जाना है।

व्हाट अबाउट [ $# -gt 1 ]

मुझे लगता है कि मैं बहुत खुश हूँ। कुछ लोग यह सोचकर सभी परेशान हो जाते हैं कि क्या किसी विशेष विस्तार से यह तय करने में समस्या हो सकती है कि क्या वे उद्धरणों को छोड़ सकते हैं।

यह कहने जैसा है। अरे, ऐसा लगता $#है कि विभाजित + ग्लोब ऑपरेटर के अधीन नहीं हो सकता है, आइए शेल को विभाजित करने के लिए कहें + इसे ग्लोब करें । या अरे, चलो गलत कोड लिखते हैं क्योंकि बग हिट होने की संभावना नहीं है

अब इसकी संभावना कितनी कम है? ठीक है, $#(या $!, $?या किसी भी अंकगणितीय प्रतिस्थापन) में केवल अंक (या -कुछ के लिए ) हो सकते हैं ताकि ग्लोब वाला भाग बाहर हो। के लिए विभाजन हिस्सा हालांकि कुछ करने के लिए, हम सभी की जरूरत के लिए है $IFSअंक होने के लिए (या -)।

कुछ गोले के साथ, $IFSपर्यावरण से विरासत में मिला जा सकता है, लेकिन अगर पर्यावरण सुरक्षित नहीं है, तो यह वैसे भी खेल है।

अब यदि आप एक फंक्शन लिखते हैं जैसे:

my_function() {
  [ $# -eq 2 ] || return
  ...
}

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

# my_function
#   inputs:
#     $1: source directory
#     $2: destination directory
#   $IFS: used to split $#, expected not to contain digits...

और अपने फ़ंक्शन को कॉल करने वाले कोड को यह सुनिश्चित करने की आवश्यकता है कि $IFSइसमें अंक नहीं हैं। वह सब क्योंकि आपको उन 2 दोहरे-भाव वर्णों को टाइप करने का मन नहीं था।

अब, उस [ $# -eq 2 ]बग को भेद्यता बनने के लिए, आपको हमलावर$IFS के नियंत्रण में मूल्य के लिए किसी तरह की आवश्यकता होगी । जब तक कि हमलावर एक और बग का फायदा उठाने में कामयाब नहीं हो जाता, तब तक यह हो सकता है ।

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

उदाहरण के लिए:

n=$(($1 + 1))
if [ $# -gt 2 ]; then
  echo >&2 "Too many arguments"
  exit 1
fi

और एक $1मान के साथ (IFS=-1234567890), उस अंकगणितीय मूल्यांकन में IFS की सेटिंग का साइड इफेक्ट होता है और अगली [ कमांड विफल हो जाती है जिसका अर्थ है कि बहुत सारे args के लिए चेक बायपास हो गया है।

जब विभाजन + ग्लोब ऑपरेटर को लागू नहीं किया जाता है तो क्या होगा?

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

[[ $a = $b ]]   # a `ksh` construct also supported by `bash`
case $a in ($b) ...; esac

परीक्षण करें कि क्या $aऔर $bएक ही हैं (को छोड़कर zsh) लेकिन अगर $aपैटर्न में मेल खाता है $b। और तुम उद्धृत करने के लिए की जरूरत है $bअगर आप तारों के रूप में तुलना करना चाहते हैं (एक ही बात में "${a#$b}"या "${a%$b}"या "${a##*$b*}"जहां $bउद्धृत किया जाना चाहिए, अगर यह नहीं है एक पैटर्न के रूप में लिया जाना चाहिए)।

क्या इसका मतलब है कि वह यह है कि [[ $a = $b ]]मामलों में सही वापस आ सकते हैं जहां $aसे अलग है $b(उदाहरण के लिए जब $aहै anythingऔर $bहै *) या जब वे समान हैं झूठी वापस आ सकते हैं (उदाहरण के लिए जब दोनों $aऔर $bकर रहे हैं [a])।

एक सुरक्षा भेद्यता के लिए कर सकते हैं? हाँ, किसी बग की तरह। यहां, हमलावर आपकी स्क्रिप्ट के तार्किक कोड प्रवाह को बदल सकता है और / या आपकी स्क्रिप्ट बनाने वाली मान्यताओं को तोड़ सकता है। उदाहरण के लिए, एक कोड जैसे:

if [[ $1 = $2 ]]; then
   echo >&2 '$1 and $2 cannot be the same or damage will incur'
   exit 1
fi

हमलावर पास से चेक को बायपास कर सकता है '[a]' '[a]'

अब, यदि न तो वह पैटर्न मिलान करता है और न ही विभाजित + ग्लोब ऑपरेटर लागू होता है, तो एक चर को छोड़ने में क्या खतरा है?

मुझे स्वीकार करना होगा कि मैं लिखता हूं:

a=$b
case $a in...

वहां, उद्धृत करना नुकसान नहीं पहुंचाता है लेकिन सख्ती से आवश्यक नहीं है।

हालांकि, उन मामलों में उद्धरण छोड़ने का एक पक्ष प्रभाव (उदाहरण के लिए प्रश्नोत्तर में) यह है कि यह शुरुआती लोगों को एक गलत संदेश भेज सकता है: कि यह चर को उद्धृत करने के लिए ठीक नहीं है

उदाहरण के लिए, वे यह सोचना शुरू कर सकते हैं कि यदि a=$bठीक है, तो export a=$bयह भी होगा (जो कि कई गोले में नहीं है क्योंकि यह exportसूची के संदर्भ में कमांड के तर्क में है) या env a=$b

किस बारे में zsh?

zshउन डिजाइन awkwardnesses के सबसे ठीक किया। में zsh(कम से कम जब श / क्श एमुलेशन मोड में नहीं), यदि आप विभाजन या ग्लोबिंग , या पैटर्न मिलान चाहते हैं, तो आपको इसे स्पष्ट रूप से अनुरोध करना होगा: $=varविभाजित करने के लिए, और $~varग्लोब के लिए या चर की सामग्री के रूप में माना जाएगा। एक तरीका।

हालांकि, विभाजन (लेकिन ग्लोबिंग नहीं) अभी भी अंतर्निहित कमांड प्रतिस्थापन (जैसा कि echo $(cmd)) पर निहित है ।

इसके अलावा, चर को उद्धृत न करने का एक कभी-कभी अवांछित साइड इफेक्ट, खाली करने वाला निष्कासन हैzshव्यवहार क्या आप अन्य गोले में (के साथ पूरी तरह ग्लोबिंग को अक्षम करके प्राप्त कर सकते हैं के समान है set -f) और बंटवारे (साथ IFS='')। अभी भी:

cmd $var

कोई विभाजन + ग्लोब नहीं होगा , लेकिन यदि $varखाली है, तो एक खाली तर्क प्राप्त करने के बजाय, cmdकोई तर्क नहीं मिलेगा।

यह कीड़े (स्पष्ट की तरह [ -n $var ]) पैदा कर सकता है । यह संभवतः एक स्क्रिप्ट की अपेक्षाओं और मान्यताओं को तोड़ सकता है और कमजोरियों का कारण बन सकता है, लेकिन मैं अभी भी एक बहुत-बहुत-बहुत-उदाहरण के साथ नहीं आ सकता हूं)।

जब आपको विभाजन + ग्लोब ऑपरेटर की आवश्यकता होती है तो क्या होगा ?

हां, यह आम तौर पर तब होता है जब आप अपने वैरिएबल को अधूरा छोड़ना चाहते हैं। लेकिन फिर आपको यह सुनिश्चित करने की आवश्यकता है कि आप अपने विभाजन और ग्लोब ऑपरेटरों को इसे इस्तेमाल करने से पहले सही ढंग से ट्यून करें । यदि आप केवल स्प्लिट भाग चाहते हैं, न कि ग्लोब पार्ट (जो कि ज्यादातर समय होता है), तो आपको ग्लोबिंग ( set -o noglob/ set -f) को अक्षम करने और ठीक करने की आवश्यकता है $IFS। अन्यथा आप कमजोरियों का कारण बनेंगे (जैसे डेविड कोर्न के सीजीआई उदाहरण ऊपर उल्लेखित हैं)।

निष्कर्ष

संक्षेप में, गोले में अछूता एक चर (या कमांड प्रतिस्थापन या अंकगणित विस्तार) को छोड़ना विशेष रूप से गलत संदर्भों में किए जाने पर वास्तव में बहुत खतरनाक हो सकता है, और यह जानना बहुत कठिन है कि वे गलत संदर्भ कौन से हैं।

यही एक कारण है कि इसे बुरा व्यवहार क्यों माना जाता है

अब तक पढ़ने के लिए धन्यवाद। यदि यह आपके सिर पर चला जाता है, तो चिंता न करें। हर कोई अपने कोड को लिखने के सभी निहितार्थों को समझने की उम्मीद नहीं कर सकता है जिस तरह से वे इसे लिखते हैं। इसलिए हमारे पास अच्छी अभ्यास सिफारिशें हैं , इसलिए उन्हें आवश्यक समझे बिना पालन किया जा सकता है क्यों।

(और यदि यह अभी तक स्पष्ट नहीं है, तो कृपया शेल में सुरक्षा संवेदनशील कोड लिखने से बचें)।

और कृपया इस साइट पर अपने जवाब पर अपने चर को उद्धृत करें!


¹In ksh93और pdkshऔर डेरिवेटिव में, ब्रेस विस्तार भी किया जाता है जब तक कि ग्लोबिंग को अक्षम नहीं किया जाता है ( ksh93संस्करण में ksh93u + तक, जबकि braceexpandविकल्प अक्षम है)।


ध्यान दें कि, [[तुलना के केवल RHS के साथ उद्धृत करने की आवश्यकता है:if [[ $1 = "$2" ]]; then
mirabilos

2
हाँ, @mirabilos, लेकिन एलएचएस की जरूरत नहीं नहीं उद्धृत करने के लिए हो सकता है, तो वहाँ कोई बाध्यकारी कारण है वहाँ यह उद्धृत करने के लिए नहीं है (यदि हमें करने के लिए सचेत निर्णय लेने के लिए कर रहे हैं डिफ़ॉल्ट रूप से बोली के रूप में यह करने के लिए सबसे समझदार बात प्रतीत हो रहा है )। यह भी ध्यान दें कि [[ $* = "$var" ]]ऐसा नहीं है जैसे [[ "$*" = "$var" ]]कि पहले वर्ण के $IFSसाथ स्थान नहीं है bash(और यह भी mkshकि अगर $IFSखाली है, तो उस स्थिति में मुझे यकीन नहीं है कि क्या $*समान है, क्या मुझे बग उठाना चाहिए?))।
स्टीफन चेज़लस

1
हाँ, आप वहाँ डिफ़ॉल्ट रूप से उद्धृत कर सकते हैं। कृपया फ़ील्ड बंटवारे के बारे में अभी और कोई कीड़े नहीं हैं, मुझे अभी भी उन लोगों को ठीक करने के लिए मिला है जिनके बारे में (आपके और अन्य लोगों से) पहले पता है, इससे पहले कि हम इसका पुनर्मूल्यांकन कर सकें।
मिराबिलोस

2
@ बरमार, आप का मतलब है foo='bar; rm *', नहीं, यह नहीं होगा, यह हालांकि वर्तमान निर्देशिका की सामग्री को सूचीबद्ध करेगा जो एक सूचना प्रकटीकरण के रूप में गिना जा सकता है। print $fooमें ksh93(जहां printके लिए प्रतिस्थापन है echoकि अपनी कमियों में से कुछ के पते) (साथ उदाहरण के लिए हालांकि एक कोड इंजेक्शन भेद्यता है foo='-f%.0d z[0$(uname>&2)]') (आप वास्तव में जरूरत है print -r -- "$foo"echo "$foo"अभी भी गलत और सुधारी जा सकने वाली नहीं है (हालांकि आम तौर पर कम harmfull))।
स्टीफन चेज़लस

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

34

[ कैस द्वारा इस उत्तर से प्रेरित ।]

पर क्या अगर …?

लेकिन क्या होगा अगर मेरी स्क्रिप्ट उपयोग करने से पहले एक ज्ञात मूल्य पर एक चर सेट करता है? विशेष रूप से, क्या होगा अगर यह दो या अधिक संभावित मानों में से एक को चर सेट करता है (लेकिन यह हमेशा इसे किसी ज्ञात चीज के लिए सेट करता है), और किसी भी मान में स्थान या ग्लोब वर्ण नहीं होते हैं? क्या उस मामले में उद्धरण के बिना इसका उपयोग करना सुरक्षित नहीं है ?

और क्या होगा यदि संभव मानों में से एक खाली स्ट्रिंग है, और मैं "खाली करने वाले खाली" पर निर्भर हूं? यानी, यदि वैरिएबल में खाली स्ट्रिंग है, तो मैं अपने कमांड में खाली स्ट्रिंग प्राप्त नहीं करना चाहता; मुझे कुछ नहीं मिलना है। उदाहरण के लिए,

अगर कुछ
फिर
    ignorecase = "- मैं"
अन्य
    ignorecase = ""
फाई
                                        # ध्यान दें कि उपरोक्त आदेशों में उद्धरण की सख्त आवश्यकता नहीं है। 
grep $ ignorecase   other_ grep _args

मैं नहीं कह सकता ; खाली स्ट्रिंग है तो विफल हो जाएगा ।grep "$ignorecase" other_grep_args$ignorecase

उत्तर:

जैसा कि अन्य उत्तर में चर्चा की गई है, यह अभी भी विफल होगा अगर IFSइसमें एक -या एक है i। यदि आपने यह सुनिश्चित कर लिया IFSहै कि आपके चर में कोई वर्ण नहीं है (और आपको यकीन है कि आपके चर में कोई गोलाकार वर्ण नहीं है), तो यह संभवतः सुरक्षित है।

लेकिन एक तरीका है जो अधिक सुरक्षित है (हालांकि यह कुछ हद तक बदसूरत और काफी अचूक है): उपयोग ${ignorecase:+"$ignorecase"}। से POSIX शेल कमांड भाषा विनिर्देश , के तहत  2.6.2 पैरामीटर विस्तार ,

${parameter:+[word]}

    वैकल्पिक मूल्य का उपयोग करें।   यदि parameterअशांत या अशक्त है, अशक्त प्रतिस्थापित किया जाएगा; अन्यथा, word (या खाली स्ट्रिंग wordको छोड़ दिया जाता है) का विस्तार प्रतिस्थापित किया जाएगा।

चाल यहाँ है, यह है, जैसे कि हम उपयोग कर रहे हैं ignorecaseके रूप में parameter और "$ignorecase"के रूप में word। तो ${ignorecase:+"$ignorecase"}मतलब है

यदि $ignorecaseअशक्त या अशक्त (यानी, खाली), अशक्त (यानी, कुछ नहीं ) को प्रतिस्थापित किया जाएगा; अन्यथा, का विस्तार "$ignorecase"प्रतिस्थापित किया जाएगा।

यह हमें वहां जाता है जहां हम जाना चाहते हैं: यदि चर को खाली स्ट्रिंग पर सेट किया जाता है, तो इसे "हटा दिया" जाएगा (यह संपूर्ण, जटिल अभिव्यक्ति कुछ भी नहीं करने के लिए मूल्यांकन करेगी - यहां तक ​​कि एक खाली स्ट्रिंग भी नहीं), और यदि चर एक गैर नहीं है -मोटे मूल्य, हमें वह मूल्य मिलता है, उद्धृत किया गया।


पर क्या अगर …?

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

उदाहरण के लिए,

अगर कुछ
फिर
    मानदंड = "- एफ टाइप करें"
अन्य
    मापदंड = ""
फाई
अगर कुछ
फिर
    मानदंड = "$ मानदंड-समय +42"
फाई
"$ start_directory" $ मानदंड   other_ खोजें _args

उत्तर:

आप सोच सकते हैं कि यह प्रयोग करने का मामला है eval  नहीं!   यहां तक ​​कि उपयोग करने के बारे में सोचने के लिए प्रलोभन का विरोध evalकरें।

फिर, यदि आपने सुनिश्चित किया है कि IFSआपके चर में कोई भी वर्ण नहीं है (रिक्त स्थान को छोड़कर, जिसे आप सम्मानित किया जाना चाहते हैं), और आप सुनिश्चित हैं कि आपके चर में कोई भी गोलाकार पात्र नहीं है, तो ऊपर शायद है सुरक्षित।

लेकिन, यदि आप bash (या ksh, zsh या yash) का उपयोग कर रहे हैं, तो एक तरीका है जो अधिक सुरक्षित है: एक सरणी का उपयोग करें:

अगर कुछ
फिर
    मानदंड = (- टाइप एफ) # आप कह सकते हैं मानदंड = ("- टाइप" "एफ") `, लेकिन यह वास्तव में अनावश्यक है।
अन्य
    मानदंड = () # इस आदेश पर किसी भी उद्धरण का उपयोग  करें!
फाई
अगर कुछ
फिर
    मापदंड + = (- mtime +42) # नोट: किसी सरणी में जोड़ने के लिए (परिशिष्ट) जोड़ने के लिए `=` नहीं, बल्कि ` + =`।
फाई
लगता है "$ start_directory" "$ {मापदंड [@]}"   other_ लगता है _args

से बैश (1) ,

सरणी के किसी भी तत्व का उपयोग करके संदर्भित किया जा सकता है । ... यदि है या  , इस शब्द का विस्तार सभी सदस्यों के लिए है । ये सदस्यता केवल तभी भिन्न होती है जब शब्द दोहरे उद्धरण चिह्नों में प्रकट होता है। यदि शब्द डबल-उद्धृत किया गया है, तो… एक अलग शब्द के प्रत्येक तत्व का विस्तार करता है ।${name[subscript]}subscript@*name${name[@]}name

इसलिए "${criteria[@]}"(प्रत्येक उपर्युक्त उदाहरण में) criteriaप्रत्येक उद्धरण के शून्य, दो या चार तत्वों का विस्तार करता है । विशेष रूप से, यदि स्थितिcriteria में से कोई भी  सत्य नहीं है, तो सरणी में कोई सामग्री नहीं है (जैसा कि criteria=()कथन द्वारा निर्धारित किया गया है), और कुछ भी"${criteria[@]}" करने के लिए मूल्यांकन करता है (एक असुविधाजनक खाली स्ट्रिंग भी नहीं)।


यह विशेष रूप से दिलचस्प और जटिल हो जाता है जब आप कई शब्दों के साथ काम कर रहे होते हैं, जिनमें से कुछ गतिशील (उपयोगकर्ता) इनपुट होते हैं, जिन्हें आप पहले से नहीं जानते हैं, और इसमें स्पेस (एस) या अन्य विशेष चरित्र (एस) हो सकते हैं। विचार करें:

प्रिंटफ़ "देखने के लिए फ़ाइल नाम दर्ज करें:"
पढ़ें
अगर ["$ fname"! = ""]
फिर
    मापदंड + = (- "नाम" $ fname ")
फाई

ध्यान दें कि $fnameउद्धृत किया गया है प्रत्येक बार यह प्रयोग किया जाता है। यह तब भी काम करता है जब उपयोगकर्ता कुछ इस तरह से प्रवेश करता है foo barया foo*"${criteria[@]}"का मूल्यांकन करता है -name "foo bar"या -name "foo*"। (याद रखें कि सरणी का प्रत्येक तत्व उद्धृत है।)

सभी POSIX गोले में ऐरे काम नहीं करता है; सरणियाँ एक ksh / bash / zsh / yash-ism हैं। सिवाय ... वहाँ एक सरणी है कि सभी गोले समर्थन करते हैं: तर्क सूची, उर्फ "$@"। यदि आप उस तर्क सूची के साथ किए जाते हैं जिसे आप के साथ लागू किया गया था (उदाहरण के लिए, आपने सभी "स्थितिगत मापदंडों" (तर्कों) को चर में कॉपी किया है, या अन्यथा उन्हें संसाधित किया गया है), आप एक सरणी के रूप में arg सूची का उपयोग कर सकते हैं:

अगर कुछ
फिर
    set - -type f # आप कह सकते हैं `set -" -type "" f "`, लेकिन यह वास्तव में अनावश्यक है।
अन्य
    सेट --
फाई
अगर कुछ
फिर
    सेट - "$ @" -टाइम +42
फाई
# इसी तरह - सेट - "$ @" -नेमी "$ fname"
"$ start_directory" "$ @"   अन्य_ ढूंढें _args

"$@"निर्माण (जो, ऐतिहासिक, पहले आया था) के रूप में ही अर्थ विज्ञान है - यह प्रत्येक तर्क का विस्तार (यानी, तर्क सूची के प्रत्येक तत्व) एक अलग शब्द के लिए, के रूप में कि आप लिखकर ।"${name[@]}""$1" "$2" "$3" …

से Excerpting POSIX शेल कमांड भाषा विनिर्देश , के तहत 2.5.2 विशेष पैरामीटर ,

@

    एक से शुरू होने वाले स्थितीय मापदंडों का विस्तार करता है, शुरू में प्रत्येक स्थितीय पैरामीटर के लिए एक क्षेत्र का निर्माण करता है जो सेट होता है। ..., प्रारंभिक फ़ील्ड को अलग फ़ील्ड के रूप में बनाए रखा जाएगा, ...। यदि कोई स्थितिगत पैरामीटर नहीं हैं, तो डबल-कोट्स के भीतर @भी, शून्य क्षेत्र का विस्तार होगा @; ...

पूरा पाठ कुछ गूढ़ है; प्रमुख बिंदु यह है कि यह निर्दिष्ट करता है कि "$@"जब कोई स्थितिगत पैरामीटर नहीं है, तो शून्य क्षेत्र उत्पन्न करेगा। ऐतिहासिक नोट: जब "$@"पहली बार 1979 में बॉर्न शेल (बैश करने के लिए पूर्ववर्ती) में पेश किया गया था, तो इसमें एक बग "$@"था जिसे एकल रिक्त स्ट्रिंग द्वारा बदल दिया गया था जब कोई स्थितिगत पैरामीटर नहीं थे; देखें कि शेल स्क्रिप्ट में क्या ${1+"$@"}मतलब है, और यह कैसे अलग है "$@"? पारंपरिक बॉर्न शेल परिवारक्या ${1+"$@"}मतलब है ... और यह कहाँ आवश्यक है? , और "$@"बनाम${1+"$@"}


पहली स्थिति में भी मदद मिलती है:

अगर कुछ
फिर
    ignorecase = (- i) # आप कह सकते हैं `ignorecase = (" - i ")`, लेकिन यह वास्तव में अनावश्यक है।
अन्य
    ignorecase = () # इस आदेश पर किसी भी उद्धरण का उपयोग  करें!
फाई
grep "$ {ignorecase [@]}"   other_ grep _args

____________________

PS (csh)

यह कहने के बिना जाना चाहिए, लेकिन, उन लोगों के लाभ के लिए जो यहां नए हैं: csh, tsh, आदि, Bourne / POSIX गोले नहीं हैं। वे एक अलग परिवार हैं। एक अलग रंग का एक घोड़ा। एक पूरी दूसरी गेंद का खेल। बिल्ली की एक अलग नस्ल। एक और पंख के पक्षी। और, सबसे विशेष रूप से, कीड़े का एक अलग कैन।

इस पृष्ठ पर जो कुछ कहा गया है, वह csh पर लागू होता है; जैसे: यह आपके सभी चर को उद्धृत करने के लिए एक अच्छा विचार है जब तक कि आपके पास एक अच्छा कारण नहीं है, और आपको यकीन है कि आप जानते हैं कि आप क्या कर रहे हैं। लेकिन, csh में, प्रत्येक चर एक सरणी है - यह सिर्फ इतना होता है कि लगभग हर चर केवल एक तत्व का एक सरणी है, और बॉर्न / पोसिक्स गोले में एक साधारण शेल चर के समान कार्य करता है। और वाक्यविन्यास भयानक रूप से अलग है (और मेरा मतलब है कि भयानक रूप से )। इसलिए हम यहां csh-family गोले के बारे में अधिक नहीं कहेंगे।


1
ध्यान दें कि में csh, यदि आप इसके बजाय का उपयोग करेंगे $var:qकी तुलना में "$var"के रूप में बाद के चर कि न्यू लाइन वर्ण हो के लिए काम नहीं करता है (या आप व्यक्तिगत रूप से सरणी के तत्वों को उद्धृत करने के बजाय एक भी बहस में रिक्त स्थान के साथ उन्हें शामिल होने चाहते हैं)।
स्टीफन चेज़लस

बाश में, के bitrate="--bitrate 320"साथ काम करता है ${bitrate:+$bitrate}, और bitrate=--bitrate 128काम नहीं करेगा ${bitrate:+"$bitrate"}क्योंकि यह कमांड को तोड़ता है। क्या ${variable:+$variable}कोई उपयोग नहीं करना सुरक्षित है "?
फ्रीडो

@ फ़्रीडो: मुझे आपकी टिप्पणी अस्पष्ट लगी। मैं विशेष रूप से अस्पष्ट हूं कि आप क्या जानना चाहते हैं कि मैंने पहले ही नहीं कहा है। दूसरा देखें "लेकिन क्या होगा अगर" शीर्षक - "लेकिन क्या होगा अगर मेरे पास एक चर है जो मुझे चाहिए / शब्दों में विभाजित होने की आवश्यकता है?" यही आपकी स्थिति है, और आपको वहां सलाह का पालन करना चाहिए। लेकिन अगर आप नहीं कर सकते हैं (जैसे, क्योंकि आप बैश, ksh, zsh या यश के अलावा किसी अन्य शेल का उपयोग कर रहे हैं, और आप $@किसी और चीज़ के लिए उपयोग कर रहे हैं  ), या आप बस अन्य कारणों से एक सरणी का उपयोग करने से इनकार करते हैं, तो मैं संदर्भित करता हूं आप के रूप में "अन्य उत्तर में चर्चा की"। … (Cont'd)
जी-मैन

(जारी) ... आपका सुझाव (का उपयोग कर ${variable:+$variable}कोई साथ ") असफल हो आईएफएस शामिल होगा  -,  0, 2, 3, a, b, e, i, rया  t
जी-मैन

वैसे मेरे वैरिएबल में a, e, b, i 2 और 3 अक्षर हैं और फिर भी यह ठीक काम करता है${bitrate:+$bitrate}
Freedo

11

मुझे स्टीफन के जवाब पर संदेह था, हालांकि इसका दुरुपयोग करना संभव है $#:

$ set `seq 101`

$ IFS=0

$ echo $#
1 1

या $ ?:

$ IFS=0

$ awk 'BEGIN {exit 101}'

$ echo $?
1 1

ये आकस्मिक उदाहरण हैं, लेकिन क्षमता मौजूद नहीं है।

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