通过哈希保护Android应用程序中的API URL访问

10

在我的Android应用程序中,用户可以提交内容到数据库,其他所有用户都可以看到这些内容。

这些新内容通过GET请求发送到服务器:

http://www.example.org/API.php?newContent=helloWorld

问题是:如果用户知道这个URL的样子,他可以很容易地在浏览器中发送恶意请求并绕过Android应用程序的访问限制。也许有人可以反编译应用程序并找出URL的信息。

我该如何保护这个URL的访问,并防止用户直接访问此API?

在应用程序中生成哈希值然后与服务器上API.php文件中生成的哈希值进行比较,这是一个好的解决方案吗?

当反编译应用程序时,不能找到哈希值的生成方式吗?

非常感谢您的帮助!

4个回答

20

要真正保护URL的唯一方法是需要对所有请求进行身份验证。

其中一种方法是将请求更改为POST请求并附带某种身份认证令牌(一个简单的哈希即可)。如果没有提供身份认证令牌,则不会响应该请求。哈希值是您将在客户端和服务器中都硬编码的内容。

现在的问题是如何隐藏您的身份认证令牌。只要您没有开源您的代码,别人获取它的唯一途径就是反编译您的程序,正如您所提到的。为了防范这种情况,您可能需要考虑使用proguard (http://developer.android.com/guide/developing/tools/proguard.html)。

需要记住的一点是,此方法存在单点故障。如果您的身份认证令牌暴露了,你就完蛋了(例如HD DVD AACS加密键危机)。

另一个身份验证的方法是按用户进行身份验证。只要请求来自有效用户,您实际上不需要关心请求是来自Web浏览器还是Android应用程序。我认为这是做事情的更好方式。通过这样做,您可以按用户限制请求速率。但这需要您管理用户配置文件以及与之相关的所有问题。

总之,最后需要注意的是,您不需要真正关心某个API部分的URL是否已知。我不了解您的特定用例,但一定有一种方式可以设计您的API,以便您不必关心如何获取您的请求。此外,如果您正在执行真正的GET操作,则不应更改服务器上的任何内容。这意味着所有“恶意人士”可以做的就是从中获取数据。这严重限制了他们可以造成的损害。实际上,除非您有不想让某些人查看的敏感数据,否则您根本没有问题。如果确实存在这样的敏感数据,则应认真考虑我的按用户进行身份验证的解决方案。


谢谢您提供这么详细的答案 :) 您说我应该只使用POST来进行修改服务器上数据的API调用。但是我的做法是:如果用户想要“点赞”项目123,我会调用:http://www.example.org/API.php?item=123&vote=up 这样做是否不好?让我困扰的是,任何人都可以轻松地查找生成身份验证令牌的函数,当反编译应用程序时,不是吗?因此,每个“攻击者”都可以轻松地自己生成正确的哈希值! - caw
两件事。首先,那不是“正确”的方法。就像我说的,你应该只在不会引起变化的情况下使用GET。我应该能够输入example.org/API.php?item=123&vote=up并获得一致的网页。其次,如果你是为用户做这个,那么你肯定应该使用我的每个用户身份验证方法。从而只允许有效的用户投票。就像我在我的答案中所说的,如果你真的担心别人看你的代码,你可以使用progaurd。 - Kurtis Nusbaum
1
非常感谢您再次发表评论!可能是一个愚蠢的问题,但是:为什么在修改操作时应该使用POST而不是GET?我在安全方面看不到优势...还有:Proguard只是让阅读您的代码更加困难,但它并不能使其变得不可能;)其他应用程序如何处理Android的哈希生成? - caw
1
如果你真的想知道POST和GET的区别,你可以在谷歌上搜索。我找到了这篇文章,从我的浏览来看,它提供了一个很好的解释:http://www.cs.tut.fi/~jkorpela/forms/methods.html 。至于Proguard,你永远不可能完全使你的代码混淆。我不知道有什么方法可以使Java完全100%无法反编译。这是有道理的,因为最终计算机仍然必须对其进行解码和运行,因此代码始终会有一定的顺序。 - Kurtis Nusbaum
感谢这个好链接!使用GET仅用于检索信息,而使用POST用于对服务器进行更新只是一种建议。当然,这是一个合理的建议。区分GET和POST的主要原因是,如果尝试刷新POST站点,则浏览器会显示警告消息。但在API中,这并不重要;)虽然我应该转换为POST,但我的问题仍然是开放的:Android应用程序中的哈希函数是否安全? - caw
1
@MarcoW。哈希函数在安卓平台上和其他任何平台上一样安全。 - Kurtis Nusbaum

2
不要相信客户端进行验证。无论是在Web浏览器中的JavaScript,还是像iPhone这样的一些受限平台中都是如此。
如果应用程序可以进行API调用,那么显然所有需要进行这些调用的内容都在手机上(秘密、哈希函数、API密钥等),然后某人总是可以转储手机存储并获取所有这些数据。他们随后可以发起任何请求。
您需要做的是对用户进行身份验证,然后在服务器端验证输入。

谢谢你的回答 :) 当然,我不想在客户端验证API调用。但是你回答中的其余部分正是我所关心的。那么,在你看来,用户认证是保护Android应用程序访问API的唯一方法吗? - caw

1

在数据传输中使用SSL(HTTPS)。在任何数据发送之前,交换都会被加密,因此任何监听者都无法看到发送到服务器的URL或数据。为了自行验证这一点,请在开发系统上安装Wireshark并将URL加载到浏览器中。您将不会看到任何明文数据(GET或POST方式发送的URL或数据)。


谢谢,HTTPS是一个很好的概念,可以防止他人监听发送到服务器的机密信息。但它并不能解决哈希生成函数可被反向工程的问题。 - caw
您可以添加身份验证,如哈希或HTTP身份验证,或基于cookie的身份验证,或OAuth等等。但如果没有加密,那么很可能会轻松绕过身份验证。 - Slartibartfast
当你仅向服务器发送哈希进行身份验证时,如何绕过此身份验证?仅通过听取发送到服务器的身份验证令牌,您无法推断出这些哈希值是如何生成的(下一次)。 - caw

0

您可以使用一种有些混淆的Java方法来混淆URL的每个字母。这样以某种方式创建自己的字典,使得如果有人对APK进行反编译,URL可能会出现为123.3*15*13或类似的形式,他们将毫无头绪。在这方面,您最好使用Proguard来混淆它,这样您的混淆对于试图逆向工程的人就没有意义了。

您可以像这样制作一个简短的Java方法:

public String confuseString() {
    Stringbuilder sb = new StringBuilder();
    //your real URL would be abc.com, but in the app you have myURL = 123.3*15*13
    //With what I'm saying the * would precede a 2 digit number that represents 1 letter
     for (int i = 0; i < stringLength; i++){
         String letter = myURL.charAt(i);
        if (letter.equals("1"){
            letter = a;
            sb.append(letter);
        } // you would go one to code each character into a letter
     }

}

当然,会有更多的if语句,但这将允许您混淆您的URL而不进行任何服务器端更改。如果您使用Proguard,那么您创建的整个方法对于试图反向工程的人来说将毫无意义。

当然,您可以使您的混淆比我建议的更加复杂,但这只是一个想法。

基本上,您将以非常令人困惑的方式加密URL。

这里有一个答案,可能是更好的加密方法,或者至少提供额外的加密:

Java - encrypt / decrypt user name and password from a configuration file


非常感谢您提供的详细答案和有趣的方法 :) 但是我不能只使用任何加密算法吗?另外,当有人试图对代码进行逆向工程时,他也会发现这个混淆函数,并且他会理解如何将混淆的字符串翻译回来,对吗? - caw
是的,您可以使用任何加密算法。当然,加密算法越复杂越好。从技术上讲,有人可能会对代码进行反向工程,但在大多数情况下,这将非常困难,因为Proguard使代码非常难以阅读。因此,如果您正确实现了加密,仍然可能被反向工程,但这将使它变得更加困难。 - Reed
谢谢你的补充评论,Jakar!我非常感激你的想法。但是我还没有明白为什么这个函数应该比ProGuard更难逆向工程?你能解释一下吗? - caw
不客气。:) 你添加的代码越多,它就越难以理解,从而使代码变得更加混乱。在我的示例中,我使用了 letter.equals("1"),但如果你在类体中定义 final String xqt = "1";,然后使用 letter.equals(xqt),那么我相信 Proguard 将把 xqt 放入不同的类中,并以模糊的方式引用它。但基本上,你添加的越多,它就越难以理解,因此也就越难以反向工程化。 - Reed
尽管混淆代码是一种好的做法,但最好还是采用某种用户登录方式并设置密码要求。这样,只有有效用户才能进行投票或其他操作。 - Reed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接