प्रतिनिधि इंटरव्यू विषय

Go कोडिंग इंटरव्यू: os.Root का उपयोग करके अविश्वसनीय फ़ाइल नामों को सुरक्षित रूप से कैसे संभालेंगे?

कोडिंगमध्यम
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

एक आर्काइव एक्सट्रैक्टर अविश्वसनीय फ़ाइल नाम प्राप्त करता है और उसे प्रत्येक आउटपुट को एक ही डायरेक्टरी के अंतर्गत रखना चाहिए। Go 1.24+ का उपयोग करके, सुरक्षित कार्यान्वयन डिज़ाइन करें और .., सिम्लिंक, TOCTOU, रिसोर्स क्लीनअप और क्रॉस-प्लेटफ़ॉर्म सीमाओं की व्याख्या करें।

प्रॉम्प्ट और दायरा

एक आर्काइव एक्सट्रैक्टर उपयोगकर्ता द्वारा अपलोड की गई फ़ाइलों को /srv/uploads/job-42 में लिखता है। आर्काइव एंट्री के नाम पूरी तरह से कॉलर द्वारा नियंत्रित होते हैं, जो ../../etc/passwd, एक पूर्ण पथ (absolute path), या एक सिम्लिंक सबमिट कर सकता है जो जॉब डायरेक्टरी के बाहर इंगित करता है। सेवा को यह सुनिश्चित करते हुए डायरेक्टरी और फ़ाइलें बनानी होंगी कि प्रत्येक ऑपरेशन जॉब डायरेक्टरी के अंदर ही रहे।

Go 1.24 या बाद के संस्करण का उपयोग करें। प्रोडक्शन में सामान्य एप्लिकेशन विशेषाधिकार और Linux मानकर चलें, जिसमें Windows या WASI क्लाइंट की संभावना हो। एक अविश्वसनीय फ़ाइल नाम और उस कॉलर के बीच अंतर करें जिसे स्पष्ट रूप से किसी भी आउटपुट डायरेक्टरी को चुनने की अनुमति है।

इंटरव्यूअर क्या जांच रहा है

  • क्या आप स्ट्रिंग रिप्लेसमेंट करने के बजाय डायरेक्टरी-प्रतिबंधित API चुनने से पहले रूट और हमलावर-नियंत्रित इनपुट को परिभाषित करते हैं।
  • क्या आप जानते हैं कि os.Root रूट से बाहर निकलने वाले .. और सिम्लिंक्स को अस्वीकार करता है, और यह गारंटी filepath.Clean से कहाँ भिन्न है।
  • क्या आप EvalSymlinks से जांच करने और बाद में खोलने में TOCTOU विंडो को पहचानते हैं।
  • क्या आप Root, फ़ाइल डिस्क्रिप्टर, अस्थायी फ़ाइलों, क्लीनअप और अनुमति नीति को संभालते हैं।
  • क्या आप बाइंड माउंट्स, GOOS=js, WASI कार्यान्वयन और अत्यधिक गहरे पाथ से जुड़ी सीमाओं का उल्लेख करते हैं।

पहले स्पष्ट करने योग्य प्रश्न

  1. क्या ऑपरेशन पढ़ना (read), लिखना (write), या दोनों है? केवल-पठन एक्सेस और निर्माण के लिए अलग-अलग OpenFile फ़्लैग और अनुमतियों की आवश्यकता होती है।
  2. क्या रूट सेवा द्वारा तय किया गया है या कॉलर द्वारा चुना गया है? यदि कॉलर कोई भी डायरेक्टरी चुन सकता है, तो os.Root कोई अतिरिक्त सैंडबॉक्स सीमा नहीं है।
  3. क्या आर्काइव सिम्लिंक्स, हार्ड लिंक्स, डिवाइस फ़ाइलें, या नाम बदलने (renames) की अनुमति दे सकता है? अस्वीकृत प्रकारों को Root पर सौंपने के बजाय आर्काइव नीति द्वारा अस्वीकार किया जाना चाहिए।
  4. क्या GOOS=js या WASI टारगेट दायरे में हैं? आधिकारिक सामग्री Unix डिस्क्रिप्टर-आधारित कार्यान्वयन से भिन्न पाथ-सुरक्षा गारंटियों का वर्णन करती है।

30-सेकंड उत्तर ढांचा

“मैं जॉब डायरेक्टरी को एकमात्र रूट बनाता हूँ। प्रत्येक आर्काइव नाम को os.Root में एक रिलेटिव नाम के रूप में पास किया जाता है; मैं इसे बेस स्ट्रिंग के साथ जोड़कर सामान्य os.Create को कॉल नहीं करता। निर्माण, खोलना और हटाना Root विधियों के माध्यम से होता है, इसलिए .. और एस्केप करने वाले सिम्लिंक विफल हो जाते हैं। मैं प्रतिबंधित अनुमतियों के साथ एक अस्थायी नाम के माध्यम से लिखता हूँ, फिर उत्पाद नीति के अनुसार कमिट करता हूँ। परीक्षण ट्रैवर्सल, सिम्लिंक रेस, समवर्ती निर्माण, Windows डिवाइस नाम और रूट को बंद करने को कवर करते हैं। Root पाथ को सीमित करता है, लेकिन यह आर्काइव-प्रकार की जांच, विशेषाधिकार अलगाव, या बाइंड-माउंट नियंत्रणों की जगह नहीं लेता है।”

चरण-दर-चरण गहन विश्लेषण

1. सुरक्षा इनवेरिएंट तय करें

“नाम में .. शामिल नहीं है” कोई सुरक्षा परिभाषा नहीं है। एक हमलावर सिम्लिंक का उपयोग कर सकता है या जांच और खोलने के बीच डायरेक्टरी एंट्री को बदल सकता है। इनवेरिएंट को सटीक रूप से बताएं: आर्काइव नाम से प्राप्त प्रत्येक फ़ाइल सिस्टम ऑपरेशन जॉब रूट के भीतर हल (resolve) होता है, और जब इसे साबित नहीं किया जा सकता है तो ऑपरेशन विफल हो जाता है।

Go 1.24 का os.OpenRoot एक रूट डायरेक्टरी खोलता है। विधियाँ जैसे कि Root.Open, Root.Create, Root.OpenFile, Root.Mkdir, और Root.Stat उस रूट के सापेक्ष नाम स्वीकार करती हैं। कार्यान्वयन रूट के बाहर .. और सिम्लिंक ट्रैवर्सल को अस्वीकार करता है। कार्य के लिए एक रूट का पुन: उपयोग करें, और सभी फ़ाइल हैंडल बंद होने के बाद इसे बंद करें।

go
func writeEntry(rootDir, name string, data []byte) error {
    root, err := os.OpenRoot(rootDir)
    if err != nil {
        return err
    }
    defer root.Close()

    f, err := root.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
    if err != nil {
        return err
    }
    defer f.Close()

    _, err = f.Write(data)
    return err
}

यह नाम समाधान (name resolution) को बाहर निकलने से रोकता है, लेकिन प्रोडक्शन कोड को अभी भी फ़ाइल आकार, एंट्री गणना और डायरेक्टरी की गहराई पर सीमाओं की आवश्यकता होती है, साथ ही लिखने में विफलता के बाद अधूरे आउटपुट को हटाने की आवश्यकता होती है। O_EXCL का अर्थ है “मौजूदा फ़ाइल को ओवरराइट न करें”; यह आर्काइव डिडुप्लीकेशन या व्यावसायिक इडेम्पोटेंसी नहीं है।

2. बताएं कि स्ट्रिंग सैनिटाइजेशन अपर्याप्त क्यों है

filepath.Clean a/../b को सामान्य (normalize) कर सकता है, लेकिन यह साबित नहीं कर सकता कि a रूट के बाहर एक सिम्लिंक नहीं है। EvalSymlinks के साथ जांच करने और बाद में खोलने में अभी भी एक TOCTOU विंडो होती है: जांच के बाद एक डायरेक्टरी एंट्री बदल सकती है। filepath.Join के बाद os.Create कोई डायरेक्टरी सीमा प्रदान नहीं करता है।

Unix पर, Root एक रूट डायरेक्टरी डिस्क्रिप्टर और प्रतिबंधित रिलेटिव ओपन का उपयोग करता है, इसलिए सीमा एक अलग जांच पर निर्भर होने के बजाय ऑपरेशन में भाग लेती है। रूट-आंतरिक रिलेटिव पाथ और सिम्लिंक की अनुमति है; ../ या एक निरपेक्ष सिम्लिंक जो बाहर निकलता है, उसे विफल होना चाहिए। कॉलर को अभी भी आर्काइव में सिम्लिंक प्रविष्टियों को अस्वीकार करना चाहिए जब तक कि उत्पाद स्पष्ट रूप से उनका समर्थन न करे।

3. आर्काइव प्रकार और राइट्स को संभालें

लिखने से पहले प्रत्येक प्रविष्टि के प्रकार, आकार, अनुमतियों और नाम को पढ़ें। डिवाइस फ़ाइलों, FIFOs, हार्ड लिंक्स और सिम्लिंक्स को अस्वीकार करें जिनका उत्पाद समर्थन नहीं करता है। Root.Mkdir या MkdirAll के साथ पैरेंट डायरेक्टरी बनाएं, फिर Root.OpenFile के साथ सामान्य फ़ाइलें बनाएं। कुल बाइट्स, प्रति-फ़ाइल बाइट्स, पाथ घटकों और समवर्ती जॉब्स के लिए बजट निर्धारित करें।

एक सुरक्षित लेखन क्रम है “अस्थायी नाम → पूर्ण लेखन → सत्यापन → परमाणु कमिट (atomic commit)।” अस्थायी नाम भी Root के माध्यम से बनाया जाना चाहिए; सिस्टम अस्थायी डायरेक्टरी में लिखना और डायरेक्टरीज़ में स्थानांतरित करना अनुमति, माउंट और परमाणुता की धारणाओं को बदल देता है। यदि उपलब्ध Root API आवश्यक रीनेम व्यवहार प्रदान नहीं कर सकता है, तो अप्रतिबंधित पाथ ऑपरेशनों पर चुपचाप निर्भर होने के बजाय इसे एक प्रलेखित प्लेटफ़ॉर्म या उत्पाद बाधा बनाएं।

4. प्लेटफ़ॉर्म और विशेषाधिकार सीमाओं का उल्लेख करें

Go सामग्री कहती है कि Unix कार्यान्वयन सामान्य रूप से रूट डायरेक्टरी डिस्क्रिप्टर को ट्रैक करते हैं, इसलिए एक रीनेम किया गया रूट अभी भी मूल डायरेक्टरी को संदर्भित करता है। Windows एक हैंडल का उपयोग करता है और कुछ आरक्षित डिवाइस नामों को ब्लॉक करता है। GOOS=js में openat परिवार के कॉल्स का अभाव है, जिससे सिम्लिंक सत्यापन में TOCTOU सीमा छूट जाती है; WASI की गारंटी इसके कार्यान्वयन पर निर्भर करती है। Root Linux बाइंड माउंट्स, /proc विशेष फ़ाइलों, या Unix डिवाइस-फ़ाइल एक्सेस को भी ब्लॉक नहीं करता है।

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

5. निष्पादन योग्य सत्यापन (Executable Verification) बनाएं

../escape, पूर्ण पाथ, रूट-आंतरिक सिम्लिंक्स, रूट के बाहर सिम्लिंक्स, ../ में समाप्त होने वाले नाम, एक फ़ाइल का समवर्ती निर्माण, अधिक आकार वाली प्रविष्टियां, और रूट को बंद करने के बाद कॉल्स का परीक्षण करें। Go 1.24.3 ने एक ऐसे मामले को ठीक किया जहां ../ में समाप्त होने वाला Root पाथ पैरेंट डायरेक्टरी को खोल सकता था, इसलिए CI को उस फिक्स वाले टूलचेन को पिन करना चाहिए और उस रिग्रेशन टेस्ट को बनाए रखना चाहिए।

Linux पर, स्वीकृत और अस्वीकृत दोनों मामलों के लिए अस्थायी डायरेक्टरी और वास्तविक सिम्लिंक्स का उपयोग करें। साझा-स्थिति रेस (shared-state races) के लिए -race का उपयोग करें, लेकिन इसे फ़ाइल सिस्टम सीमा के प्रमाण के रूप में न मानें। एक क्रॉस-कंपाइल की गई बाइनरी समान कर्नेल गारंटी साबित नहीं करती है; परीक्षण मैट्रिक्स में प्रत्येक GOOS को शामिल करें।

उच्च गुणवत्ता वाला नमूना उत्तर

“मैं जॉब डायरेक्टरी को एक निश्चित रूट बनाता हूँ और अविश्वसनीय आर्काइव नामों को केवल os.Root को दिए गए रिलेटिव पाथ के रूप में अनुमति देता हूँ। एक्सट्रैक्टर डिवाइस फ़ाइलों, हार्ड लिंक्स और असमर्थित सिम्लिंक्स को अस्वीकार करता है, और प्रविष्टियों, बाइट्स और डायरेक्टरी की गहराई को सीमित करता है। यह Root के माध्यम से पैरेंट डायरेक्टरी और नियमित फ़ाइलें बनाता है, उसी रूट के तहत एक अस्थायी नाम पर लिखता है, और विफलता पर अधूरे आउटपुट को हटा देता है। .. और बाहर निकलने वाले सिम्लिंक्स को प्रतिबंधित ओपन द्वारा ही अस्वीकार कर दिया जाता है, जिससे जांच-फिर-खोलने वाली TOCTOU विंडो से बचा जा सकता है।

मैं इसे एक पूर्ण सैंडबॉक्स नहीं कहूँगा: बाइंड माउंट्स, विशेषाधिकार, कंटेनर माउंट्स और आर्काइव नीति अलग रहती हैं। Linux परीक्षण ट्रैवर्सल, डुप्लिकेट निर्माण, क्लीनअप और ट्रेलिंग ../ के लिए वास्तविक सिम्लिंक्स और समवर्ती राइट्स का उपयोग करते हैं; Windows, WASI और GOOS=js को स्पष्ट सीमा परीक्षण मिलते हैं। यदि उत्पाद कॉलर द्वारा चुनी गई किसी मनमानी डायरेक्टरी की अनुमति देता है, तो मैं झूठी Root धारणा को हटा देता हूँ और एक स्पष्ट विशेषाधिकार और ऑडिट नीति का उपयोग करता हूँ।”

सामान्य गलतियाँ

  • लक्षण: filepath.Join(base, name) के बाद सामान्य os.Createयह क्यों विफल होता है: .. और सिम्लिंक base के बाहर हल हो सकते हैं → सुधार: रूट को ठीक करें और प्रत्येक ऑपरेशन को Root के माध्यम से करें।
  • लक्षण: EvalSymlinks के साथ जांचें, फिर सामान्य रूप से खोलें → यह क्यों विफल होता है: एक TOCTOU विंडो बनी रहती है → सुधार: प्रतिबंध को खोलने का हिस्सा बनाएं और रेस सीमा का परीक्षण करें।
  • लक्षण: दावा करना कि os.Root प्रत्येक फ़ाइल सिस्टम एस्केप को रोकता है → यह क्यों विफल होता है: बाइंड माउंट, डिवाइस फ़ाइलें और विशेषाधिकार उस API गारंटी से बाहर हैं → सुधार: माउंट, विशेषाधिकार और आर्काइव-प्रकार नियंत्रण जोड़ें।
  • लक्षण: GOOS=js, WASI और पैच संस्करणों की अनदेखी करना → यह क्यों विफल होता है: प्लेटफ़ॉर्म कार्यान्वयन और सुरक्षा सुधार भिन्न होते हैं → सुधार: टूलचेन को पिन करें और एक क्रॉस-प्लेटफ़ॉर्म रिग्रेशन मैट्रिक्स बनाएं।
  • लक्षण: केवल सफल निष्कर्षण का परीक्षण करना → यह क्यों विफल होता है: अस्वीकृत एस्केप और क्लीनअप द्वारा पाथ सुरक्षा का प्रमाण मिलता है → सुधार: ट्रैवर्सल, पूर्ण नाम, सिम्लिंक, टकराव, सीमाएं और बंद-रूट कॉल्स का परीक्षण करें।

फॉलो-अप प्रश्न और उत्तर

फॉलो-अप 1: क्या आर्काइव में रूट-आंतरिक सिम्लिंक की अनुमति दी जानी चाहिए?

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

फॉलो-अप 2: आप Linux बाइंड माउंट्स को कैसे संभालते हैं?

Root बाइंड-माउंट अलगाव प्रदान नहीं करता है। कंटेनर या होस्ट माउंट नीति के माध्यम से जॉब डायरेक्टरी को एक समर्पित फ़ाइल सिस्टम पर रखें, एप्लिकेशन विशेषाधिकारों को प्रतिबंधित करें, और परिनियोजन (deployment) जांच में अविश्वसनीय माउंट्स को अस्वीकार करें। यदि थ्रेट मॉडल में एक विशेषाधिकार प्राप्त हमलावर शामिल है, तो अधिक स्ट्रिंग जांच जोड़ने के बजाय एक मजबूत सैंडबॉक्स या एक पृथक वर्कर का उपयोग करें।

फॉलो-अप 3: सिस्टम अस्थायी डायरेक्टरी में एक अस्थायी फ़ाइल क्यों न लिखें और इसे रूट में ले जाएं?

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

फॉलो-अप 4: क्या GOOS=js में समान सुरक्षा होने का दावा किया जा सकता है?

नहीं। आधिकारिक व्याख्या कहती है कि उस टारगेट में openat परिवार का अभाव है और सिम्लिंक सत्यापन में TOCTOU सीमा है। उस टारगेट के लिए सुविधा को प्रतिबंधित करें, रनटाइम के सैंडबॉक्स पर भरोसा करें, या अविश्वसनीय-फ़ाइल प्रसंस्करण को मजबूत डायरेक्टरी बाधाओं वाले सर्वर पर ले जाएं। API और दस्तावेज़ीकरण को इस अंतर को स्पष्ट करना चाहिए।

सार्वजनिक स्रोत

संबंधित प्रश्न

संबंधित इंटरव्यू टूल

कोडिंग प्रॉम्प्ट के लिए स्क्रीनशॉट का उपयोग करें

समस्या को कैप्चर करें, फिर क्रम से प्रतिबंधों (constraints), समाधान, कोड, एज केस और जटिलता पर काम करें।

टूल देखें