मशीन-से-मशीन कम्यूनिकेशन का असुरक्षित सेटअप

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

खास जानकारी

ऐसे ऐप्लिकेशन का इस्तेमाल करना आम बात है जो उपयोगकर्ताओं को रेडियो फ़्रीक्वेंसी (आरएफ़) कम्यूनिकेशन या केबल कनेक्शन का इस्तेमाल करके, डेटा ट्रांसफ़र करने या अन्य डिवाइसों से इंटरैक्ट करने की सुविधा देते हैं. Android में इस काम के लिए, आम तौर पर इन टेक्नोलॉजी का इस्तेमाल किया जाता है: क्लासिक ब्लूटूथ (Bluetooth BR/EDR), ब्लूटूथ स्मार्ट (बीएलई), वाई-फ़ाई पी2पी, एनएफ़सी, और यूएसबी.

आम तौर पर, इन टेक्नोलॉजी को उन ऐप्लिकेशन में लागू किया जाता है जो स्मार्ट होम ऐक्सेसरी, सेहत की निगरानी करने वाले डिवाइसों, सार्वजनिक परिवहन के कियॉस्क, पेमेंट टर्मिनल, और Android की सुविधा वाले अन्य डिवाइसों से कम्यूनिकेट करते हैं.

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

Android, मशीन-टू-मशीन कम्यूनिकेशन को कॉन्फ़िगर करने के लिए, डेवलपर को खास एपीआई उपलब्ध कराता है.

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

असर

ऐप्लिकेशन में लागू की गई डिवाइस-टू-डिवाइस टेक्नोलॉजी के हिसाब से, इस सुविधा का असर अलग-अलग हो सकता है.

मशीन-टू-मशीन कम्यूनिकेशन चैनलों का गलत इस्तेमाल या कॉन्फ़िगरेशन करने से, उपयोगकर्ता के डिवाइस पर ऐसे कम्यूनिकेशन के अनुरोध आ सकते हैं जिन पर भरोसा नहीं किया जा सकता. इससे डिवाइस पर मैन-इन-द-मिडल (एमआईटीएम), कमांड इंजेक्शन, DoS या पहचान चुराकर किए जाने वाले हमलों जैसे अन्य हमले हो सकते हैं.

जोखिम: वायरलेस चैनलों पर संवेदनशील डेटा को चोरी-छिपे सुना जा सकता है

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

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

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


जोखिम: वायरलेस तरीके से नुकसान पहुंचाने वाला डेटा इंजेक्ट किया जा सकता है

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

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

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

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

यहां दिए गए स्निपेट में, डेटा की पुष्टि करने के लॉजिक का उदाहरण दिखाया गया है. इसे Android डेवलपर के उदाहरण के आधार पर लागू किया गया था. इस उदाहरण में, ब्लूटूथ डेटा ट्रांसफ़र को लागू करने का तरीका बताया गया है:

Kotlin

class MyThread(private val mmInStream: InputStream, private val handler: Handler) : Thread() {

    private val mmBuffer = ByteArray(1024)
      override fun run() {
        while (true) {
            try {
                val numBytes = mmInStream.read(mmBuffer)
                if (numBytes > 0) {
                    val data = mmBuffer.copyOf(numBytes)
                    if (isValidBinaryData(data)) {
                        val readMsg = handler.obtainMessage(
                            MessageConstants.MESSAGE_READ, numBytes, -1, data
                        )
                        readMsg.sendToTarget()
                    } else {
                        Log.w(TAG, "Invalid data received: $data")
                    }
                }
            } catch (e: IOException) {
                Log.d(TAG, "Input stream was disconnected", e)
                break
            }
        }
    }

    private fun isValidBinaryData(data: ByteArray): Boolean {
        if (// Implement data validation rules here) {
            return false
        } else {
            // Data is in the expected format
            return true
        }
    }
}

Java

public void run() {
            mmBuffer = new byte[1024];
            int numBytes; // bytes returned from read()
            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                try {
                    // Read from the InputStream.
                    numBytes = mmInStream.read(mmBuffer);
                    if (numBytes > 0) {
                        // Handle raw data directly
                        byte[] data = Arrays.copyOf(mmBuffer, numBytes);
                        // Validate the data before sending it to the UI activity
                        if (isValidBinaryData(data)) {
                            // Data is valid, send it to the UI activity
                            Message readMsg = handler.obtainMessage(
                                    MessageConstants.MESSAGE_READ, numBytes, -1,
                                    data);
                            readMsg.sendToTarget();
                        } else {
                            // Data is invalid
                            Log.w(TAG, "Invalid data received: " + data);
                        }
                    }
                } catch (IOException e) {
                    Log.d(TAG, "Input stream was disconnected", e);
                    break;
                }
            }
        }

        private boolean isValidBinaryData(byte[] data) {
            if (// Implement data validation rules here) {
                return false;
            } else {
                // Data is in the expected format
                return true;
           }
    }

जोखिम: यूएसबी के ज़रिए नुकसान पहुंचाने वाला डेटा इंजेक्ट किया गया

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

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

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

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

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

इस पुष्टि को, बफ़र किए गए कॉन्टेंट की जांच करने के लिए एक नया थ्रेड बनाकर पूरा किया जा सकता है. उदाहरण के लिए, bulkTransfer पर:

Kotlin

fun performBulkTransfer() {
    // Stores data received from a device to the host in a buffer
    val bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.size, 5000)

    if (bytesTransferred > 0) {
        if (//Checks against buffer content) {
            processValidData(buffer)
        } else {
            handleInvalidData()
        }
    } else {
        handleTransferError()
    }
}

Java

public void performBulkTransfer() {
        //Stores data received from a device to the host in a buffer
        int bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.length, 5000);
        if (bytesTransferred > 0) {
            if (//Checks against buffer content) {
                processValidData(buffer);
            } else {
                handleInvalidData();
            }
        } else {
            handleTransferError();
        }
    }

खास जोखिम

इस सेक्शन में उन जोखिमों के बारे में जानकारी दी जाती है जिनके लिए, जोखिम कम करने की सामान्य रणनीतियों के अलावा अन्य रणनीतियों की ज़रूरत होती है. इसके अलावा, इसमें उन जोखिमों के बारे में भी जानकारी दी जाती है जिन्हें एसडीके के किसी लेवल पर कम किया गया था और यहां पूरी जानकारी देने के लिए मौजूद हैं.

जोखिम: ब्लूटूथ – डिवाइस के दिखने का गलत समय

Android डेवलपर के ब्लूटूथ दस्तावेज़ में हाइलाइट किए गए तरीके के मुताबिक, ऐप्लिकेशन में ब्लूटूथ इंटरफ़ेस कॉन्फ़िगर करते समय, डिवाइस को खोजने की सुविधा चालू करने के लिए startActivityForResult(Intent, int) तरीके का इस्तेमाल करें. साथ ही, EXTRA_DISCOVERABLE_DURATION को शून्य पर सेट करें. इससे डिवाइस तब तक खोजा जा सकेगा, जब तक ऐप्लिकेशन बैकग्राउंड या फ़ोरग्राउंड में चल रहा हो. क्लासिक ब्लूटूथ स्पेसिफ़िकेशन के लिए, आस-पास के डिवाइस लगातार खास डिस्कवरी मैसेज ब्रॉडकास्ट करते हैं. इससे अन्य डिवाइस, डिवाइस का डेटा पा सकते हैं या उससे कनेक्ट हो सकते हैं. ऐसे में, नुकसान पहुंचाने वाला कोई तीसरा पक्ष इन मैसेज को इंटरसेप्ट कर सकता है और Android डिवाइस से कनेक्ट हो सकता है. कनेक्ट होने के बाद, हमलावर अन्य हमले कर सकता है. जैसे, डेटा चोरी करना, सेवा से इनकार (DoS) करना या कमांड इंजेक्शन.

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

EXTRA_DISCOVERABLE_DURATION को कभी भी शून्य पर सेट नहीं किया जाना चाहिए. अगर EXTRA_DISCOVERABLE_DURATION पैरामीटर सेट नहीं किया जाता है, तो Android डिफ़ॉल्ट रूप से डिवाइसों को दो मिनट के लिए खोजने की सुविधा चालू कर देता है. EXTRA_DISCOVERABLE_DURATION पैरामीटर के लिए, ज़्यादा से ज़्यादा दो घंटे (7200 सेकंड) का समय सेट किया जा सकता है. हमारा सुझाव है कि ऐप्लिकेशन के इस्तेमाल के उदाहरण के हिसाब से, डिवाइस को खोजने की सुविधा चालू रखने की अवधि को कम से कम रखें.


जोखिम: एनएफ़सी – क्लोन किए गए इंटेंट-फ़िल्टर

नुकसान पहुंचाने वाला कोई ऐप्लिकेशन, खास एनएफ़सी टैग या एनएफ़सी की सुविधा वाले डिवाइसों का डेटा पढ़ने के लिए, इंटेंट-फ़िल्टर रजिस्टर कर सकता है. ये फ़िल्टर, किसी भरोसेमंद ऐप्लिकेशन के तय किए गए फ़िल्टर की तरह काम कर सकते हैं. इससे हमलावर, बदले गए एनएफ़सी डेटा का कॉन्टेंट पढ़ सकता है. ध्यान दें कि जब दो गतिविधियां, किसी खास एनएफ़सी टैग के लिए एक जैसे इंटेंट-फ़िल्टर तय करती हैं, तो गतिविधि चुनने वाला टूल दिखता है. इसलिए, हमला सफल होने के लिए उपयोगकर्ता को अब भी नुकसान पहुंचाने वाला ऐप्लिकेशन चुनना होगा. हालांकि, इंटेंट-फ़िल्टर को क्लॉकिंग के साथ इस्तेमाल करने पर, ऐसा अब भी हो सकता है. यह हमला सिर्फ़ उन मामलों में अहम है जहां एनएफ़सी के ज़रिए शेयर किए गए डेटा को बेहद संवेदनशील माना जा सकता है.

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

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


जोखिम: एनएफ़सी – एनडीईएफ़ मैसेज की पुष्टि नहीं की जा सकी

जब Android डिवाइस को एनएफ़सी टैग या एनएफ़सी की सुविधा वाले डिवाइस से डेटा मिलता है, तो सिस्टम अपने-आप उस ऐप्लिकेशन या गतिविधि को ट्रिगर करता है जिसे एनडीईएफ़ मैसेज को हैंडल करने के लिए कॉन्फ़िगर किया गया है. ऐप्लिकेशन में लागू किए गए लॉजिक के मुताबिक, टैग में मौजूद डेटा या डिवाइस से मिला डेटा, अन्य गतिविधियों को दिया जा सकता है. इससे आगे की कार्रवाइयां ट्रिगर की जा सकती हैं. जैसे, वेब पेज खोलना.

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

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

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

यहां दिए गए स्निपेट में, डेटा की पुष्टि करने के लॉजिक का उदाहरण दिखाया गया है. इसे एक ऐसे तरीके के तौर पर लागू किया गया है जिसमें NDEF मैसेज को आर्ग्युमेंट के तौर पर इस्तेमाल किया जाता है. साथ ही, इसमें मैसेज ऐरे में मौजूद मैसेज का इंडेक्स भी शामिल होता है. इसे Android डेवलपर के उदाहरण के तौर पर लागू किया गया था, ताकि स्कैन किए गए एनएफ़सी एनडीईएफ़ टैग से डेटा मिल सके:

Kotlin

//The method takes as input an element from the received NDEF messages array
fun isValidNDEFMessage(messages: Array<NdefMessage>, index: Int): Boolean {
    // Checks if the index is out of bounds
    if (index < 0 || index >= messages.size) {
        return false
    }
    val ndefMessage = messages[index]
    // Retrieves the record from the NDEF message
    for (record in ndefMessage.records) {
        // Checks if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
        if (record.tnf == NdefRecord.TNF_ABSOLUTE_URI && record.type.size == 1) {
            // Loads payload in a byte array
            val payload = record.payload

            // Declares the Magic Number that should be matched inside the payload
            val gifMagicNumber = byteArrayOf(0x47, 0x49, 0x46, 0x38, 0x39, 0x61) // GIF89a

            // Checks the Payload for the Magic Number
            for (i in gifMagicNumber.indices) {
                if (payload[i] != gifMagicNumber[i]) {
                    return false
                }
            }
            // Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
            if (payload.size == 13) {
                return true
            }
        }
    }
    return false
}

Java

//The method takes as input an element from the received NDEF messages array
    public boolean isValidNDEFMessage(NdefMessage[] messages, int index) {
        //Checks if the index is out of bounds
        if (index < 0 || index >= messages.length) {
            return false;
        }
        NdefMessage ndefMessage = messages[index];
        //Retrieve the record from the NDEF message
        for (NdefRecord record : ndefMessage.getRecords()) {
            //Check if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
            if ((record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) && (record.getType().length == 1)) {
                //Loads payload in a byte array
                byte[] payload = record.getPayload();
                //Declares the Magic Number that should be matched inside the payload
                byte[] gifMagicNumber = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}; // GIF89a
                //Checks the Payload for the Magic Number
                for (int i = 0; i < gifMagicNumber.length; i++) {
                    if (payload[i] != gifMagicNumber[i]) {
                        return false;
                    }
                }
                //Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
                if (payload.length == 13) {
                    return true;
                }
            }
        }
        return false;
    }

संसाधन