【问题标题】:Android App Retrieve Data from Server but in a Secure wayAndroid 应用程序以安全的方式从服务器检索数据
【发布时间】:2014-05-01 01:42:52
【问题描述】:

显然我不是 android 或 java 专家。我想在我的 Android 应用程序中做的是,从服务器加载数据。我已经开始工作这部分并附上了源代码。但我想以一种安全的方式来做。作为第一步,我想用这样的用户名/密码来代替http://thisismyurl.com/a.php?action=gethttp://username:password@thisismyurl.com/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();

    }   

}

【问题讨论】:

    标签: java android inputstream bufferedreader


    【解决方案1】:
    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());
    

    【讨论】:

    • 如果有人打开android apk文件中的源代码,用户名/密码以及服务器的url是否可见?
    • 加密您的用户名、密码和链接。并对其进行加密
    • 你必须为你的解决方案添加一点解释。
    【解决方案2】:

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

    【讨论】:

    • 我尝试使用密钥确保安全。服务器可以随时生成新密钥。如果有人破解了我们的密钥。我们可以生成一个新的,新的文本将由该新密钥加密或解密。它将无法使用旧密钥解密文本。应用程序将通过标题中的密钥版本了解密钥。
    • 如何发送密钥?在 http 请求/响应的标头中以明文形式显示?
    • yes.in 标题中的纯文本。但只有在有新密钥时才会发送密钥。否则只会发送版本,以便我们可以使用正确的密钥解密。
    • 理论上,如果有人在传输新密钥时“监听”您的网络连接,他们现在就拥有了密钥......
    • 如果他有钥匙,我们可以随时更换。所以旧钥匙将不再使用,它​​成为浪费。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      加密不是答案。

      如果有人想要存储在客户端中的 URL、用户和密码,你无法避免。即使它是加密的,你也必须向客户端提供解密密钥,然后它可以自己反编译。

      您无法阻止对服务接口进行逆向工程。

      因此无法阻止其他客户端使用您的服务接口。使用Fiddler 可以轻松嗅探网络流量。即使 SSL 也没有问题,因为我们可以在数据加密之前操纵客户端本身。

      让我们see some other SO threads about reverse-engineering.

      【讨论】:

        【解决方案5】:

        首先,您应该首先考虑您想要实现什么以及如何实现,然后再决定您需要什么。

        首先,您必须清楚恶意用户尝试破解您的应用,如果您的应用存储财务、个人或其他类型的敏感数据,持久性将成倍增加。 p>

        话虽如此,有几点考虑:

        • 将密钥硬编码到您的代码中是一个糟糕的想法。如果你这样做了,破解者破解你使用的密钥只是时间问题。

        • 在 URL 中硬编码键是一个更糟糕的主意。请记住,在到达终点(您的服务器)之前,您的 URL 将经过很多地方,同时 任何访问该流量的人 都会看到您的凭据,甚至无需像您一样努力。以未加密的方式重新发送。

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

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

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

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

        最后一种方法的优点是:

        • 您不必担心如何让您的密钥安全地到达对方,因为它会被加密并且只有服务器能够解密。

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

        • 1234563其他方法)。
        • 替换旧密钥对就像生成新密钥对、替换旧私钥并让您的客户拥有新公钥一样简单。

        您可能想看看这些链接:

        1. Public-key cryptography

        2. Symmetric-key algorithm

        3. Advanced Encryption Standard (AES)

        4. Transport Layer Security

        【讨论】:

        • 当然还有混合方法...使用非对称算法安全地传输对称算法的密钥...显然这是更多的工作,但这样做有好处,因为作为一般规则非对称算法比对称算法慢得多。所以这样一开始你只会受到一次非对称加密的惩罚。
        • 好的,我想我了解公钥/私钥方法。我还有一个问题。同样,如果有人可以访问源代码,会看到公钥和服务器 ip。他可以将自己的加密消息发送到服务器,服务器使用自己的私钥解密。还是我在这里错了?我也想避免这种情况。如果可能。我只希望我的客户端能够与我的服务器通信。
        • 这里的重点是即使有人决定破解您的应用程序也很难获取信息。所以让我们假设有人真的破解了你的应用程序并获取了你的代码,他可以用你的公钥做什么?他可以将一些加密信息发送到您的服务器,但仅此而已。在服务器端,您可以实现几种机制来处理对您有一定逻辑的信息。 @Bob 的观点也很有趣,我没有提到它,但是不对称算法在处理大量数据时速度很慢,所以混合方法是用 加密对称密钥
        • 非对称密钥,这样你只加密密码短语,它比使用非对称密钥加密整个内容要快得多。
        • @PetrosMastrantonas 使用非对称算法或混合方式的加密将确保 2 个方面:监听您网络的任何人都无法访问您传输的数据,并且此外,即使有人反编译了您的代码,他们仍然无法获取您发送的数据,因为代码没有存储密钥。如果您想确保您的网络服务只能通过您的应用程序访问,那么不幸的是,仅加密对您没有帮助。
        【解决方案6】:

        按照 nKn 的回答:

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

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

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

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

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

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

        【讨论】:

          猜你喜欢
          • 2022-12-13
          • 2023-04-07
          • 1970-01-01
          • 2013-05-19
          • 1970-01-01
          • 1970-01-01
          • 2016-01-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多