Apa cara yang paling tepat untuk menyimpan pengaturan pengguna di aplikasi Android

308

Saya membuat aplikasi yang menghubungkan ke server menggunakan nama pengguna / kata sandi dan saya ingin mengaktifkan opsi "Simpan kata sandi" sehingga pengguna tidak perlu mengetik kata sandi setiap kali aplikasi dimulai.

Saya mencoba melakukannya dengan Preferensi Bersama tetapi saya tidak yakin apakah ini adalah solusi terbaik.

Saya akan menghargai setiap saran tentang cara menyimpan nilai / pengaturan pengguna di aplikasi Android.

Niko Gamulin
sumber

Jawaban:

233

Secara umum SharedPreferences adalah pilihan terbaik untuk menyimpan preferensi, jadi secara umum saya akan merekomendasikan pendekatan itu untuk menyimpan pengaturan aplikasi dan pengguna.

Satu-satunya bidang yang menjadi perhatian di sini adalah apa yang Anda tabung. Kata sandi selalu merupakan hal yang sulit untuk disimpan, dan saya akan sangat berhati-hati menyimpannya sebagai teks yang jelas. Arsitektur Android sedemikian rupa sehingga SharedPreferences aplikasi Anda dikosongkan untuk mencegah aplikasi lain mengakses nilai sehingga ada beberapa keamanan di sana, tetapi akses fisik ke ponsel berpotensi memungkinkan akses ke nilai.

Jika mungkin saya akan mempertimbangkan memodifikasi server untuk menggunakan token yang dinegosiasikan untuk menyediakan akses, sesuatu seperti OAuth . Atau Anda mungkin perlu membangun semacam toko kriptografi, meskipun itu tidak sepele. Paling tidak, pastikan Anda mengenkripsi kata sandi sebelum menuliskannya ke disk.

Reto Meier
sumber
3
Bisakah Anda jelaskan apa yang Anda maksudkan dengan sandbox?
Abhijit
14
program berpasir adalah aplikasi apa pun yang proses dan informasinya (seperti preferensi yang dibagikan itu) tetap tersembunyi dari aplikasi lainnya. Aplikasi android yang berjalan dalam suatu paket tidak dapat secara langsung mengakses apa pun di dalam paket lain. Itu sebabnya aplikasi dalam paket yang sama (yang selalu menjadi milik Anda) dapat mengakses informasi dari yang lain
Korcholis
@Reto Meier persyaratan saya adalah untuk melindungi layanan web yang tersedia untuk umum untuk itu saya menggunakan token, apakah menyimpannya di preferensi bersama aman? saya punya penerima siaran bootup di aplikasi saya yang akan menghapus semua data preferensi-berbagi-pakai jika ditemukan perangkat sebagai root. Apakah ini cukup untuk melindungi token saya.
pyus13
1
Per android-developers.blogspot.com/2013/02/… , kredensial pengguna harus disimpan dengan flag MODE_PRIVATE yang ditetapkan dan disimpan dalam penyimpanan internal (dengan peringatan yang sama tentang menyimpan segala jenis kata sandi yang secara lokal terbuka untuk diserang). Yang mengatakan, apakah menggunakan MODE_PRIVATEdengan SharedPreferences setara dengan melakukan hal yang sama dengan file yang dibuat pada penyimpanan internal, dalam hal efektivitas untuk mengaburkan data yang disimpan secara lokal?
qix
6
Jangan menyimpan kata sandi dalam preferensi bersama. Jika pengguna kehilangan telepon, kata sandi hilang. Itu akan dibaca. Jika mereka menggunakan kata sandi itu di tempat lain, setiap tempat yang mereka gunakan itu dikompromikan. Selain itu, Anda kehilangan akun ini secara permanen karena dengan kata sandi mereka dapat mengubah kata sandi Anda. Cara yang benar untuk melakukan ini adalah mengirim kata sandi ke server satu kali, dan menerima token masuk kembali. Simpan itu dalam preferensi bersama dan kirimkan bersama setiap permintaan. Jika token itu dikompromikan, tidak ada lagi yang hilang.
Gabe Sechan
211

Saya setuju dengan Reto dan FIXedd. Secara obyektif itu tidak masuk akal berinvestasi waktu dan usaha yang signifikan dalam mengenkripsi kata sandi di SharedPreferences karena setiap penyerang yang memiliki akses ke file preferensi Anda kemungkinan besar juga memiliki akses ke biner aplikasi Anda, dan karena itu kunci untuk tidak mengenkripsi kode tersebut. kata sandi.

Namun, yang dikatakan, tampaknya ada inisiatif publisitas untuk mengidentifikasi aplikasi seluler yang menyimpan kata sandi mereka dalam teks yang jelas dalam SharedPreferences dan menyorotkan cahaya yang tidak menguntungkan pada aplikasi tersebut. Lihat http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ dan http://viaforensics.com/appwatchdog untuk beberapa contoh.

Meskipun kita perlu lebih banyak perhatian pada keamanan secara umum, saya berpendapat bahwa perhatian semacam ini pada satu masalah khusus ini sebenarnya tidak secara signifikan meningkatkan keamanan keseluruhan kita. Namun, sebagaimana persepsi mereka, inilah solusi untuk mengenkripsi data yang Anda tempatkan di SharedPreferences.

Cukup bungkus objek SharedPreferences Anda sendiri dengan yang ini, dan data apa pun yang Anda baca / tulis akan dienkripsi dan didekripsi secara otomatis. misalnya.

final SharedPreferences prefs = new ObscuredSharedPreferences( 
    this, this.getSharedPreferences(MY_PREFS_FILE_NAME, Context.MODE_PRIVATE) );

// eg.    
prefs.edit().putString("foo","bar").commit();
prefs.getString("foo", null);

Berikut kode untuk kelas:

/**
 * Warning, this gives a false sense of security.  If an attacker has enough access to
 * acquire your password store, then he almost certainly has enough access to acquire your
 * source binary and figure out your encryption key.  However, it will prevent casual
 * investigators from acquiring passwords, and thereby may prevent undesired negative
 * publicity.
 */
public class ObscuredSharedPreferences implements SharedPreferences {
    protected static final String UTF8 = "utf-8";
    private static final char[] SEKRIT = ... ; // INSERT A RANDOM PASSWORD HERE.
                                               // Don't use anything you wouldn't want to
                                               // get out there if someone decompiled
                                               // your app.


    protected SharedPreferences delegate;
    protected Context context;

    public ObscuredSharedPreferences(Context context, SharedPreferences delegate) {
        this.delegate = delegate;
        this.context = context;
    }

    public class Editor implements SharedPreferences.Editor {
        protected SharedPreferences.Editor delegate;

        public Editor() {
            this.delegate = ObscuredSharedPreferences.this.delegate.edit();                    
        }

        @Override
        public Editor putBoolean(String key, boolean value) {
            delegate.putString(key, encrypt(Boolean.toString(value)));
            return this;
        }

        @Override
        public Editor putFloat(String key, float value) {
            delegate.putString(key, encrypt(Float.toString(value)));
            return this;
        }

        @Override
        public Editor putInt(String key, int value) {
            delegate.putString(key, encrypt(Integer.toString(value)));
            return this;
        }

        @Override
        public Editor putLong(String key, long value) {
            delegate.putString(key, encrypt(Long.toString(value)));
            return this;
        }

        @Override
        public Editor putString(String key, String value) {
            delegate.putString(key, encrypt(value));
            return this;
        }

        @Override
        public void apply() {
            delegate.apply();
        }

        @Override
        public Editor clear() {
            delegate.clear();
            return this;
        }

        @Override
        public boolean commit() {
            return delegate.commit();
        }

        @Override
        public Editor remove(String s) {
            delegate.remove(s);
            return this;
        }
    }

    public Editor edit() {
        return new Editor();
    }


    @Override
    public Map<String, ?> getAll() {
        throw new UnsupportedOperationException(); // left as an exercise to the reader
    }

    @Override
    public boolean getBoolean(String key, boolean defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Boolean.parseBoolean(decrypt(v)) : defValue;
    }

    @Override
    public float getFloat(String key, float defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Float.parseFloat(decrypt(v)) : defValue;
    }

    @Override
    public int getInt(String key, int defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Integer.parseInt(decrypt(v)) : defValue;
    }

    @Override
    public long getLong(String key, long defValue) {
        final String v = delegate.getString(key, null);
        return v!=null ? Long.parseLong(decrypt(v)) : defValue;
    }

    @Override
    public String getString(String key, String defValue) {
        final String v = delegate.getString(key, null);
        return v != null ? decrypt(v) : defValue;
    }

    @Override
    public boolean contains(String s) {
        return delegate.contains(s);
    }

    @Override
    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }

    @Override
    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener onSharedPreferenceChangeListener) {
        delegate.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
    }




    protected String encrypt( String value ) {

        try {
            final byte[] bytes = value!=null ? value.getBytes(UTF8) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(Base64.encode(pbeCipher.doFinal(bytes), Base64.NO_WRAP),UTF8);

        } catch( Exception e ) {
            throw new RuntimeException(e);
        }

    }

    protected String decrypt(String value){
        try {
            final byte[] bytes = value!=null ? Base64.decode(value,Base64.DEFAULT) : new byte[0];
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
            SecretKey key = keyFactory.generateSecret(new PBEKeySpec(SEKRIT));
            Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
            pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(Settings.Secure.getString(context.getContentResolver(),Settings.Secure.ANDROID_ID).getBytes(UTF8), 20));
            return new String(pbeCipher.doFinal(bytes),UTF8);

        } catch( Exception e) {
            throw new RuntimeException(e);
        }
    }

}
emmby
sumber
3
FYI Base64 tersedia di API level 8 (2.2) dan yang lebih baru. Anda dapat menggunakan iharder.sourceforge.net/current/java/base64 atau yang lainnya untuk OS sebelumnya.
emmby
34
Ya, saya menulis ini. Jangan ragu untuk menggunakannya, tidak perlu ada atribusi
emmby
8
Saya setuju denganmu. Tetapi jika kata sandi hanya digunakan di server, mengapa tidak menggunakan enkripsi kunci Publik / pribadi? Kunci publik pada klien saat menyimpan kata sandi. Klien tidak akan pernah harus membaca kata sandi teks yang jelas lagi, kan? Server kemudian dapat mendekripsi dengan kunci privat. Jadi, bahkan jika seseorang menelusuri kode sumber aplikasi Anda, mereka tidak bisa mendapatkan kata sandi, kecuali mereka meretas server Anda dan mendapatkan kunci pribadi.
Patrick Boos
4
Saya telah menambahkan beberapa fitur ke kode ini dan meletakkannya di github di github.com/RightHandedMonkey/WorxForUs_Library/blob/master/src/… . Sekarang menangani migrasi preferensi non-terenkripsi ke preferensi terenkripsi. Juga menghasilkan kunci saat runtime, jadi mendekompilasi aplikasi tidak melepaskan kunci.
RightHandedMonkey
3
Tambahan terlambat, tetapi komentar oleh @PatrickBoos adalah ide bagus. Namun, satu masalah dengan hal ini adalah meskipun Anda telah mengenkripsi kata sandi, penyerang yang mencuri sandi itu masih dapat masuk ke server Anda, karena server Anda melakukan dekripsi. Satu tambahan untuk pendekatan ini adalah mengenkripsi kata sandi bersama dengan stempel waktu. Dengan begitu Anda dapat memutuskan, misalnya, untuk hanya mengizinkan kata sandi yang disimpan di masa lalu (seperti menambahkan tanggal kedaluwarsa ke "token" Anda), atau bahkan mengharuskan pengguna tertentu untuk memiliki stempel waktu sejak tanggal tertentu (biarkan Anda "mencabut" "token" lama).
adevine
29

Tentang cara paling sederhana untuk menyimpan satu preferensi dalam Aktivitas Android adalah dengan melakukan sesuatu seperti ini:

Editor e = this.getPreferences(Context.MODE_PRIVATE).edit();
e.putString("password", mPassword);
e.commit();

Jika Anda khawatir tentang keamanannya, maka Anda selalu dapat mengenkripsi kata sandi sebelum menyimpannya.

Jeremy Logan
sumber
9
Saya sangat setuju dengan Anda tentang pendekatan sederhana ini; namun, Anda harus selalu khawatir tentang keamanan kata sandi yang Anda simpan? Tergantung pada aplikasi Anda, Anda memiliki kewajiban potensial untuk informasi pribadi yang dicuri. Hanya menunjukkan ini kepada siapa pun yang mencoba menyimpan kata sandi aktual untuk hal-hal seperti rekening bank atau sesuatu yang sama pentingnya. Saya masih memilih Anda.
While-E
1
Di mana Anda menyimpan kunci yang menyimpan kata sandi? Jika preferensi bersama dapat diakses oleh pengguna lain, maka kuncinya adalah.
OrhanC1
@ OrhanC1 apakah Anda mendapatkan jawabannya?
eRaisedToX
10

Dengan menggunakan potongan yang disediakan oleh Richard, Anda dapat mengenkripsi kata sandi sebelum menyimpannya. Namun API preferensi tidak menyediakan cara mudah untuk mencegat nilai dan mengenkripsi nilai tersebut - Anda dapat memblokirnya disimpan melalui pendengar OnPreferenceChange, dan Anda secara teoritis dapat memodifikasinya melalui preferensiChangeListener, tetapi itu menghasilkan loop tanpa akhir.

Saya sebelumnya menyarankan menambahkan preferensi "tersembunyi" untuk mencapai ini. Ini jelas bukan cara terbaik. Saya akan menyajikan dua opsi lain yang saya anggap lebih layak.

Pertama, yang paling sederhana, ada di preferChangeListener, Anda bisa mengambil nilai yang dimasukkan, mengenkripsi, dan kemudian menyimpannya ke file preferensi alternatif:

  public boolean onPreferenceChange(Preference preference, Object newValue) {
      // get our "secure" shared preferences file.
      SharedPreferences secure = context.getSharedPreferences(
         "SECURE",
         Context.MODE_PRIVATE
      );
      String encryptedText = null;
      // encrypt and set the preference.
      try {
         encryptedText = SimpleCrypto.encrypt(Preferences.SEED,(String)newValue);

         Editor editor = secure.getEditor();
         editor.putString("encryptedPassword",encryptedText);
         editor.commit();
      }
      catch (Exception e) {
         e.printStackTrace();
      }
      // always return false.
      return false; 
   }

Cara kedua, dan cara yang sekarang saya sukai, adalah membuat preferensi kustom Anda sendiri, memperluas EditTextPreference, @ Mengesampingkan setText()dan getText()metode, sehingga setText()mengenkripsi kata sandi, dan getText()mengembalikan nol.

Menandai
sumber
Saya tahu ini cukup lama, tetapi bisakah Anda memposting kode Anda untuk versi kustom EditTextPreference Anda?
RenniePet
Sudahlah, saya menemukan sampel yang dapat digunakan di sini groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M dan saya membuatnya berfungsi sekarang. Terima kasih telah menyarankan pendekatan ini.
RenniePet
6

Baik; sudah lama sejak jawabannya adalah campuran, tapi inilah beberapa jawaban umum. Saya meneliti ini seperti orang gila dan sulit untuk membangun jawaban yang bagus

  1. Metode MODE_PRIVATE dianggap aman secara umum, jika Anda menganggap bahwa pengguna tidak melakukan root pada perangkat. Data Anda disimpan dalam teks biasa di bagian sistem file yang hanya dapat diakses oleh program asli. Membuat ini mengambil kata sandi dengan aplikasi lain pada perangkat yang di-rooting dengan mudah. Kemudian lagi, apakah Anda ingin mendukung perangkat yang di-rooting?

  2. AES masih merupakan enkripsi terbaik yang dapat Anda lakukan. Ingatlah untuk mencari ini jika Anda memulai implementasi baru jika sudah lama sejak saya memposting ini. Masalah terbesar dengan ini adalah "Apa yang harus dilakukan dengan kunci enkripsi?"

Jadi, sekarang kita berada di "Apa yang harus dilakukan dengan kunci?" bagian. Ini bagian yang sulit. Mendapatkan kunci ternyata tidak terlalu buruk. Anda dapat menggunakan fungsi derivasi kunci untuk mengambil kata sandi dan menjadikannya kunci yang cukup aman. Anda masuk ke masalah seperti "berapa banyak pass yang Anda lakukan dengan PKFDF2?", Tapi itu topik lain

  1. Idealnya, Anda menyimpan kunci AES dari perangkat. Anda harus mencari cara yang baik untuk mengambil kunci dari server dengan aman, andal, dan aman sekalipun

  2. Anda memiliki semacam urutan login (bahkan urutan login asli yang Anda lakukan untuk akses jarak jauh). Anda dapat melakukan dua kali menjalankan generator kunci Anda dengan kata sandi yang sama. Cara kerjanya adalah Anda menurunkan kunci dua kali dengan garam baru dan vektor inisialisasi aman baru. Anda menyimpan salah satu kata sandi yang dihasilkan di perangkat, dan Anda menggunakan kata sandi kedua sebagai kunci AES.

Ketika Anda masuk, Anda mendapatkan kembali kunci pada login lokal dan membandingkannya dengan kunci yang disimpan. Setelah selesai, Anda menggunakan kunci turunan # 2 untuk AES.

  1. Menggunakan pendekatan "umumnya aman", Anda mengenkripsi data menggunakan AES dan menyimpan kunci dalam MODE_PRIVATE. Ini direkomendasikan oleh posting blog Android terbaru. Tidak terlalu aman, tetapi jauh lebih baik bagi sebagian orang daripada teks biasa

Anda dapat melakukan banyak variasi dari ini. Misalnya, alih-alih urutan login penuh, Anda dapat melakukan PIN cepat (berasal). PIN cepat mungkin tidak seaman urutan masuk penuh, tetapi berkali-kali lebih aman daripada teks biasa

Joe Plante
sumber
5

Saya tahu ini sedikit necromancy, tetapi Anda harus menggunakan Manajer Akun Android . Ini dibuat khusus untuk skenario ini. Ini sedikit rumit tetapi salah satu hal yang dilakukannya adalah membatalkan kredensial lokal jika kartu SIM berubah, jadi jika seseorang menggesek ponsel Anda dan melempar SIM baru ke dalamnya, kredensial Anda tidak akan terganggu.

Ini juga memberi pengguna cara cepat dan mudah untuk mengakses (dan berpotensi menghapus) kredensial yang disimpan untuk akun apa pun yang mereka miliki di perangkat, semuanya dari satu tempat.

SampleSyncAdapter adalah contoh yang memanfaatkan kredensial akun yang disimpan.

Jon O
sumber
1
Harap dicatat bahwa menggunakan AccountManager tidak lebih aman daripada metode lain yang disediakan di atas! developer.android.com/training/id-auth/…
Sander Versluys
1
Kasus penggunaan untuk AccountManager adalah ketika akun harus dibagikan antara aplikasi yang berbeda, dan aplikasi dari penulis yang berbeda. Menyimpan kata sandi dan memberikannya ke aplikasi yang meminta tidak akan sesuai. Jika penggunaan pengguna / kata sandi hanya untuk satu aplikasi, jangan gunakan AccountManager.
dolmen
1
@ Astaga, itu tidak sepenuhnya benar. AccountManager tidak akan memberikan kata sandi akun ke aplikasi apa pun yang UID-nya tidak cocok dengan Authenticator. Namanya, ya; token autentik, ya; kata sandi, tidak. Jika Anda mencoba, itu akan membuang SecurityException. Dan use case jauh lebih luas dari itu. developer.android.com/training/id-auth/identify.html
Jon O
5

Saya akan melemparkan topi saya ke atas ring hanya untuk berbicara tentang mengamankan kata sandi secara umum di Android. Pada Android, biner perangkat harus dianggap dikompromikan - ini sama untuk aplikasi akhir mana pun yang berada dalam kendali pengguna langsung. Secara konseptual, seorang hacker dapat menggunakan akses yang diperlukan ke biner untuk mendekompilasi dan membasmi kata sandi terenkripsi Anda dan lain-lain

Karena itu ada dua saran yang ingin saya buang jika keamanan menjadi perhatian utama Anda:

1) Jangan menyimpan kata sandi yang sebenarnya. Simpan token akses yang diberikan dan gunakan token akses dan tanda tangan ponsel untuk mengautentikasi sisi server sesi. Manfaatnya adalah Anda dapat membuat token memiliki durasi terbatas, Anda tidak kompromi kata sandi asli dan Anda memiliki tanda tangan yang baik yang dapat Anda gunakan untuk berkorelasi dengan lalu lintas (misalnya untuk memeriksa upaya intrusi dan membatalkan token rendering itu tidak berguna).

2) Memanfaatkan otentikasi 2 faktor. Ini mungkin lebih menyebalkan dan mengganggu tetapi untuk beberapa situasi kepatuhan tidak dapat dihindari.

dcgregorya
sumber
2

Ini adalah jawaban tambahan bagi mereka yang tiba di sini berdasarkan judul pertanyaan (seperti yang saya lakukan) dan tidak perlu berurusan dengan masalah keamanan terkait dengan menyimpan kata sandi.

Cara menggunakan Preferensi Bersama

Pengaturan pengguna umumnya disimpan secara lokal di Android menggunakan SharedPreferencesdengan pasangan nilai kunci. Anda menggunakan Stringkunci untuk menyimpan atau mencari nilai terkait.

Menulis ke Preferensi Bersama

String key = "myInt";
int valueToSave = 10;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(key, valueToSave).commit();

Gunakan apply()alih-alih commit()menyimpan di latar daripada langsung.

Baca dari Preferensi Bersama

String key = "myInt";
int defaultValue = 0;

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(context);
int savedValue = sharedPref.getInt(key, defaultValue);

Nilai default digunakan jika kunci tidak ditemukan.

Catatan

  • Daripada menggunakan String kunci lokal di banyak tempat seperti yang saya lakukan di atas, akan lebih baik menggunakan konstanta di satu lokasi. Anda dapat menggunakan sesuatu seperti ini di bagian atas aktivitas pengaturan Anda:

      final static String PREF_MY_INT_KEY = "myInt";
  • Saya menggunakan intdalam contoh saya, tetapi Anda juga dapat menggunakan putString(), putBoolean(), getString(), getBoolean(), dll

  • Lihat dokumentasi untuk lebih jelasnya.

  • Ada beberapa cara untuk mendapatkan SharedPreferences. Lihat jawaban ini untuk apa yang harus diwaspadai.

Suragch
sumber
1

Jawaban ini didasarkan pada pendekatan yang disarankan oleh Mark. Versi kustom dari kelas EditTextPreference dibuat yang mengubah bolak-balik antara teks biasa yang terlihat dalam tampilan dan versi terenkripsi dari kata sandi yang disimpan dalam penyimpanan preferensi.

Seperti yang telah ditunjukkan oleh sebagian besar yang menjawab di utas ini, ini bukan teknik yang sangat aman, meskipun tingkat keamanan sebagian tergantung pada kode enkripsi / dekripsi yang digunakan. Tapi itu cukup sederhana dan nyaman, dan akan menggagalkan pengintaian yang paling kasual.

Berikut adalah kode untuk kelas EditTextPreference kustom:

package com.Merlinia.OutBack_Client;

import android.content.Context;
import android.preference.EditTextPreference;
import android.util.AttributeSet;
import android.util.Base64;

import com.Merlinia.MEncryption_Main.MEncryptionUserPassword;


/**
 * This class extends the EditTextPreference view, providing encryption and decryption services for
 * OutBack user passwords. The passwords in the preferences store are first encrypted using the
 * MEncryption classes and then converted to string using Base64 since the preferences store can not
 * store byte arrays.
 *
 * This is largely copied from this article, except for the encryption/decryption parts:
 * https://groups.google.com/forum/#!topic/android-developers/pMYNEVXMa6M
 */
public class EditPasswordPreference  extends EditTextPreference {

    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context) {
        super(context);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
    }


    // Constructor - needed despite what compiler says, otherwise app crashes
    public EditPasswordPreference(Context context, AttributeSet attributeSet, int defaultStyle) {
        super(context, attributeSet, defaultStyle);
    }


    /**
     * Override the method that gets a preference from the preferences storage, for display by the
     * EditText view. This gets the base64 password, converts it to a byte array, and then decrypts
     * it so it can be displayed in plain text.
     * @return  OutBack user password in plain text
     */
    @Override
    public String getText() {
        String decryptedPassword;

        try {
            decryptedPassword = MEncryptionUserPassword.aesDecrypt(
                     Base64.decode(getSharedPreferences().getString(getKey(), ""), Base64.DEFAULT));
        } catch (Exception e) {
            e.printStackTrace();
            decryptedPassword = "";
        }

        return decryptedPassword;
    }


    /**
     * Override the method that gets a text string from the EditText view and stores the value in
     * the preferences storage. This encrypts the password into a byte array and then encodes that
     * in base64 format.
     * @param passwordText  OutBack user password in plain text
     */
    @Override
    public void setText(String passwordText) {
        byte[] encryptedPassword;

        try {
            encryptedPassword = MEncryptionUserPassword.aesEncrypt(passwordText);
        } catch (Exception e) {
            e.printStackTrace();
            encryptedPassword = new byte[0];
        }

        getSharedPreferences().edit().putString(getKey(),
                                          Base64.encodeToString(encryptedPassword, Base64.DEFAULT))
                .commit();
    }


    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        if (restoreValue)
            getEditText().setText(getText());
        else
            super.onSetInitialValue(restoreValue, defaultValue);
    }
}

Ini menunjukkan bagaimana itu dapat digunakan - ini adalah file "item" yang mendorong tampilan preferensi. Perhatikan itu berisi tiga tampilan EditTextPreference biasa dan salah satu tampilan EditPasswordPreference kustom.

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">

    <EditTextPreference
        android:key="@string/useraccountname_key"
        android:title="@string/useraccountname_title"
        android:summary="@string/useraccountname_summary"
        android:defaultValue="@string/useraccountname_default"
        />

    <com.Merlinia.OutBack_Client.EditPasswordPreference
        android:key="@string/useraccountpassword_key"
        android:title="@string/useraccountpassword_title"
        android:summary="@string/useraccountpassword_summary"
        android:defaultValue="@string/useraccountpassword_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverip_key"
        android:title="@string/outbackserverip_title"
        android:summary="@string/outbackserverip_summary"
        android:defaultValue="@string/outbackserverip_default"
        />

    <EditTextPreference
        android:key="@string/outbackserverport_key"
        android:title="@string/outbackserverport_title"
        android:summary="@string/outbackserverport_summary"
        android:defaultValue="@string/outbackserverport_default"
        />

</PreferenceScreen>

Adapun enkripsi / dekripsi aktual, yang tersisa sebagai latihan untuk pembaca. Saat ini saya menggunakan beberapa kode berdasarkan artikel ini http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/ , meskipun dengan nilai yang berbeda untuk kunci dan vektor inisialisasi.

RenniePet
sumber
1

Pertama-tama saya pikir data Pengguna tidak boleh disimpan di ponsel, dan jika harus menyimpan data di suatu tempat di telepon, data itu harus dienkripsi dengan data pribadi aplikasi. Keamanan kredensial pengguna harus menjadi prioritas aplikasi.

Data sensitif harus disimpan dengan aman atau tidak sama sekali. Jika terjadi infeksi perangkat atau malware yang hilang, data yang disimpan dengan tidak aman dapat dikompromikan.

Yauraw Gadav
sumber
1

Saya menggunakan Android KeyStore untuk mengenkripsi kata sandi menggunakan RSA dalam mode ECB dan kemudian menyimpannya di SharedPreferences.

Ketika saya ingin kata sandi kembali, saya membaca yang dienkripsi dari SharedPreferences dan mendekripsi menggunakan KeyStore.

Dengan metode ini Anda menghasilkan pasangan kunci publik / pribadi di mana kunci pribadi disimpan dan dikelola oleh Android.

Berikut ini tautan tentang cara melakukannya: Tutorial KeyStore Android

Braveblacklion
sumber
-2

preferensi bersama adalah cara termudah untuk menyimpan data aplikasi kami. tetapi ada kemungkinan bahwa siapa pun dapat menghapus data preferensi bersama kami melalui manajer aplikasi. jadi saya pikir itu tidak sepenuhnya aman untuk aplikasi kita.

Rohit Jain
sumber
-3

Anda perlu menggunakan sqlite, security apit untuk menyimpan kata sandi. di sini adalah contoh terbaik, yang menyimpan kata sandi, - kata sandi aman. di sini adalah tautan untuk sumber dan penjelasan - http://code.google.com/p/android-passwordsafe/

Artem Russakovskii
sumber
3
OP perlu menyimpan satu pasangan nama pengguna dan kata sandi. Akan konyol untuk mempertimbangkan membuat seluruh tabel database untuk penggunaan yang satu ini
HXCaine
@HXCaine saya dengan hormat tidak setuju - saya dapat melihat setidaknya 1 penggunaan lain dari tabel sqlite pengguna / kata sandi. JIKA ANDA MEMPERTIMBANGKAN RISIKO (menggunakan sqlite) DITERIMA, selain otentikasi login aplikasi sederhana, Anda bisa menggunakan tabel untuk menyimpan beberapa kata sandi ftp (jika aplikasi Anda menggunakan ftp - ranjau kadang-kadang lakukan), misalnya. ditambah, membuat kelas adaptor sqlite untuk manipulasi ini sederhana.
tony gil
Kebangkitan komentar dua tahun yang bagus! Agar adil, komentar saya adalah setahun setelah jawaban :) Bahkan dengan beberapa kata sandi FTP, biaya overhead jauh lebih besar dengan tabel SQLite daripada dengan SharedPreferences baik dalam hal ruang dan pengkodean. Tentunya itu tidak perlu
HXCaine