असुरक्षित तरीके से डीसीरियलाइज़ेशन

OWASP कैटगरी: MASVS-CODE: कोड क्वालिटी

खास जानकारी

बड़ी मात्रा में Java ऑब्जेक्ट डेटा को सेव या ट्रांसफ़र करते समय, डेटा को पहले क्रम से लगाना ज़्यादा बेहतर होता है. इसके बाद, डेटा को पाने वाले ऐप्लिकेशन, गतिविधि या सेवा देने वाली कंपनी, डेटा को डिसीरियलाइज़ेशन की प्रोसेस से गुज़रेगी. सामान्य परिस्थितियों में, डेटा को क्रम से लगाया जाता है और फिर उपयोगकर्ता के किसी भी रुकावट के बिना डीसीरियलाइज़ (पार्स) किया जाता है. हालांकि, डीसीरियलाइज़ेशन प्रोसेस और इसके मनचाहे ऑब्जेक्ट के बीच भरोसे के रिश्ते का गलत इस्तेमाल, कोई नुकसान पहुंचाने वाला व्यक्ति कर सकता है. उदाहरण के लिए, कोई ऐसा व्यक्ति जो सीरियल नंबर वाली चीज़ों को बीच में रोक सकता है या उनमें बदलाव कर सकता है. इससे नुकसान पहुंचाने वाले व्यक्ति को, सेवा में रुकावट (DoS), विशेषाधिकार बढ़ाने, और रिमोट कोड चलाने (आरसीई) जैसे हमले करने में मदद मिलेगी.

Serializable क्लास, सीरियलाइज़ेशन को मैनेज करने का एक सामान्य तरीका है. हालांकि, Android में सीरियलाइज़ेशन को मैनेज करने के लिए, Parcel नाम की एक क्लास भी है. Parcel क्लास का इस्तेमाल करके, ऑब्जेक्ट डेटा को बाइट स्ट्रीम डेटा में क्रम से लगाया जा सकता है. साथ ही, Parcelable इंटरफ़ेस का इस्तेमाल करके, उसे Parcel में पैक किया जा सकता है. इससे Parcel को ज़्यादा आसानी से ट्रांसपोर्ट या स्टोर किया जा सकता है.

हालांकि, Parcel क्लास का इस्तेमाल करते समय सावधानी बरतनी चाहिए, क्योंकि इसे बेहतर तरीके से काम करने वाली आईपीसी ट्रांसपोर्ट तकनीक के तौर पर बनाया गया है. हालांकि, इसका इस्तेमाल लोकल परसिस्टेंट स्टोरेज में सीरियल वाली चीज़ों को स्टोर करने के लिए नहीं किया जाना चाहिए. इससे डेटा के साथ काम करने से जुड़ी समस्याएं आ सकती हैं या ऐसा हो सकता है. जब डेटा को पढ़ने की ज़रूरत होती है, तो Parcelable इंटरफ़ेस का इस्तेमाल करके Parcel को सीरियल से डीसीरियल किया जा सकता है और उसे फिर से ऑब्जेक्ट डेटा में बदला जा सकता है.

Android में डेसिरियलाइज़ेशन का गलत इस्तेमाल करने के लिए, तीन मुख्य तरीके हैं:

  • डेवलपर की गलत धारणा का फ़ायदा उठाना कि कस्टम क्लास टाइप से आगे बढ़ने वाले ऑब्जेक्ट को डिससिरियलाइज़ करना सुरक्षित है. असल में, किसी भी क्लास से सोर्स किए गए किसी भी ऑब्जेक्ट को नुकसान पहुंचाने वाले कॉन्टेंट से बदला जा सकता है. सबसे खराब स्थिति में, यह उसी या दूसरे ऐप्लिकेशन के क्लास लोडर में रुकावट डाल सकता है. यह इंटरफ़ियरेंस, खतरनाक वैल्यू को इंजेक्ट करने के तौर पर होता है. क्लास के मकसद के हिसाब से, इन वैल्यू की वजह से डेटा का ऐक्सेस किसी दूसरे के पास जा सकता है या खाता हैक हो सकता है.
  • डीसीरियलाइज़ेशन के उन तरीकों का गलत इस्तेमाल करना जिन्हें डिज़ाइन के हिसाब से असुरक्षित माना जाता है. उदाहरण के लिए, CVE-2023-35669, लोकल प्रिविलेज एस्कलेशन से जुड़ी गड़बड़ी. इसकी वजह से डीप-लिंक डीसीरियलाइज़ेशन वेक्टर का इस्तेमाल करके आर्बिट्रेरी JavaScript कोड इंजेक्शन को अनुमति दी जाती है
  • ऐप्लिकेशन लॉजिक में मौजूद गड़बड़ियों का फ़ायदा उठाना. उदाहरण के लिए, CVE-2023-20963, एक स्थानीय विशेषाधिकार बढ़ाने वाली गड़बड़ी, जिसकी वजह से ऐप्लिकेशन को Android के WorkSource पार्सल लॉजिक में मौजूद गड़बड़ी की मदद से, खास सुविधाओं वाले एनवायरमेंट में कोड डाउनलोड करने और उसे चलाने की अनुमति मिली.

असर

ऐसा कोई भी ऐप्लिकेशन जो अविश्वसनीय या नुकसान पहुंचाने वाले सीरियलाइज़ किए गए डेटा को डीसीरियलाइज़ करता है, वह रिमोट कोड को चलाने या सेवा में रुकावट डालने वाले हमलों का शिकार हो सकता है.

जोखिम: भरोसेमंद न होने वाले इनपुट को डिसीरियलाइज़ करना

हमलावर, ऐप्लिकेशन के लॉजिक में पार्सल की पुष्टि न होने का फ़ायदा उठा सकता है. ऐसा करके, वह ऐप्लिकेशन में मनमुताबिक ऑब्जेक्ट इंजेक्ट कर सकता है. डिसइरियलाइज़ होने के बाद, ये ऑब्जेक्ट ऐप्लिकेशन को नुकसान पहुंचाने वाले कोड को चलाने के लिए मजबूर कर सकते हैं. इससे, सेवा का अस्वीकार होना (डीओएस), विशेषाधिकार बढ़ाना, और रिमोट कोड एक्सीक्यूशन (आरसीई) जैसी समस्याएं हो सकती हैं.

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

जोखिम कम करने के तरीके

सबसे सही तरीका यह है कि मान लें कि किसी खास क्रम में मौजूद डेटा पर भरोसा नहीं किया जा सकता और वह खतरनाक हो सकता है. यह पक्का करने के लिए कि क्रम से लगाए गए डेटा का रखरखाव सही है, डेटा की पुष्टि करने के लिए जांच करें. इससे यह पक्का हो सकेगा कि ऐप्लिकेशन में इस्तेमाल किए जाने वाले क्लास और फ़ॉर्मैट सही हैं.

इसका एक संभावित समाधान यह हो सकता है कि java.io.ObjectInputStream लाइब्रेरी के लिए, पहले से तैयार पैटर्न को लागू किया जाए. डिसर्रियलाइज़ेशन के लिए ज़िम्मेदार कोड में बदलाव करके, यह पक्का किया जा सकता है कि इंटेंट में सिर्फ़ क्लास का साफ़ तौर पर बताया गया सेट ही डिसर्रियलाइज़ किया जाए.

Android 13 (एपीआई लेवल 33) के बाद, Intent क्लास में कई तरीके अपडेट किए गए हैं. इन्हें पार्सल मैनेज करने के लिए, पुराने और अब काम न करने वाले तरीकों के मुकाबले ज़्यादा सुरक्षित माना जाता है. getParcelableExtra(java.lang.String, java.lang.Class) और getParcelableArrayListExtra(java.lang.String, java.lang.Class) जैसे नए और सुरक्षित तरीके, डेटा टाइप की जांच करते हैं. इससे, डेटा टाइप के मेल न खाने की कमजोरियों का पता चलता है. इनकी वजह से, ऐप्लिकेशन क्रैश हो सकते हैं. साथ ही, इनका इस्तेमाल प्रिविलेज एस्कलेशन अटैक करने के लिए भी किया जा सकता है. जैसे, CVE-2021-0928.

इस उदाहरण में बताया गया है कि Parcel क्लास के सुरक्षित वर्शन को कैसे लागू किया जा सकता है:

मान लें कि UserParcelable क्लास Parcelable को लागू करती है और उपयोगकर्ता के डेटा का एक इंस्टेंस बनाती है. इसके बाद, उसे Parcel में लिखा जाता है. इसके बाद, क्रम से लगाए गए पार्सल को पढ़ने के लिए नीचे दिए गए readParcelable के टाइप-सुरक्षित तरीके का इस्तेमाल किया जा सकता है:

Kotlin

val parcel = Parcel.obtain()
val userParcelable = parcel.readParcelable(UserParcelable::class.java.classLoader)

Java

Parcel parcel = Parcel.obtain();
UserParcelable userParcelable = parcel.readParcelable(UserParcelable.class, UserParcelable.CREATOR);

ऊपर दिए गए Java उदाहरण में, UserParcelable.CREATOR का इस्तेमाल, विधि में किया गया है. यह ज़रूरी पैरामीटर, readParcelable तरीके को बताता है कि किस तरह के डेटा की उम्मीद की जा सकती है. साथ ही, यह readParcelable तरीके के अब अमान्य वर्शन की तुलना में बेहतर परफ़ॉर्म करता है.

खास जोखिम

यह सेक्शन उन जोखिमों की जानकारी देता है जिन्हें कम करने के लिए नॉन-स्टैंडर्ड रणनीतियां ज़रूरी हैं या जिन्हें SDK टूल के कुछ लेवल पर कम किया गया है. इन जोखिमों को कम करने के तरीके यहां बताए गए हैं.

जोखिम: अनचाहे ऑब्जेक्ट को डिसीरियलाइज़ करना

किसी क्लास में Serializable इंटरफ़ेस लागू करने पर, उस क्लास के सभी सब-टाइप में इंटरफ़ेस अपने-आप लागू हो जाएगा. इस स्थिति में, कुछ ऑब्जेक्ट ऊपर बताए गए इंटरफ़ेस को इनहेरिट कर सकते हैं. इसका मतलब है कि जिन ऑब्जेक्ट को डीसीरियलाइज़ नहीं किया जाना है उन्हें अब भी प्रोसेस किया जाएगा. इससे अनजाने में, हमले के दायरे में आने वाले एलिमेंट की संख्या बढ़ सकती है.

जोखिम कम करने के तरीके

अगर कोई क्लास OWASP के दिशा-निर्देशों के मुताबिक, Serializable इंटरफ़ेस को इनहेरिट करती है, तो क्लास में ऑब्जेक्ट के सेट को डिससिरियलाइज़ होने से बचाने के लिए, readObject मेथड को इस तरह से लागू किया जाना चाहिए:

Kotlin

@Throws(IOException::class)
private final fun readObject(in: ObjectInputStream) {
    throw IOException("Cannot be deserialized")
}

Java

private final void readObject(ObjectInputStream in) throws java.io.IOException {
    throw new java.io.IOException("Cannot be deserialized");
}

संसाधन