دسته OWASP: MASVS-STORAGE: ذخیره سازی
نمای کلی
Log Info Disclosure نوعی آسیبپذیری است که در آن برنامهها دادههای حساس را در گزارش دستگاه چاپ میکنند. در صورت قرار گرفتن در معرض عوامل مخرب، این اطلاعات حساس ممکن است کاملاً ارزشمند باشد - مانند اعتبار یک کاربر یا اطلاعات شخصی قابل شناسایی (PII) - یا ممکن است حملات بیشتری را فعال کند.
این مشکل می تواند در هر یک از حالات زیر رخ دهد:
- گزارش های ایجاد شده توسط برنامه:
- گزارشها عمداً اجازه دسترسی به بازیگران غیرمجاز را میدهند، اما به طور تصادفی حاوی دادههای حساس هستند.
- گزارشها عمداً شامل دادههای حساس هستند، اما به طور تصادفی برای عوامل غیرمجاز قابل دسترسی هستند.
- گزارشهای خطای عمومی که ممکن است در مواقعی بسته به پیام خطای راهاندازی شده، دادههای حساس را چاپ کنند.
- لاگ های تولید شده خارجی:
- اجزای خارجی مسئول چاپ لاگ هایی هستند که شامل داده های حساس هستند.
عبارات Android Log.*
در بافر حافظه مشترک logcat
می نویسند. از Android 4.1 (سطح API 16)، تنها برنامههای سیستمی دارای امتیاز میتوانند با اعلام مجوز READ_LOGS
به خواندن logcat
دسترسی داشته باشند. با این حال، Android از مجموعه بسیار متنوعی از دستگاهها پشتیبانی میکند که برنامههای از پیش بارگذاری شده آنها گاهی اوقات امتیاز READ_LOGS
را اعلام میکنند. در نتیجه، ورود مستقیم به logcat
ممنوع است زیرا بیشتر مستعد نشت داده است.
اطمینان حاصل کنید که تمام ورود به logcat
در نسخه های بدون اشکال برنامه شما پاکسازی شده است. هر گونه داده ای که ممکن است حساس باشد را حذف کنید. به عنوان یک اقدام احتیاطی اضافی، از ابزارهایی مانند R8 برای حذف تمام سطوح گزارش به جز هشدار و خطا استفاده کنید. اگر به گزارشهای دقیقتر نیاز دارید، به جای استفاده از گزارش سیستم، از حافظه داخلی استفاده کنید و گزارشهای خود را مستقیماً مدیریت کنید.
تاثیر
شدت کلاس آسیبپذیری Log Info Disclosure بسته به زمینه و نوع دادههای حساس میتواند متفاوت باشد. به طور کلی، تأثیر این کلاس آسیبپذیری از بین رفتن محرمانه بودن اطلاعات بالقوه حیاتی مانند PII و اعتبارنامهها است.
اقدامات کاهشی
ژنرال
به عنوان یک اقدام پیشگیرانه کلی در طول طراحی و اجرا، مرزهای اعتماد را طبق اصل حداقل امتیاز ترسیم کنید. در حالت ایدهآل، دادههای حساس نباید از هیچ یک از مناطق مورد اعتماد عبور کنند یا به خارج از آن برسند. این جداسازی امتیازات را تقویت می کند.
داده های حساس را ثبت نکنید در صورت امکان فقط ثابت های زمان کامپایل را ثبت کنید. می توانید از ابزار ErrorProne برای حاشیه نویسی ثابت در زمان کامپایل استفاده کنید.
از گزارشهایی که بسته به خطای ایجاد شده، عباراتی را چاپ میکنند که ممکن است حاوی اطلاعات پیشبینی نشده، از جمله دادههای حساس باشد. تا آنجا که ممکن است، داده های چاپ شده در گزارش ها و گزارش های خطا فقط باید شامل اطلاعات قابل پیش بینی باشد.
از ورود به logcat
خودداری کنید. این به این دلیل است که ورود به logcat
ممکن است به دلیل برنامههای دارای مجوز READ_LOGS
به یک مشکل حریم خصوصی تبدیل شود. همچنین ناکارآمد است زیرا نمی تواند هشدارها را راه اندازی کند یا درخواست شود. توصیه میکنیم برنامهها باطن logcat
را فقط برای ساختهای توسعهدهنده پیکربندی کنند.
اکثر کتابخانههای مدیریت لاگ امکان تعریف سطوح گزارش را فراهم میکنند که امکان ثبت مقادیر متفاوتی از اطلاعات بین گزارشهای اشکال زدایی و تولید را فراهم میکند. سطح گزارش را طوری تغییر دهید که به محض پایان آزمایش محصول، با "اشکال زدایی" متفاوت باشد.
تا جایی که ممکن است سطوح لاگ را از تولید حذف کنید. اگر نمیتوانید از نگهداری گزارشها در تولید اجتناب کنید، متغیرهای غیر ثابت را از دستورات گزارش حذف کنید. سناریوهای زیر ممکن است رخ دهد:
- شما میتوانید همه گزارشها را از Production حذف کنید.
- شما باید گزارش های هشدار و خطا را در Production نگه دارید.
برای هر دوی این موارد، لاگ ها را به طور خودکار با استفاده از کتابخانه هایی مانند R8 حذف کنید. هرگونه تلاش برای حذف لاگ ها به صورت دستی مستعد خطا است. بهعنوان بخشی از بهینهسازی کد، R8 را میتوان طوری تنظیم کرد که سطوح گزارشی را که میخواهید برای اشکالزدایی نگه دارید، اما در Production حذف کنید، با خیال راحت حذف کند.
اگر میخواهید وارد پروداکشن شوید، پرچمهایی را آماده کنید که میتوانید از آنها برای خاموش کردن ورود به سیستم در صورت بروز حادثه استفاده کنید. پرچمهای واکنش به حادثه باید در اولویت قرار گیرند: ایمنی استقرار. سرعت و سهولت استقرار، دقت در ویرایش گزارشها، استفاده از حافظه و هزینههای عملکرد اسکن هر پیام گزارش.
با استفاده از R8، لاگها را از بیلدهای Production به 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 قابلیتهای کوچک کردن برنامه و عملکرد log-stripping را ارائه میکند. اگر میخواهید از R8 فقط برای عملکرد log-stripping آن استفاده کنید، موارد زیر را به فایل proguard-rules.pro
خود اضافه کنید:
-dontwarn **
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!code/allocation/variable
-keep class **
-keepclassmembers class *{*;}
-keepattributes *
هرگونه گزارش احتمالی در Production حاوی داده های حساس را پاکسازی کنید
به منظور جلوگیری از نشت داده های حساس، مطمئن شوید که تمام ورود به logcat
در نسخه های غیر اشکال زدایی برنامه شما پاکسازی می شود. هر گونه داده ای که ممکن است حساس باشد را حذف کنید.
مثال:
کاتلین
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)
}
}
جاوا
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
. - فیلتر کردن. اگر از قبل وجود نداشته باشند، رشته های قالب را در کتابخانه ورود به سیستم انتخابی خود پیاده سازی کنید تا تغییر مقادیر غیر ثابت در عبارات گزارش را تسهیل کنید.
همانطور که در قطعه کد زیر نشان داده شده است، چاپ لاگ فقط باید از طریق یک جزء "ضدعفونی کننده سیاههها" انجام شود که تضمین می کند همه سیاههها قبل از چاپ پاکسازی می شوند.
کاتلین
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())
}
جاوا
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());
}