ذخیره سازی ناامن داده(Insecure Data Storage)
با استفاده از این آسیب پذیری می توان به داده های اپلیکیشن سمت کاربر دسترسی پیدا نمود. از داده های مهم که به هیچ عنوان نباید به صورت مستفیم سمت کاربر ذخیره شود می توان به:
- نام کاربری یا username
- توکن احراهویت
- کوکی ها
- اطلاعات موقعیت مکانی
- گزارشات یا log های برنامه
- تاریخچه تراکنش های بانکی
- اطلاعات کارت های بانکی
- UDID یاEMEI
- اطلاعات هویتی مانند کدملی، آدرس ها
- نام دستگاه
- اطلاعات شبکه
اشاره نمود.
همچنین نگهداری داده ها به صورت مستقیم در
- پایگاه داده های sqlite
- فایل های log
- فایل های plist
- فایل های xml و Manifest
- فایل های binary
- نگهداری در sd card
- نگهداری دقیق و همگام با فایل های cloud
دارای ریسک است.
راه حل های کلی برای جلوگیری از این آسیب پذیری را عبارتنند از:
- برای استفاده از local storage می توانید از setStorageEncryption استفاده کنید.
- برای ذخیره سازی داده در SD Card می توانید از javax.crypto استفاده کنید.
- داده های حساس را به صورت مستقیم در shared preferences قرار ندهید.
- هنگام استفاده از رمزنگاری کلید رمز خود را به صورت مستقیم یا به اصطلاح hardcode در کد خود قرار ندهید.
- علاوه بر رمزنگاری داده ها به صورت پیش فرض توسط سیستم عامل، لایه دیگر رمزنگاری ایجاد کنید.
برای مثال در کد زیر اطلاعات حساسی مانند نام کاربری و کلمه عبور مستقیم در shared preferences قرار گرفته
SharedPreferences sharedPref = getSharedPreferences("key", MODE_WORLD_READABLE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putString("username", "administrator"); editor.putString("password", "supersecret"); editor.commit();
برای جلوگیری می توان به موارد زیر اشاره نمود.
1-ذخیره <ننمودن> داده های حساس در shared preferences
2-استفاده از کتابخانه های رمزنگاری مانند Secure-preferences:
SharedPreferences prefs = new SecurePreferences(context);
Sqlite
همانطور که می دانید یک از پرکاربردترین و قدیمی ترین پایگاه داد ها برای نگهداری داده های sqlite است. فایل های با پسوند .db حاوی رکورد های این پایگاه داده است.
اتصال به پایگاه داده با نام کاربری و کلمه عبور hardcode شده در کد اتصال دارای ریسک است.
برای مثال کد زیر را در نظر بگیرید:
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();
برای جلوگیری از این آسیب پذیری می توان از کتابخانه 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();
نکته مهم:
برای جلوگیری از درج کلمه عبور پایگاه داده به صورت مستقیم در کد، می توانید هنگام ورود برنامه کلمه عبور رمزنگاری شده ارتباط با پایگاه داده را با اطلاعات وارد شده از طرف کاربر رمزگشایی کنید.
Realm
پایگاه داده های Realm که به نوعی جایگزین sqlite امروز بسیار استفاده می شود.
کد زیر بدون رمزنگاری برای اتصال به این پایگاه داده در نظر بگیرید:
RealmConfiguration realmConfig = new RealmConfiguration.Builder() .name("tasky.realm") .schemaVersion(0) .build(); Realm.setDefaultConfiguration(realmConfig);
حال کد زیر را در نظر بگیرید:
RealmConfiguration config = new RealmConfiguration.Builder() .encryptionKey(getKey()) .build(); Realm realm = Realm.getInstance(config);
برای getKey() هم می توان از keystore و یا از سرور درخواست کلید نمود.
Internal Storage
دسترسی به internal storage به صورت پیش فرض توسط سایر برنامه ها ممکن نیست.
ولی اگر ذخیره سازی داده های به صورت ناامن و به شکل زیر و استفاده از حالت های `MODE_WORLD_READABLE` و `MODE_WORLD_WRITEABLE` باشد دسترسی سایر برنامه ها به internal storage برنامه امکان پذیر است.
FileOutputStream fos = null; try { fos = openFileOutput(FILENAME, Context.MODE_WORLD_READABLE); fos.write(test.getBytes()); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
حالت امن تر استفاده از MODE_PRIVATE در ذخیره سازی است.
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(); }
External Storage
می توان داد ها را در دستگاه های جانبی مانند SD Card ذخیره نمود.
ولی هنگام write بر روی دستگاه های جانبی فایلی در این مسیر ایجاد خواهد شد:
`/sdcard/Android/data/com.company.app/files/save/appdata/save_appdata`
کد زیر را در نظر بگیرید:
private String filename = "myfile" private String string = "sensitive data such as credit card number" FileOutputStream fos = null; try { File file = new File(getExternalFilesDir(TARGET_TYPE), filename); fos = new FileOutputStream(file, false); fos.write(string.getBytes()); } catch (FileNotFoundException e) { // handle FileNotFoundException } catch (IOException e) { // handle IOException } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { // handle error } } }
برای جلوگیری از این آسیب پذیری می توان از openFileOutput و حالت MODE_PRIVATE به صورت زیر استفاد نمود:
private String filename = "myfile" private String string = "sensitive data such as credit card number" FileOutputStream fos = null; try { fos = openFileOutput(filename, Context.MODE_PRIVATE); fos.write(string.getBytes()); fos.close(); } catch (FileNotFoundException e) { // handle FileNotFoundException } catch (IOException e) { // handle IOException } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { // handle error } } }