رده OWASP: MASVS-STORAGE: ذخیرهسازی
نمای کلی
افشای اطلاعات لاگ نوعی آسیبپذیری است که در آن برنامهها دادههای حساس را در لاگ دستگاه چاپ میکنند. اگر این اطلاعات حساس در معرض دید عوامل مخرب قرار گیرد، ممکن است کاملاً ارزشمند باشد - مانند اعتبارنامههای کاربر یا اطلاعات شخصی قابل شناسایی (PII) - یا ممکن است حملات بیشتری را ممکن سازد.
این مشکل میتواند در هر یک از سناریوهای زیر رخ دهد:
- گزارشهای تولید شده توسط برنامه:
- این گزارشها عمداً به افراد غیرمجاز اجازه دسترسی میدهند، اما بهطور تصادفی حاوی دادههای حساس هستند.
- این گزارشها عمداً شامل دادههای حساس هستند، اما بهطور تصادفی در دسترس افراد غیرمجاز قرار میگیرند.
- گزارشهای خطای عمومی که ممکن است گاهی اوقات، بسته به پیام خطای ایجاد شده، دادههای حساس را چاپ کنند.
- لاگهای تولید شده خارجی:
- اجزای خارجی مسئول چاپ لاگهایی هستند که شامل دادههای حساس هستند.
دستورات Android Log.* در بافر حافظه مشترک logcat نوشته میشوند. از اندروید ۴.۱ (سطح API ۱۶)، فقط برنامههای سیستمی دارای امتیاز میتوانند با اعلام مجوز READ_LOGS به خواندن logcat دسترسی داشته باشند. با این حال، اندروید از مجموعهای فوقالعاده متنوع از دستگاههایی پشتیبانی میکند که برنامههای از پیش بارگذاری شده آنها گاهی اوقات امتیاز READ_LOGS را اعلام میکنند. در نتیجه، ثبت مستقیم اطلاعات در logcat توصیه نمیشود زیرا بیشتر مستعد نشت دادهها است.
مطمئن شوید که تمام گزارشهای logcat در نسخههای غیر دیباگ برنامه شما، ایمنسازی شدهاند. هرگونه دادهای که احتمالاً میتواند حساس باشد را حذف کنید. به عنوان یک اقدام احتیاطی اضافی، از ابزارهایی مانند R8 برای حذف تمام سطوح گزارش به جز هشدار و خطا استفاده کنید. اگر به گزارشهای دقیقتری نیاز دارید، به جای استفاده از گزارش سیستم، از حافظه داخلی استفاده کنید و گزارشهای خود را مستقیماً مدیریت کنید.
تأثیر
شدت کلاس آسیبپذیری افشای اطلاعات لاگ میتواند بسته به زمینه و نوع دادههای حساس متفاوت باشد. بهطورکلی، تأثیر این کلاس آسیبپذیری، از دست دادن محرمانگی اطلاعات بالقوه حیاتی مانند اطلاعات شخصی و اعتبارنامهها است.
کاهشها
عمومی
به عنوان یک اقدام پیشگیرانه عمومی در طول طراحی و پیادهسازی، مرزهای اعتماد را بر اساس اصل حداقل امتیاز ترسیم کنید. در حالت ایدهآل، دادههای حساس نباید از هیچ یک از مناطق اعتماد عبور کنند یا به خارج از آنها برسند. این امر تفکیک امتیازات را تقویت میکند.
دادههای حساس را ثبت نکنید. فقط در صورت امکان، ثابتهای زمان کامپایل را ثبت کنید. میتوانید از ابزار ErrorProne برای حاشیهنویسی ثابتهای زمان کامپایل استفاده کنید.
از گزارشهایی که بسته به خطای ایجاد شده، ممکن است حاوی اطلاعات پیشبینی نشده، از جمله دادههای حساس باشند، خودداری کنید. تا حد امکان، دادههای چاپ شده در گزارشها و گزارشهای خطا فقط باید شامل اطلاعات قابل پیشبینی باشند.
از ورود به سیستم در logcat خودداری کنید. دلیل این امر این است که ورود به سیستم در logcat ممکن است به دلیل برنامههایی با مجوز READ_LOGS به یک مشکل حریم خصوصی تبدیل شود. همچنین این روش بیاثر است زیرا نمیتواند هشدارها را فعال کند یا مورد پرسش قرار گیرد. توصیه میکنیم برنامهها، backend logcat را فقط برای نسخههای توسعهدهنده پیکربندی کنند.
اکثر کتابخانههای مدیریت لاگ، امکان تعریف سطوح لاگ را فراهم میکنند که امکان ثبت مقادیر مختلف اطلاعات بین لاگهای اشکالزدایی و تولید را فراهم میکند. به محض پایان آزمایش محصول، سطح لاگ را تغییر دهید تا با «اشکالزدایی» متفاوت باشد.
تا حد امکان سطوح لاگ را از محیط عملیاتی حذف کنید. اگر نمیتوانید از نگهداری لاگها در محیط عملیاتی اجتناب کنید، متغیرهای غیرثابت را از دستورات لاگ حذف کنید. سناریوهای زیر ممکن است رخ دهد:
- شما میتوانید تمام لاگها را از محیط تولید حذف کنید.
- شما باید لاگهای هشدار و خطا را در محیط عملیاتی (Production) نگه دارید.
برای هر دو مورد، لاگها را با استفاده از کتابخانههایی مانند R8 به طور خودکار حذف کنید. هرگونه تلاش برای حذف دستی لاگها مستعد خطا است. به عنوان بخشی از بهینهسازی کد، R8 را میتوان طوری تنظیم کرد که سطوح لاگی را که میخواهید برای اشکالزدایی نگه دارید، با خیال راحت حذف کند، اما در محیط عملیاتی حذف کند.
اگر قصد دارید در محیط عملیاتی وارد سیستم شوید، پرچمهایی (flag) را آماده کنید که بتوانید در صورت بروز حادثه، ثبت وقایع را به صورت مشروط متوقف کنید. پرچمهای واکنش به حادثه باید موارد زیر را در اولویت قرار دهند: ایمنی استقرار؛ سرعت و سهولت استقرار، دقت در ویرایش گزارشها، میزان استفاده از حافظه و هزینههای عملکرد اسکن هر پیام گزارش.
با استفاده از R8، لاگها را از فایلهای Production به logcat منتقل کنید.
در اندروید استودیو ۳.۴ یا افزونه اندروید گریدل ۳.۴.۰ و بالاتر، 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 قابلیتهای app-shrinking و 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 *
هرگونه لاگ نهایی در محیط عملیاتی که حاوی دادههای حساس است را پاکسازی کنید.
برای جلوگیری از نشت دادههای حساس، اطمینان حاصل کنید که تمام گزارشهای 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. - فیلتر کردن. اگر رشتههای قالببندیشده در کتابخانهی ثبت وقایع انتخابی شما وجود ندارند، آنها را پیادهسازی کنید تا اصلاح مقادیر غیرثابت در دستورات ثبت وقایع تسهیل شود.
چاپ لاگ فقط باید از طریق یک کامپوننت «logs sanitizer» انجام شود که تضمین میکند تمام لاگها قبل از چاپ، همانطور که در قطعه کد زیر نشان داده شده است، پاکسازی شدهاند.
کاتلین
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());
}