लॉग की जानकारी ज़ाहिर करना

OWASP कैटगरी: MASVS-STORAGE: स्टोरेज

खास जानकारी

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

यह समस्या इनमें से किसी भी स्थिति में हो सकती है:

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

Android Log.* स्टेटमेंट, सामान्य मेमोरी बफ़र logcat में लिखते हैं. Android 4.1 (एपीआई लेवल 16) के बाद, सिर्फ़ खास अधिकार वाले सिस्टम ऐप्लिकेशन को logcat को पढ़ने का ऐक्सेस दिया जा सकता है. इसके लिए, READ_LOGS अनुमति का एलान करना ज़रूरी है. हालांकि, Android ऐसे कई तरह के डिवाइसों पर काम करता है जिनमें पहले से लोड किए गए ऐप्लिकेशन, कभी-कभी READ_LOGS की सुविधा का एलान करते हैं. इसलिए, सीधे logcat में लॉग करने का सुझाव नहीं दिया जाता, क्योंकि इससे डेटा लीक होने की संभावना ज़्यादा होती है.

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

असर

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

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

सामान्य सेटिंग

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

संवेदनशील डेटा को लॉग न करें. जहां तक हो सके, सिर्फ़ कंपाइल के समय के कॉन्स्टेंट को लॉग करें. कंपाइल के समय कॉन्स्टेंट एनोटेशन के लिए, ErrorProne टूल का इस्तेमाल किया जा सकता है.

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

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

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

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

  • आपके पास प्रोडक्शन से सभी लॉग हटाने का विकल्प होता है.
  • आपको प्रोडक्शन में चेतावनी और गड़बड़ी के लॉग रखने होंगे.

इन दोनों मामलों में, R8 जैसी लाइब्रेरी का इस्तेमाल करके लॉग अपने-आप हटाएं. लॉग को मैन्युअल तरीके से हटाने पर, गड़बड़ी हो सकती है. कोड ऑप्टिमाइज़ेशन के तहत, R8 को उन लॉग लेवल को सुरक्षित तरीके से हटाने के लिए सेट किया जा सकता है जिन्हें आपको डीबग करने के लिए रखना है, लेकिन प्रोडक्शन में हटाना है.

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

R8 का इस्तेमाल करके, प्रोडक्शन बिल्ड से लॉग को logcat में भेजें.

Android Studio 3.4 या Android Gradle प्लग इन 3.4.0 और इसके बाद के वर्शन में, कोड को ऑप्टिमाइज़ करने और छोटा करने के लिए, R8 डिफ़ॉल्ट कंपाइलर होता है. हालांकि, आपको R8 को चालू करना होगा.

R8 ने ProGuard की जगह ले ली है, लेकिन प्रोजेक्ट के रूट फ़ोल्डर में मौजूद नियमों की फ़ाइल को अब भी proguard-rules.pro कहा जाता है. यहां दिया गया स्निपेट, proguard-rules.pro फ़ाइल का एक सैंपल दिखाता है. यह फ़ाइल, चेतावनियों और गड़बड़ियों के अलावा प्रोडक्शन से सभी लॉग हटा देती है:

-assumenosideeffects class android.util.Log {
    private static final String TAG = "MyTAG";
    public static boolean isLoggable(java.lang.String, int);
    public static int v(TAG, "My log as verbose");
    public static int d(TAG, "My log as debug");
    public static int i(TAG, "My log as information");
}

यहां दी गई सैंपल proguard-rules.pro फ़ाइल, प्रोडक्शन से सभी लॉग हटा देती है:

-assumenosideeffects class android.util.Log {
    private static final String TAG = "MyTAG";
    public static boolean isLoggable(java.lang.String, int);
    public static int v(TAG, "My log as verbose");
    public static int d(TAG, "My log as debug");
    public static int i(TAG, "My log as information");
    public static int w(TAG, "My log as warning");
    public static int e(TAG, "My log as error");
}

ध्यान दें कि R8, ऐप्लिकेशन को छोटा करने की सुविधाएं और लॉग हटाने की सुविधा देता है. अगर आपको सिर्फ़ लॉग हटाने की सुविधा के लिए R8 का इस्तेमाल करना है, तो अपनी proguard-rules.pro फ़ाइल में यह जोड़ें:

-dontwarn **
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose

-optimizations !code/simplification/arithmetic,!code/allocation/variable
-keep class **
-keepclassmembers class *{*;}
-keepattributes *

प्रॉडक्शन में मौजूद ऐसे सभी लॉग को साफ़ करें जिनमें संवेदनशील डेटा शामिल है

संवेदनशील डेटा को लीक होने से बचाने के लिए, पक्का करें कि आपके ऐप्लिकेशन के नॉन-डीबग वर्शन में, logcat में की जाने वाली सभी लॉगिंग को साफ़ किया गया हो. ऐसा कोई भी डेटा हटाएं जो संवेदनशील हो सकता है.

उदाहरण:

Kotlin

data class Credential<T>(val data: String) {
  /** Returns a redacted value to avoid accidental inclusion in logs. */
  override fun toString() = "Credential XX"
}

fun checkNoMatches(list: List<Any>) {
    if (!list.isEmpty()) {
          Log.e(TAG, "Expected empty list, but was %s", list)
    }
}

Java

public class Credential<T> {
  private T t;
  /** Returns a redacted value to avoid accidental inclusion in logs. */
  public String toString(){
         return "Credential XX";
  }
}

private void checkNoMatches(List<E> list) {
   if (!list.isEmpty()) {
          Log.e(TAG, "Expected empty list, but was %s", list);
   }
}

लॉग में मौजूद संवेदनशील डेटा छिपाना

अगर आपको अपने लॉग में संवेदनशील डेटा शामिल करना है, तो हमारा सुझाव है कि आप लॉग को प्रिंट करने से पहले, उनमें से संवेदनशील डेटा हटा दें या उसे धुंधला कर दें. ऐसा करने के लिए, इनमें से किसी एक तरीके का इस्तेमाल करें:

  • टोकन बनाना. अगर संवेदनशील डेटा किसी वॉल्ट में सेव किया गया है, जैसे कि एन्क्रिप्शन मैनेजमेंट सिस्टम, जिसमें पासवर्ड को टोक़न के ज़रिए रेफ़र किया जा सकता है, तो संवेदनशील डेटा के बजाय टोक़न को लॉग करें.
  • डेटा मास्किंग. डेटा मास्किंग की प्रोसेस को पूरा करने के बाद, उसे पहले जैसा नहीं किया जा सकता. यह संवेदनशील डेटा का एक ऐसा वर्शन बनाता है जो मूल डेटा से मिलता-जुलता है. हालांकि, इसमें फ़ील्ड में मौजूद सबसे संवेदनशील जानकारी छिपी होती है. उदाहरण: क्रेडिट कार्ड नंबर 1234-5678-9012-3456 को XXXX-XXXX-XXXX-1313 से बदलना. हमारा सुझाव है कि अपने ऐप्लिकेशन को प्रोडक्शन में रिलीज़ करने से पहले, सुरक्षा की समीक्षा की प्रक्रिया पूरी करें. इससे, डेटा मास्किंग के इस्तेमाल की जांच की जा सकेगी. चेतावनी: डेटा मास्किंग का इस्तेमाल उन मामलों में न करें जहां संवेदनशील डेटा का सिर्फ़ एक हिस्सा रिलीज़ करने से भी सुरक्षा पर काफ़ी असर पड़ सकता है. जैसे, पासवर्ड मैनेज करते समय.
  • जानकारी छिपाना. जानकारी छिपाने की सुविधा, मास्क करने की सुविधा जैसी ही होती है. हालांकि, इससे किसी फ़ील्ड में मौजूद सारी जानकारी छिप जाती है. उदाहरण: क्रेडिट कार्ड नंबर 1234-5678-9012-3456 को XXXX-XXXX-XXXX-XXXX से बदलना.
  • फ़िल्टर करना. अगर फ़ॉर्मैट स्ट्रिंग पहले से मौजूद नहीं हैं, तो अपनी पसंद की लॉगिंग लाइब्रेरी में उन्हें लागू करें. इससे लॉग स्टेटमेंट में, नॉन-कंसटेंट वैल्यू में बदलाव करने में आसानी होगी.

लॉग की प्रिंटिंग सिर्फ़ “लॉग सैनिटाइज़र” कॉम्पोनेंट की मदद से की जानी चाहिए. इससे यह पक्का होता है कि प्रिंट किए जाने से पहले, सभी लॉग सैनिटाइज़ कर दिए गए हैं. इस बारे में नीचे दिए गए कोड स्निपेट में बताया गया है.

Kotlin

data class ToMask<T>(private val data: T) {
  // Prevents accidental logging when an error is encountered.
  override fun toString() = "XX"

  // Makes it more difficult for developers to invoke sensitive data
  // and facilitates sensitive data usage tracking.
  fun getDataToMask(): T = data
}

data class Person(
  val email: ToMask<String>,
  val username: String
)

fun main() {
    val person = Person(
        ToMask("name@gmail.com"), 
        "myname"
    )
    println(person)
    println(person.email.getDataToMask())
}

Java

public class ToMask<T> {
  // Prevents accidental logging when an error is encountered.
  public String toString(){
         return "XX";
  }

  // Makes it more difficult for developers to invoke sensitive data 
  // and facilitates sensitive data usage tracking.
  public T  getDataToMask() {
    return this;
  }
}

public class Person {
  private ToMask<String> email;
  private String username;

  public Person(ToMask<String> email, String username) {
    this.email = email;
    this.username = username;
  }
}

public static void main(String[] args) {
    Person person = new Person(
        ToMask("name@gmail.com"), 
        "myname"
    );
    System.out.println(person);
    System.out.println(person.email.getDataToMask());
}