Jaki jest najbardziej odpowiedni sposób przechowywania ustawień użytkownika w aplikacji na Androida

Tworzę aplikację, która łączy się z serwerem za pomocą nazwy użytkownika/hasła i chciałbym włączyć opcję "Zapisz hasło", aby użytkownik nie musiał wpisywać hasła przy każdym uruchomieniu aplikacji.

Próbowałem to zrobić ze współdzielonymi preferencjami, ale nie jestem pewien, czy jest to najlepsze rozwiązanie.

Byłbym wdzięczny za wszelkie sugestie, jak przechowywać wartości/ustawienia użytkownika w aplikacji na Androida.

Author: chiragkyada, 2009-04-24

13 answers

Ogólnie SharedPreferences są najlepszym rozwiązaniem do przechowywania preferencji, więc ogólnie polecam takie podejście do zapisywania ustawień aplikacji i użytkownika.

Jedynym problemem jest to, co oszczędzasz. Hasła są zawsze trudną rzeczą do przechowywania, a ja byłbym szczególnie ostrożny w przechowywaniu ich jako wyraźnego tekstu. Architektura Androida jest taka, że SharedPreferences aplikacji są piaskownicy, aby uniemożliwić innym aplikacjom dostęp do wartości tak jest tam pewne zabezpieczenie, ale fizyczny dostęp do telefonu może potencjalnie umożliwić dostęp do wartości.

Jeśli to możliwe, rozważyłbym zmodyfikowanie serwera tak, aby używał negocjowanego tokena do zapewnienia dostępu, coś w stylu OAuth . Alternatywnie może być konieczne zbudowanie pewnego rodzaju sklepu kryptograficznego, choć nie jest to trywialne. Przynajmniej upewnij się, że szyfrujesz hasło przed zapisaniem go na dysk.

 225
Author: Reto Meier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-24 16:20:35

Zgadzam się z Reto I fiXedd. Obiektywnie rzecz biorąc, to nie ma sensu inwestowanie dużo czasu i wysiłku w szyfrowanie haseł w SharedPreferences, ponieważ każdy atakujący, który ma dostęp do pliku preferencji jest dość prawdopodobne, aby również mieć dostęp do aplikacji binarnej, a zatem Klucze do odszyfrowania hasła.

Jednak, biorąc to pod uwagę, wydaje się, że istnieje inicjatywa reklamowa dotycząca identyfikacji aplikacji mobilnych, które przechowują ich hasła w cleartext w SharedPreferences i świeci niekorzystnym światłem na tych aplikacjach. Zobacz http://blogs.wsj.com/digits/2011/06/08/some-top-apps-put-data-at-risk/ i http://viaforensics.com/appwatchdog na kilka przykładów.

Chociaż potrzebujemy więcej uwagi poświęconej bezpieczeństwu w ogóle, uważam, że tego rodzaju uwaga na ten jeden konkretny problem nie zwiększa w rzeczywistości naszego ogólnego bezpieczeństwa. Jednak postrzeganie jest takie, jakie są, oto rozwiązanie do szyfrowania danych umieszczanych w SharedPreferences.

Po prostu owinąć swój własny obiekt SharedPreferences w tym, a wszystkie dane, które odczytasz/zapisasz, zostaną automatycznie zaszyfrowane i odszyfrowane. np.

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);

Oto kod dla klasy:

/**
 * 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);
        }
    }

}
 202
Author: emmby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-13 17:09:14

Najprostszym sposobem na przechowywanie pojedynczej preferencji w aktywności Androida jest zrobienie czegoś takiego:

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

Jeśli martwisz się o ich bezpieczeństwo, zawsze możesz zaszyfrować hasło przed jego zapisaniem.

 28
Author: Jeremy Logan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 17:26:22

Używając fragmentu dostarczonego przez Richarda, możesz zaszyfrować hasło przed jego zapisaniem. API preferencji nie zapewnia jednak łatwego sposobu przechwycenia wartości i zaszyfrowania jej - można zablokować jej zapisywanie za pomocą słuchacza OnPreferenceChange, a teoretycznie można ją modyfikować za pomocą preferenceChangeListener, ale skutkuje to nieskończoną pętlą.

Wcześniej zasugerowałem dodanie" ukrytej " preferencji, aby to osiągnąć. To zdecydowanie nie jest najlepszy sposób. I ' m przedstawię dwie inne opcje, które uważam za bardziej realne.

Najpierw, najprostszy, jest w preferenceChangeListener, możesz pobrać wpisaną wartość, zaszyfrować ją, a następnie zapisać ją do alternatywnego pliku preferencji:

  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; 
   }

Drugi sposób, i sposób, który teraz preferuję, to tworzenie własnych preferencji, Rozszerzanie EditTextPreference, @ Override'ing metod setText() i getText(), tak aby setText() szyfrowało hasło, a getText() zwracało null.

 9
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-14 12:36:21

Wiem, że to trochę nekromancji, ale powinieneś użyć Androida AccountManager . Jest specjalnie stworzony do tego scenariusza. Jest to trochę kłopotliwe, ale jedną z rzeczy, które robi, jest unieważnienie lokalnych poświadczeń, jeśli karta SIM się zmieni, więc jeśli ktoś przesunie Twój telefon i wrzuci do niego nową kartę SIM, Twoje poświadczenia nie zostaną naruszone.

Daje to również użytkownikowi szybki i łatwy sposób dostępu (i potencjalnie usunięcia) przechowywanych poświadczeń dla dowolnego konta mają na urządzeniu, wszystko z jednego miejsca.

SampleSyncAdapter {[2] } jest przykładem, który wykorzystuje dane uwierzytelniające przechowywane konto.

 5
Author: Jon O,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-04 18:04:40

Ok; minęło trochę czasu, odkąd odpowiedź jest trochę mieszana, ale oto kilka typowych odpowiedzi. Zbadałem to jak szalony i trudno było zbudować dobrą odpowiedź

  1. Metoda MODE_PRIVATE jest ogólnie uważana za bezpieczną, jeśli założysz, że użytkownik nie wykorzenił urządzenia. Twoje dane są przechowywane w postaci zwykłego tekstu w części systemu plików, do której dostęp ma tylko oryginalny program. To sprawia, że chwytanie hasła z inną aplikacją na zakorzenionym urządzeniu jest łatwe. / Align = "left" / , czy chcesz wspierać zrootowane urządzenia?

  2. AES jest nadal najlepszym szyfrowaniem, jakie możesz zrobić. Pamiętaj, aby to sprawdzić, jeśli rozpoczynasz nową implementację, jeśli minęło trochę czasu od opublikowania tego. Największym problemem jest "co zrobić z kluczem szyfrującym?"

Więc teraz jesteśmy na "co zrobić z kluczem?"porcja. To najtrudniejsza część. Zdobycie klucza nie jest takie złe. Możesz użyć funkcji wyprowadzania klucza, aby wziąć jakieś hasło i zrobić to dość bezpieczny klucz. Dostajesz się do problemów typu " ile przepustek robisz z PKFDF2?", ale to już inny temat

  1. Najlepiej przechowywać klucz AES poza urządzeniem. Musisz wymyślić dobry sposób, aby odzyskać klucz z serwera bezpiecznie, niezawodnie i bezpiecznie

  2. Masz jakąś sekwencję logowania (nawet oryginalną sekwencję logowania, którą robisz dla zdalnego dostępu). Można zrobić dwa działa generator kluczy na tym samym hasłem. Jak to działa jest aby uzyskać klucz dwa razy z nowym salt i nowym bezpiecznym wektorem inicjalizacji. Jedno z wygenerowanych haseł przechowujesz na urządzeniu i używasz drugiego hasła jako klucza AES.

Po zalogowaniu, ponownie pobiera się klucz z lokalnego loginu i porównuje go z przechowywanym kluczem. Gdy to zrobisz, używasz klucza derive # 2 dla AES.

  1. używając "ogólnie bezpiecznego" podejścia, szyfrujesz dane za pomocą AES i przechowujesz klucz w MODE_PRIVATE. Jest to zalecane przez ostatni wpis na blogu Androida. Niezbyt bezpieczne, ale o wiele lepsze dla niektórych osób niż zwykły tekst

Można zrobić wiele odmian tych. Na przykład zamiast pełnej sekwencji logowania można wykonać Szybki PIN (wyprowadzony). Szybki PIN może nie być tak bezpieczny jak pełna sekwencja logowania, ale jest wielokrotnie bezpieczniejszy niż zwykły tekst

 5
Author: Joe Plante,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-14 13:30:50

Rzucę czapkę na ring, żeby porozmawiać o zabezpieczeniu haseł w ogóle na Androidzie. Na Androidzie binarne urządzenie powinno być uważane za zagrożone-to samo dotyczy każdej aplikacji końcowej, która jest pod bezpośrednią kontrolą Użytkownika. Koncepcyjnie haker może użyć niezbędnego dostępu do pliku binarnego, aby go dekompilować i wykorzenić zaszyfrowane hasła itp.

W związku z tym są dwie propozycje, które chciałbym wyrzucić, jeśli Bezpieczeństwo jest dla ciebie poważnym problemem:

1) Nie przechowuj rzeczywistego hasła. Przechowuj przyznany token dostępu i użyj tokenu dostępu i podpisu telefonu, aby uwierzytelnić sesję po stronie serwera. Zaletą tego jest to, że możesz sprawić, że token będzie miał ograniczony czas, nie naruszysz oryginalnego hasła i masz dobry podpis, którego możesz użyć do późniejszego skorelowania z ruchem (na przykład sprawdzania prób włamania i unieważniania tokenu, czyniąc go bezużytecznym).

2) uwierzytelnianie. Może to być bardziej irytujące i natrętne, ale w niektórych sytuacjach nieuniknione zgodności.

 5
Author: dcgregorya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-30 17:00:00

Możesz również sprawdzić ten mały lib, zawierający funkcjonalność, o której wspomniałeś.

Https://github.com/kovmarci86/android-secure-preferences

Jest podobny do innych aproachów tutaj. Nadzieja pomaga:)

 2
Author: Marcell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-21 21:51:40

jest to odpowiedź uzupełniająca dla osób przybywających tutaj w oparciu o tytuł pytania (tak jak ja) i nie muszą zajmować się kwestiami bezpieczeństwa związanymi z zapisywaniem haseł.

Jak korzystać z wspólnych preferencji

Ustawienia użytkownika są zazwyczaj zapisywane lokalnie w systemie Android za pomocą SharedPreferences z parą klucz-wartość. Używasz klucza String, Aby zapisać lub wyszukać powiązaną wartość.

Zapis do wspólnych preferencji

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

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

Użyj apply() zamiast commit() aby zapisać w tło, a nie od razu.

Odczyt z wspólnych preferencji

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

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

Wartość domyślna jest używana, jeśli klucz nie został znaleziony.

Uwagi

  • Zamiast używać Lokalnego ciągu kluczy w wielu miejscach, jak to zrobiłem powyżej, lepiej byłoby użyć stałej w jednym miejscu. Możesz użyć czegoś takiego w górnej części aktywności ustawień:

    final static String PREF_MY_INT_KEY = "myInt";
    
  • W moim przykładzie użyłem int, ale można również użyć putString(), putBoolean(), getString(), getBoolean(), itd.

  • Zobacz dokumentacja aby uzyskać więcej szczegółów.
  • istnieje wiele sposobów, aby uzyskać SharedPreferences. Zobacz ta odpowiedź na co zwrócić uwagę.
 2
Author: Suragch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-25 06:24:23

Ta odpowiedź opiera się na podejściu sugerowanym przez Marka. Tworzona jest niestandardowa wersja klasy EditTextPreference, która konwertuje pomiędzy zwykłym tekstem widocznym w widoku a zaszyfrowaną wersją hasła przechowywaną w magazynie preferencji.

Jak zauważyła większość osób, które odpowiedziały w tym wątku, nie jest to bardzo bezpieczna technika, chociaż stopień bezpieczeństwa zależy częściowo od użytego kodu szyfrującego / deszyfrującego. Ale to dość proste i wygodny i pokrzyżuje najbardziej przypadkowe węszenie.

Oto kod dla niestandardowej klasy EditTextPreference:

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);
    }
}

Pokazuje, jak można go używać - jest to plik "items", który napędza wyświetlanie preferencji. Uwaga zawiera trzy zwykłe widoki EditTextPreference i jeden z niestandardowych widoków EditPasswordPreference.

<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>

Jeśli chodzi o rzeczywiste szyfrowanie/deszyfrowanie, to pozostaje to jako ćwiczenie dla czytelnika. Obecnie używam kodu opartego na tym artykule http://zenu.wordpress.com/2011/09/21/aes-128bit-cross-platform-java-and-c-encryption-compatibility/, choć z różnymi wartościami dla klucza i wektora inicjalizacji.

 1
Author: RenniePet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-06 05:49:10

Przede wszystkim uważam, że dane użytkownika nie powinny być przechowywane w telefonie, a jeśli jest to konieczne, aby przechowywać dane gdzieś w telefonie powinny być szyfrowane z w aplikacjach prywatnych danych. Bezpieczeństwo poświadczeń użytkowników powinno być priorytetem aplikacji.

Dane wrażliwe powinny być przechowywane bezpiecznie lub wcale. W przypadku utraty urządzenia lub infekcji złośliwym oprogramowaniem, dane przechowywane w sposób niebezpieczny mogą zostać naruszone.

 1
Author: Gauraw Yadav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-03 10:40:56

Musisz użyć SQLite, apit bezpieczeństwa do przechowywania haseł. oto najlepszy przykład, który przechowuje hasła, -- passwordsafe. oto link do źródła i wyjaśnienia -- http://code.google.com/p/android-passwordsafe/

 -2
Author: Artem Russakovskii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-11 08:45:40

Współdzielone Preferencje to najprostszy sposób na przechowywanie Danych naszej aplikacji. ale jest możliwe, że każdy może wyczyścić nasze wspólne dane preferencji za pomocą aplikacji manager.so nie sądzę, że jest to całkowicie bezpieczne dla naszej aplikacji.

 -2
Author: Rohit Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-18 08:41:05