Приложение Android извлекает данные с сервера, но безопасным способом

Очевидно, что я не специалист по Android или Java. Что я хочу сделать в своем приложении для Android, загружайте данные с сервера. Я уже работал над этой частью, а исходный код прилагается. Но я хочу сделать это безопасным способом. В качестве первого шага вместо http://thisismyurl.com/a.php?action=get я хочу сделать это с именем пользователя/паролем следующим образом: http://username:[email protected]/a.php?action=get Как мне это сделать? Должен ли я просто добавить имя пользователя и пароль в URL?

Предположим, что я сделал это, это не будет использовать, потому что кто-то может просто открыть apk и декомпилировать исходный код, получить URL-адрес и имя пользователя/пароль. так есть ли действительно безопасный способ сделать это?

Надеюсь, меня поняли здесь.

String url = "http://thisismyurl.com/a.php?action=get";
String result = Web.executeWeb(url);

public class Web {

    public static String executeWeb(final String url) {

        final StringBuilder sb = new StringBuilder();

        Thread thread = new Thread(new Runnable() {
            public void run() 
                {
                try 
                {
                    InputStream is = (InputStream) new URL(url).getContent();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                    String result, line = reader.readLine();
                    result = line;
                    while((line=reader.readLine())!=null){
                        result+=line;
                    }

                    sb.append(result);
                    //System.out.println(result);       
                    //Log.i("My Response :: ", result);

                } catch (Exception e)
                {
                // TODO: handle exception
                }
            }
          });   

        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return sb.toString();

    }   

}

Ответ 1

Начнем с того, что вы должны сначала рассмотреть то, что вы хотите достичь, и как и после этого решить, что вам нужно.

Во-первых, вам должно быть ясно, что злоумышленники попытаются взломать ваше приложение, и если ваше приложение будет хранить финансовые, личные или другие конфиденциальные данные, стойкость будет увеличиваться экспоненциально.

Учитывая сказанное, несколько соображений:

  • Ключи жесткого кодирования в ваш код - плохая идея. Если вы это сделаете, важно, чтобы взломщик расшифровал ключ, который вы использовали.

  • Ключи жесткого кодирования в URL-адресе еще хуже. Имейте в виду, что ваш URL-адрес будет путешествовать по множеству мест до достижения конечной точки (ваш сервер), и тем временем любой, кто имел доступ к этому трафику, будет видеть ваши учетные данные и даже без усилий, поскольку вы отправляете их незашифрованными.

  • В зависимости от того, как вы будете генерировать свои ключи, я бы предложил использовать симметричное или асимметричное шифрование:

    • Если вы планируете хранить уникальный пароль для всех своих клиентов (что, кстати, также плохое, потому что, если злоумышленник нарушает ваш ключ, у них может быть all ваша информация о клиенте), вы можете использовать симметричный метод шифрования, такой как AES. Вы просто шифруете свои сообщения, отправляете их через HTTP POST (например) и расшифровываете их на другой стороне. Довольно простая концепция.

    • Если вы планируете генерировать ключ для каждого из ваших клиентов, у вас есть дополнительный гандикап, который вам нужно, чтобы ваш сервер знал ключ, который вы создали, или ваш клиент знает, какой ключ сгенерирован для клиента (зависит от того, как вы сталкиваетесь с этим). В этом контексте вы можете использовать следующий подход к точкам (который в основном тот, который я бы рекомендовал среди всех этих).

  • Вы можете просто использовать метод ассиметричного шифрования. Это означает, что сервер генерирует пару ключей, один открытый и один закрытый. Пользователи (клиенты) будут иметь публичный доступ для шифрования сообщений и отправки их на сервер. Возможно, вам интересно: а как защитить свои сообщения, чтобы никто не мог их расшифровать, кроме моего сервера? Что, когда закрывается закрытый ключ, вы можете просто расшифровать сообщения, если у вас есть закрытый ключ. Вот почему вы не хотите делиться им с кем-либо (откуда появляется его имя). Таким образом, ваши клиенты могут иметь ваш открытый ключ в любое время без каких-либо проблем с обфускацией, а затем использовать его для шифрования текста и отправки его. Сервер расшифровывает сообщение с помощью его закрытого ключа и обрабатывает его соответствующим образом.

Преимуществами этого последнего подхода являются:

  • Вам не нужно беспокоиться о том, как сделать ваш ключ безопасным для других пользователей, поскольку он будет зашифрован и только сервер сможет расшифровать.

  • Вам не нужно генерировать ключ для каждого из ваших клиентов.

  • Если вы выберете хороший асимметричный алгоритм, такой как SSL/TLS, вам не нужно беспокоиться о его взломе (или, по крайней мере, не так сильно, как если бы вы выбрали другой подход).

  • Замена старой пары ключей так просто, как создание новой пары, замена старого закрытого ключа и создание у ваших клиентов нового открытого ключа.

Возможно, вы захотите ознакомиться с этими ссылками:

Ответ 2

String httpsURL = "https://www.abcd.com/auth/login/";

String query = "email="+URLEncoder.encode("[email protected]","UTF-8"); 
query += "&";
query += "password="+URLEncoder.encode("abcd","UTF-8") ;

URL myurl = new URL(httpsURL);
HttpsURLConnection con = (HttpsURLConnection)myurl.openConnection();
con.setRequestMethod("POST");

con.setRequestProperty("Content-length", String.valueOf(query.length())); 
con.setRequestProperty("Content-Type","application/x-www-form-urlencoded"); 
con.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0;Windows98;DigExt)"); 
con.setDoOutput(true); 
con.setDoInput(true); 

DataOutputStream output = new DataOutputStream(con.getOutputStream());  


output.writeBytes(query);

output.close();

DataInputStream input = new DataInputStream( con.getInputStream() ); 



for( int c = input.read(); c != -1; c = input.read() ) 
System.out.print( (char)c ); 
input.close(); 

System.out.println("Resp Code:"+con .getResponseCode()); 
System.out.println("Resp Message:"+ con .getResponseMessage());

Ответ 3

что я сделал, я использовал для этого шифрование AES. всякий раз, когда пользователь регистрируется, я отправляю ключ шифрования и версию в заголовок к приложению, поэтому вся связь будет зашифрована. Сервер всегда проверяет версию ключа и затем дешифрует соответственно. если новый ключ, доступный сервер, отправит новый ключ в приложение, а затем ключ обновления приложения, а затем расшифрует его.

Я использовал этот метод для дешифрования и шифрования в android.

   public  byte[] decrypt(byte[] cipherText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
    Cipher cipher = Cipher.getInstance(cipherTransformation);
    SecretKeySpec secretKeySpecy = new SecretKeySpec(key, aesEncryptionAlgorithm);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
    cipher.init(Cipher.DECRYPT_MODE, secretKeySpecy, ivParameterSpec);
    cipherText = cipher.doFinal(cipherText);
    return cipherText;
}

public byte[] encrypt(byte[] plainText, byte[] key, byte [] initialVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
    Cipher cipher = Cipher.getInstance(cipherTransformation);
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, aesEncryptionAlgorithm);
    IvParameterSpec ivParameterSpec = new IvParameterSpec(initialVector);
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
    plainText = cipher.doFinal(plainText);
    return plainText;
}

и в запросе добавить заголовок, например

request.addHeader("KeyVersion",String.valueOf(utils.getInt(Key.Key_Version)));
    request.addHeader("EmpId",String.valueOf(utils.getInt(Key.Emp_Id)));

и когда придет ответ, я проверяю новый ключ, например

Header[] headers = response.getHeaders("KeyVersion");  

    if(headers.length>0){
        String keyVersion = headers[0].getValue();
        if (keyVersion == null) {
            System.out.println("Key 'Server' is not found!");
        } else {
                System.out.println("Key 'Server' found! -- with version "+keyVersion);
                if(utils.getInt("KeyVersion")<Integer.parseInt(keyVersion)){
                    utils.saveInt("KeyVersion", Integer.parseInt(keyVersion));
                    utils.saveString("Key", response.getHeaders("KeyValue")[0].getValue());
                    String s = response.getHeaders("KeyValue")[0].getValue();
                    System.out.println("key is "+s);
                }
        }

Ответ 4

Во-первых, никогда не работайте с самим паролем, работайте только с хэш-представлением (функция sha1) пароля. Во-вторых, вы можете использовать SSL (https) для установления безопасного соединения. Чтобы подвести итог, после того, как пользователь нажмет кнопку входа в систему, получите пароль от edittext и создайте хэш. На стороне сервера также используется хеш-представление. Затем отправьте данные как тело запроса https на www.yoursite.com/login, а в теле запроса будут ваши данные. Отправьте его как json f.e...

Ответ 5

Шифрование не является ответом.

Если кто-то хочет URL, пользователя и пароль, которые хранятся на клиенте, вы не можете избежать этого. Даже если он зашифрован, вы должны предоставить ключ дешифрования клиенту, который затем может быть декомпилирован.

Вы не можете предотвратить обратное проектирование вашего сервисного интерфейса.

И поэтому не может препятствовать другим клиентам использовать ваш служебный интерфейс. Легко нюхать сетевой трафик с помощью Fiddler. Даже SSL не проблема, потому что мы можем манипулировать самим клиентом, прежде чем данные будут зашифрованы.

Пусть просмотрите некоторые другие потоки SO об обратном проектировании.

Ответ 6

После ответа nKn:

Один из способов сделать то, что вы хотите сделать, - использовать асимметричное и симметричное шифрование, что вы делаете:

  • Клиент инициирует подключение к серверу
  • Сервер генерирует пару открытых и закрытых ключей для асимметричного шифрования (например, RSA).
  • Сервер отправляет открытый ключ незашифрованным в открытый текст клиенту
  • Клиент генерирует новый несвязанный ключ для симметричного шифрования (например, AES).
  • Клиент использует открытый ключ раньше, чтобы зашифровать ключ симметричного алгоритма и отправить его на сервер.
  • Сервер расшифровывает сообщение с помощью закрытого ключа, и теперь обе стороны имеют общий симметричный ключ для использования.

Этот подход заставляет клиента генерировать новый симметричный ключ каждый раз, поэтому вам нужен ваш сервер для сохранения правильного ключа для каждого соединения/сеанса. Вы не можете использовать этот подход, если хотите статический симметричный ключ, для этого вы можете использовать другой подход:

  • Клиент создает асимметричную пару ключей и отправляет открытый ключ клиенту.
  • Сервер использует открытый ключ для шифрования симметричного ключа и отправки его клиенту.
  • Клиент расшифровывает сообщение с помощью закрытого ключа и как можно скорее уничтожает закрытый ключ. - Теперь обе стороны имеют один и тот же симметричный ключ

При втором подходе вы можете просто сохранить симметричный ключ на своем сервере, и вам не нужно хранить другой ключ для каждого соединения/сеанса. Я бы порекомендовал вам периодически менять симметричный ключ, чтобы быть уверенным.

Оба подхода не заставляют вас вводить жесткие ключи кода в код клиента или отправлять симметричные ключи в виде открытого текста. Единственное, что отправлено в открытом тексте - это открытый ключ, и это совсем не проблема, отсюда и название "открытый ключ".