بررسی آسیب پذیری اپلیکیشن‌های موبایل در مقابل ذخیره سازی داده‌های حساس

محافظت از توکن‌های احراز هویت، اطلاعات خصوصی و داده‌های حساس اساس امنیت موبایل هستند. برای ذخیره داده‌ها به شکل داده‌های عمومی، در دسترس همه و داده‌های حساس خصوصی که امنیت آن‌ها اهمیت دارد بهتر است خارج از فضای دستگاه ذخیره سازی شوند.

تشخیص طبقه بندی این داده های حساس وابسته به برنامه های موبایل می باشد. دید کلی این است که داده های حساس در صورت امکان در حافظه‌ی محلی ذخیره شوند. در سناریو‌های عملی برخی از انواع داده‌های کاربر باید ذخیره شود. به طور مثال وارد کردن رمز عبور پیچیده در هر بار استفاده از برنامه برای کاربر جذاب نیست. بیشتر برنامه‌ها جهت جلوگیری از این اتفاق برخی از انواع توکن های احراز هویت را به صورت محلی ذخیره می‌کنند. اطلاعات شناسایی افراد (PII) و اطلاعات حساس دیگر که نیاز به فراخوانی دارند هم ممکن است ذخیره گردند. داده‌های حساس زمانی آسیب پذیر هستند که توسط برنامه‌ای که مدام آنها را ذخیره می‌کنند، محافظت نشوند. برنامه‌های موبایلی قادرند تا داده‌ها را در چندین مکان مختلف مثل خود گوشی و یا روی SD کارت خارجی ذخیره می‌کنند. پلت فرم‌های اندرویدی تکنیک‌های مختلفی جهت ذخیره سازی داده‌ها به کار می‌برند که شامل موارد زیر است:

* Shared Preferences

* SQLite Databases

* Realm Databases

* Internal Storage

* External Storage

1-محل قرار گیری داده‌ها در اندروید

1.1- Share Preferences

این بخش جهت ذخیره ی مجموعه های کوچک از داده‌ها معمولا کلیدهای احراز هویت به کار می رود. داده‌های این بخش در قالب فایل xml متنی نوشته می‌شود. این داده‌ها می‌توانند قابل دسترسی توسط عموم و یا خصوصی باشند. برنامه نویسی اشتباه این ذخیره سازی می‌تواند منجربه افشای اطلاعات حساس شود.

SharedPreferences sharedPref = getSharedPreferences("key", MODE_WORLD_READABLE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("username", "administrator");
editor.putString("password", "supersecret");
editor.commit();

زمانیکه activity مورد نظر فراخوانی می شود، فایل key.xml با داده مورد نظر ایجاد خواهد شد. نام کاربری و رمز عبور به صورت متنی واضح در مسیر زیر ذخیره خواهند شد.

/data/data/<package-name>/shared_prefs/key.xml

همچنین حالت MODE_WORLD_READABLE اجازه می‌دهد همه ی برنامه‌های کاربردی به محتوای فایل  key.xml دسترسی داشته باشند.

1.2-SQLite Database / Realm Databases

SQLite یک موتور پایگاه داده است که داده‌ها را در فایل‌های .db ذخیره می‌کند. android.database.sqlite پکیج اصلی آن جهت مدیریت پایگاه داده است. Android SDk بصورت پیش فرض از پایگاه داده‌های SQLite حمایت می کند. کد های زیر جهت ذخیره داده‌های حساس در یک activity به کار گرفته می‌شود:

SQLiteDatabase notSoSecure = openOrCreateDatabase("privateNotSoSecure",MODE_PRIVATE,null);
notSoSecure.execSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR, Password VARCHAR);");
notSoSecure.execSQL("INSERT INTO Accounts VALUES('admin','AdminPass');");
notSoSecure.close();

زمانی که activity  فراخوانی می شود، فایل پایگاه داده privateNotSoSecure داده‌ها را فراهم می‌کند و در فایل متنی در مسیر زیر ذخیره می‌کند.

مسیر پایگاه داده ممکن است شامل چندین فایل مبتنی بر SQLite باشند.

  • Journal files : فایل های temp که جهت مدیریت و پیاده سازی استفاده می شوند.
  • Lock files : این فایل ها جهت افزایش میزان همزمانی پایگاه داده استفاده می شوند.

داده‌های حساس نباید در پایگاه داده رمز گذاری نشده ذخیره شوند. با SQLCipher امکان رمزنگاری پایگاه داده وجود خواهد داشت.

SQLiteDatabase secureDB = SQLiteDatabase.openOrCreateDatabase(database, "password123", null);
secureDB.execSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR,Password VARCHAR);");
secureDB.execSQL("INSERT INTO Accounts VALUES('admin','AdminPassEnc');");
secureDB.close();

در صورتیکه از نسخه رمزنگاری شده پایگاه داده استفاده شود باید برررسی شود که آیا رمزعبور در سورس ذخیره شده است و یا بصورت مخفی در جایی از کد نگه داشته شده است.

1.2.1- Realm Databases

این نوع پایگاه داده در بین برنامه نویسان اندروید طرفدار پیداکرده است. پایگاه داده و محتویاتش به کمک یک کلیدی که در فایل های تنظیمات برنامه ذخیره می‌شوند می تواند رمزگذاری شود.

//the getKey() method either gets the key from the server or from a Keystore, or is deferred from a password.
RealmConfiguration config = new RealmConfiguration.Builder()
  .encryptionKey(getKey())
  .build();
Realm realm = Realm.getInstance(config);


اگر پایگاه داده رمزگذاری نشده است، شما قادر به دستیابی به داده ها هستید. اگر پایگاه داده رمزگذاری شده بررسی کنید که آیا کلید در سورس یا توابع هارد کد شده است و آیا در share preferences یا محل‌های دیگر به صورت غیر محافظت شده ذخیره شده است؟

1.3- Internal Storage

شما می‌توانید فایل ها را در حافظه داخلی گوشی ذخیره کنید. فایل‌های ذخیره شده به صورت پیش فرض در حافظه داخلی قرار داده می‌شوند و برنامه‌های دیگر روی دستگاه موبایل نمی‌توانند به آن دسترسی پیدا کنند. و وقتی کاربر برنامه را حذف می‌کند داده ها نیز پاکسازی می شوند. کد زیر فایل های حساس را در حافظه داخلی ذخیره می‌کند.

FileOutputStream fos = null;
try {
   fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
   fos.write(test.getBytes());
   fos.close();
} catch (FileNotFoundException e) {
   e.printStackTrace();
} catch (IOException e) {
   e.printStackTrace();
}

شما باید نوع فایل را بررسی کنید تا مطمئن شوید فقط برنامه مربوطه می‌تواند به فایل دسترسی داشته باشد. شما می توانید این دسترسی را با MODE_PRIVATE تنظیم کنید. مودهای MODE_WORLD_READABLE و MODE_WORLD_WRITEABLE ریسک امنیتی بیشتری را خواهند داشت. کلاس FileInputStream را جستجو کنید تا بررسی کنید که کدام فایل با این برنامه کاربردی باز و یا خوانده می‌شوند.

4-External Storage

هر دستگاه اندرویدی shared external storage را پشتیبانی می‌کند. فایل‌های ذخیره شده در حافظه خارجی فایل‌های قابل خواندن توسط عموم می‌باشند. زمانیکه حالت انتقال داده‌ها برای کاربر فعال می‌باشد، کاربر می‌تواند آنها را تغییر دهد. کدهای زیر جهت ذخیره داده‌ها در حافظه‌های خارجی مورد استفاده هستند.

File file = new File (Environment.getExternalFilesDir(), "password.txt");
String password = "SecretPassword";
FileOutputStream fos;
    fos = new FileOutputStream(file);
    fos.write(password.getBytes());
    fos.close();

فایل‌ها بصورت واضح در حافظه خارجی هنگامیکه activity فراخوانی می‌شود ایجاد و ذخیره می‌شوند. فایل‌های بیرون از مسیر برنامه کاربردی مربوطه وقتی که کاربر برنامه را پاک می‌کند نیز حذف نمی‌شوند.

2-تست آسیب پذیری ها

همانطور که گفته شد چندین راه جهت ذخیره سازی اطلاعات روی دستگاه‌های اندرویدی وجود دارد. بنابراین شما باید چندین منبع را جهت تعیین نوع ذخیره سازی به کار رفته در برنامه‌ی کاربردی بررسی نمایید آیا برنامه مورد نظر داده‌های حساس را به صورت غیر امن پردازش می‌کند.

1- بررسی AndroidManifest.xml برای سطح دسترسی حافظه خارجی به عنوان مثال:

android:name=”android.permission.WRITE_EXTERNAL_STORAGE”.

2- بررسی سورس کد برای کلید واژه‌ها و فراخوانی API  که در داده‌های ذخیره شده استفاده می‌شوند.

2.1- سطح دسترسی فایل ها:

از به کارگیری دسترسی‌های MODE_WORLD_READABLE  یا  MODE_WORLD_WRITABLE برای فایل‌ها اجتناب کنید چون هر برنامه کاربردی دیگر قادر خواهد بود از فایل ها بخواند یا در آن‌ها بنویسد، حتی اگر آن‌ها در مسیر private  برنامه ذخیره شوند.

اگر داده با برنامه های دیگر یه اشتراک گذاشته می‌شوند content provider  را استفاده کنید. این بخش دسترسی write و read را به برنامه‌های دیگر می دهد و می‌تواند دسترسی داینامیک را روی یک مورد مدیریت کند.

2.2- کلاس ها و توابع  مانند:

  • کلاس the SharedPreferences برای ذخیره‌ی جفت کلیدها و مقادیر آن‌ها.
  • کلاس‌های FileOutPutStream که حافظه داخلی و خارجی را استفاده می‌کنند.
  • توابع getExternal  که حافظه خارجی را استفاده می‌کند.
  • تابع getWritableDatabase یک پایگاه داده‌ی SQLiteDatabase  برای نوشتن بر می‌گرداند.
  • تابع getReadableDatabase یک پایگاه داده‌ی SQLiteDatabase  برای خواندن  بر‌می‌گرداند
  • تابع getCacheDir و getExternalCacheDirs فایل‌های cache را استفاده می کند.

2.3- رمز گذاری باید به کمک توابع SDK صحت سنجی شده انجام گردد. موارد زیر موارد نامناسب را در برنامه نویسی نمایش می‌دهد.

  • رمزگذاری داده‌های حساس ذخیره شده محلی با عملگر‌های ساده رمزنگاری XOR یا bit flipping (الگوریتم‌های قابل حدس). از این نوع عملیات‌ها باید اجتناب شود چون داده‌های رمزگذاری شده می تواند به راحتی بازیابی شود.
  • کلیدهایی که بدون ویژگی‌هایی مانند KeyStore های اندروید ایجاد و یا استفاده می‎شوند.
  • کلیدهایی که در هارد کد شدن برنامه قابل دستیابی باشند.

2.4- بررسی محل‌های رایج secret کدها در مسیر res/values/strings.xml

<resources>
    <string name="app_name">SuperApp</string>
    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="secret_key">My_Secret_Key</string>
  </resources>

و محل کانفیگ مانند Local.properties و یا Gradle.properties

 buildTypes {
  debug {
    minifyEnabled true
    buildConfigField "String", "hiddenPassword", "\"${hiddenPassword}\""
  }
}

3-راه کار های  مناسب امن بودن

  • رمزگذاری داده‌های حساس به کمک کلیدهایی که در Android KeyStore فراهم شده‌اند.
  • عدم استفاده از share preferences (که به صورت پیش فرض رمزگذاری نشده و یا امن می‌باشد).
  • عدم استفاده از حافظه‌های خارجی برای ذخیره داده‌های حساس ( داده‌ها به صورت پیش فرض حتی با حذف برنامه هم حذف نمی شوند).

در ادامه آموزشی به صورت ویدیو در مورد تحلیل ایستا همانطور که در بالا ارایه شده و تحلیل داینامیک از استخراج دیتا از حافظه و shareprefernces را می توانید در لینک ویدیو مشاهده نمایید.

منابع

  1. راهنمای تست امنیت موبایل OWASP پی دی اف MSTG بخشTesting Local Storage for Sensitive Data
در صورت تمایل به اشتراگ بگذارید