繁体   English   中英

Android应用从服务器以安全方式检索数据

[英]Android App Retrieve Data from Server but in a Secure way

显然我不是android或java专家。 我要在Android应用程序中执行的操作是从服务器加载数据。 我已经开始处理此部分,并附带了源代码。 但是我想以一种安全的方式来做。 第一步,不是使用http://thisismyurl.com/a.php?action=get ,而是要使用用户名/密码,例如: http://username:password@thisismyurl.com/a.php?action=get我将如何做? 我应该只将用户名和密码部分添加到网址中吗?

可以说我已经实现了这一点,因为有人可以打开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();

    }   

}

首先,您应该首先考虑要实现的目标以及如何以及随后确定需要的目标。

首先,您必须清楚恶意用户尝试破解您的应用程序,并且如果您的应用程序存储财务,个人或其他类型的敏感数据,则持久性将呈指数级增长。

话虽这么说,但有几点注意事项:

  • 将密钥硬编码到代码中是个主意。 如果这样做,那么解密者破译使用的密钥只是时间问题。

  • 对URL中的键进行硬编码甚至是一个更糟糕的主意。 请记住,您的URL在到达终点(您的服务器)之前会经过很多地方,同时, 任何能够看到该流量的人都可以看到您的凭据,即使您不加加密地发送它们也毫不费力。

  • 根据您将如何生成密钥,我建议使用对称或非对称加密:

    • 如果您打算为所有客户端存储一个唯一的密码(顺便说一句,这也是一个坏主意,因为如果恶意用户破坏了您的密钥,他们可能会拥有您所有客户端的信息),则可以使用对称加密方法像AES。 您只需对消息进行加密,然后通过HTTP POST发送消息(例如),然后在另一端将其解密。 很简单的概念。

    • 如果您计划为每个客户生成密钥,那么您还需要以其他方式使服务器知道您生成的密钥,或者让客户知道为客户生成了哪个密钥(取决于您面对的方式)。它)。 在这种情况下,您可以使用“下一步”方法(基本上是我从所有这些方法中推荐的方法)。

  • 您可以简单地使用非对称加密方法。 这意味着服务器生成一对密钥,一个是公共密钥,一个是私有密钥。 用户(客户端)将拥有公用的加密消息并发送到服务器的消息。 您可能想知道:我该如何保护我的消息,以便除了我的服务器之外没有人可以解密它们? 那就是私钥加入的地方,如果您拥有私钥,则只能解密消息。 这就是为什么您不希望与任何人共享它的原因(这就是它的名字来源)。 这样,您的客户可能会随时拥有您的公钥,而不会产生任何混淆需求,然后您可以使用它来加密一些文本并发送。 服务器将使用其私钥解密消息并进行相应处理。

最后一种方法的优点是:

  • 您不必担心如何使您的密钥安全地到达另一方,因为它将被加密并且只有服务器才能解密。

  • 您无需为每个客户生成密钥。

  • 如果选择良好的非对称算法(例如SSL / TLS),则无需担心其破解(或者至少不像选择其他方法那样担心)。

  • 替换旧的密钥对很容易,例如生成新的密钥对,替换旧的私有密钥并让您的客户拥有新的公共密钥。

您可能想看看这些链接:

  1. 公钥密码学

  2. 对称密钥算法

  3. 高级加密标准(AES)

  4. 传输层安全

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

String query = "email="+URLEncoder.encode("abc@xyz.com","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());

我所做的是为此使用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);
                }
        }

首先,永远不要使用密码本身,而只能使用密码的哈希表示(sha1函数)。 其次,您可以使用SSL(https)建立安全连接。 总结一下,在用户单击登录按钮后,从edittext获取密码并创建它的哈希。 在服务器端也使用哈希表示。 接下来,将数据作为https请求的正文发送到www.yoursite.com/login,请求正文中的将是您的数据。 发送为json fe ..

加密不是答案。

如果有人想要将URL,用户名和密码存储在客户端中,则无法避免。 即使已加密,您也必须向客户端提供解密密钥,然后可以将其自身反编译。

您不能阻止对服务接口进行反向工程。

因此,不能阻止其他客户端使用您的服务界面。 使用Fiddler嗅探网络流量很容易。 甚至SSL也没问题,因为我们可以在数据加密之前操纵客户端本身。

让我们看看其他有关逆向工程的SO线程。

接下来是nKn的答案:

做您想做的一种方法是同时使用非对称和对称加密,您要做的是:

  1. 客户端启动与服务器的连接
  2. 服务器生成用于非对称加密的一对公钥和私钥(例如RSA)。
  3. 服务器将未加密的公钥以明文形式发送给客户端
  4. 客户端为对称加密(例如AES)生成一个新的无关密钥。
  5. 客户端使用以前的公钥加密对称算法密钥并将其发送到服务器。
  6. 服务器使用私钥解密消息,现在双方都有一个通用的对称密钥可以使用。

这种方法迫使客户端每次都生成一个新的对称密钥,因此您需要服务器为每个连接/会话保留正确的密钥。 如果需要静态对称密钥,则不能使用此方法,为此,可以使用另一种方法:

  1. 客户端生成非对称密钥对,并将公共密钥发送给客户端。
  2. 服务器使用公共密钥加密对称密钥并将其发送给客户端。
  3. 客户端使用私钥解密消息,并尽快销毁私钥。 -现在双方共享相同的对称密钥

使用第二种方法,您只需将对称密钥存储在服务器上,而不必为每个连接/会话存储不同的密钥。 我仍然建议您定期更改对称密钥,以确保。

两种方法都不会强迫您将密钥硬编码到客户端代码中,也不会以明文形式发送对称密钥。 以明文形式发送的唯一内容是公钥,这根本不是问题,因此名称为“公钥”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM