如何仅为受信任的移动应用程序维护REST API


96

如何确保我的REST API仅响应受信任客户端(对于我自己的移动应用程序)生成的请求?我想防止来自其他来源的不必要的请求。我不希望用户填写序列号或其他任何东西,它应该在安装后在后台进行,并且不需要任何用户交互。

据我所知,HTTPS仅用于验证与之通信的服务器的身份。我当然要使用HTTPS加密数据。

有没有办法做到这一点?

更新: 用户可以执行只读操作,这些操作不需要用户登录,但可以执行写操作,这些操作也需要用户登录(通过访问令牌进行身份验证)。在这两种情况下,我都希望API响应仅来自受信任的移动应用程序的请求。

该API也将用于通过移动应用程序注册新帐户。

更新2:似乎有多个答案,但是老实说,我不知道将哪个标记为答案。有人说可以做到,有人说不能做到。


HTTPS使用SSL(和TLS)。SSL / TLS可以与客户端身份验证一起使用。
2013年

您是指客户端SSL证书吗?我认为这是我正在寻找的,除了我不知道在移动应用程序(Android和iOS)中是否还可以实现?客户证书将存储在哪里?设备存储,内存?
Supercell

SSL仅会认证移动设备,而不认证移动应用程序。
2013年

@Supercell:我加入了一个答案
ATK

Answers:


48

你不能。

您永远无法验证实体,任何实体,无论是个人,硬件客户端还是软件客户端。您只能验证他们说的是正确的,然后假设为诚实

例如,Google如何知道我正在登录我的Gmail帐户?他们只是问我的用户名和密码,验证那个,然后 承担诚实,因为还有谁就会有信息?在某个时候,Google认为这还不够,并添加了行为验证(寻找奇怪的行为),但这仍然依赖于该执行该行为,然后验证该行为

这与验证客户端完全相同。您只能验证客户端的行为,而不能验证客户端本身。

因此,使用SSL,您可以验证客户端是否具有有效的证书,因此,您可以简单地安装您的应用,获取证书,然后运行所有新代码。

因此,问题是:为什么这如此关键?如果这是一个真正的问题,我会质疑您对胖客户的选择。也许您应该使用Web应用程序(这样就不必公开API)。

另请参阅:击败Android应用程序的SSL证书验证

和:客户端SSL证书在移动应用中的安全性如何?


1
我在硬件上使用了客户端证书,该证书存储在操作系统加密的驱动器上。但是即使在那儿,也没人相信它是万无一失的。目标只是使休闲用户难以使用。
史蒂芬·本纳普

1
@Morons:Web应用程序可以解决此问题,但我们认为用户比Web应用程序更可能使用本机应用程序(如果我们的假设错误,请更正我)。之所以如此重要,是因为API使用户可以访问我们数据库的各个部分,其中包含我们经过数月的工作收集的大量数据。这是其他公司或用户可以轻松用于自己目的的数据。如果没有保护客户,我们就不会知道谁在使用它(反对我们)。
Supercell

6
Webapp无法解决问题。修改任何webapp客户端并使其随心所欲是相当琐碎的。
史蒂芬·本纳普

5
@Supercell您无法显示某人数据,然后阻止他们共享数据。如果您不希望某些人拥有数据,则不要将其提供(显示)给他们。
2013年

我同意,但出于不同的原因。如果您可以控制设备,则有点像rsa,例如en.wikipedia.org/wiki/SecurID。但是手机不是可以控制的东西(它们可以接受附件,例如插件密钥等)。
imel96

31

我确信您会适应用户登录以及通过SSL进行的通信,因此,我将重点介绍我认为这是问题中最有趣的部分:如何确保只读操作-这根本要求用户进行身份验证-只能从自己的客户端应用程序接受?

首先,fNek在较早的答案中指出了一个缺点-您的客户端应用程序可能会受到潜在敌对用户的控制。可以检查它们,检查它们的通信,反汇编其代码。我要提出的任何建议都不能保证您不会有人对您的客户端进行反向工程并滥用您的REST API。但这应该在任何随意尝试之前设置障碍。

无论如何,一种常见的方法是:

  • 客户端包含一个秘密
  • 发出请求时,它将请求参数与密码连接起来,并对结果进行哈希处理
  • 该哈希与请求一起发送,并由服务器检查

例如,想象一个GET请求/products/widgets

假设客户机密是“ OH_HAI_I_IZ_SECRET”

连接HTTP动词,URL和密码:

GET/products/widgetsOH_HAI_I_IZ_SECRET

并采用SHA-1哈希值:

4156023ce06aff06777bef3ecaf6d7fdb6ca4e02

然后将其发送,因此请求将用于:

GET /products/widgets?hash=4156023ce06aff06777bef3ecaf6d7fdb6ca4e02

最后,为防止某人至少重播单个请求,请同时添加一个时间戳,并将其添加到参数和哈希中。例如,在Unix时间,现在是1384987891。将其添加到串联中:

GET/products/widgetsOH_HAI_I_IZ_SECRET1384987891

哈希:

2774561d4e9eb37994d6d71e4f396b85af6cacd1

并且寄出:

GET /products/widgets?time=1384987891&hash=2774561d4e9eb37994d6d71e4f396b85af6cacd1

服务器将检查哈希值,并还验证时间戳记是否为最新时间戳(例如,在5分钟内允许时钟不完全同步)。

警告!由于您在谈论移动应用程序,因此肯定存在某人的手机时钟错误的风险。或时区错误。或者其他的东西。将时间添加到哈希中可能会破坏一些合法用户,因此请谨慎使用该想法。


6
任何程序员在拆解apk时都可以理解这种哈希机制。
Punith Raj

8
确实,@ PunithRaj在第二段中进行了介绍。“我要提出的任何建议都不能保证您不会有人对您的客户端进行反向工程并滥用您的REST API。但这在任何偶然的尝试面前都应设置障碍。”
Carson63000

对于警告,正在服务器和移动设备上使用UTC,这可以解决问题,对吗?
shareef

@ Carson63000-那么,有没有具体的解决方案?特别是对于必须公开开放的用户注册API(用户需要先注册才能登录,无论是在Web上还是在移动应用程序上),并且机器人可以将其作为目标以创建成千上万的假用户。
Tohid

17

对于任何有兴趣的人,您都可以在Android上验证您收到的请求是从您的应用发送的。

简而言之,当您将应用程序上载到Google时,您需要使用只有您(和Google)才知道的唯一密钥对其进行签名。

验证过程如下:

  1. 您的应用转到Google并要求提供身份验证令牌
  2. 您的应用将令牌安全地发送到您的后端
    1. 您的后端转到Google并检查它从您的应用程序获取的身份验证令牌。
    2. 然后您的后端检查您的应用程序已签名的唯一密钥是否匹配,如果不匹配,则表明您的应用程序不是...

完整的博客对其进行了解释以及如何实现,请参见:http : //android-developers.blogspot.co.il/2013/01/verifying-back-end-calls-from-android.html


1
好的答案,但是恶意用户仍然可以尽力而为。但是没有什么是真正安全的,这不是时间的问题,而是时间的问题
mateos

1
对于iOS,有此选项:链接 DeviceCheck API还可让您验证收到的令牌是否来自已下载应用程序的真实Apple设备
Iwaz

它需要帐户(电子邮件)
user25

5

好的,因此在我开始之前值得一提的是,对于大多数应用程序来说,这太过分了。在大多数情况下,仅拥有一个有效的证书和/或令牌就足够了。如果涉及到诸如反编译应用程序之类的艰巨任务,那么即使您没有提供一些非常有价值的数据,即使是大多数黑客也不会打扰。但是,嘿,答案的乐趣在哪里?

因此,您可以做的是建立不对称加密,有点像用于对程序进行签名数字签名。然后,每个应用程序都可以具有由单个CA颁发并在用户连接时进行验证的单独证书。(无论是首次注册还是首次安装时)在对该证书进行身份验证之后,您可以通过将该证书注册为对一个给定的设备标识符(例如Android ID)有效来进一步保护您的应用程序


5

正如@Morons在他的回答中提到的那样,很难验证连接另一端的实体。

提供某种程度的真实性的最简单方法是让服务器检查一些只有真实实体才知道的秘密。对于用户,可能是用户名和密码。对于没有用户的软件,您可能会嵌入一个秘密。

这些方法的问题在于,您必须在客户端中建立一些信任。如果有人对您的应用程序进行逆向工程或窃取您的密码,他们可能会假扮您。

您可以采取措施,通过在可执行文件中对其进行混淆来使其更难提取秘密信息。ProGuard之类的工具是Java的混淆器,可以帮助解决这一问题,我对其他语言的混淆知之甚少,但可能还有类似的工具。使用TLS连接有助于防止人们窥探您的流量,但不能阻止MITM攻击。固定可以帮助解决该问题。

我为一家名为CriticalBlue(完全披露!)的公司工作,该公司的产品名为Approov,试图解决这一信任问题。它目前适用于Android / iOS,并为我们的服务器提供了一种机制来检查客户端应用程序的完整性。它通过让客户端计算对随机挑战的响应来实现。客户端必须使用已安装的应用程序包的属性来计算响应,这些属性很难伪造,并且包括一些复杂的防篡改机制。

它返回一个令牌,您可以将其作为真实性证明发送到您的API。

这种方法的重要区别在于,尽管可以在客户端上禁用真实性检查,但是如果这样做,则不会获得需要使用服务器验证应用程序的身份验证令牌。该库还与它所在的可执行文件的特性紧密相关,因此很难将其嵌入到伪造的应用程序中并使之正常工作。

任何API开发人员都必须进行成本/收益分析,以决定某人试图破解其API的可能性以及成本如何。在应用程序中进行简单的秘密检查可以防止琐碎的攻击,但是保护自己免受更坚定的攻击者的侵害可能要复杂得多,而且成本可能很高。


0

SSL将保护通信通道。

成功登录将通过加密连接发出身份验证令牌。

身份验证令牌将在所有后续请求中传递到您的REST API。


1
我添加了一些其他信息。我打算像您提到的那样使用访问令牌进行身份验证。REST API不需要用户登录,仅用于特定操作。在这两种情况下,都需要对客户端进行签名/信任。
Supercell

我对原生移动开发了解不多,但是您的移动应用程序可以为REST API提供移动号码吗?当我在Android手机上安装应用程序时,经常会要求我授予该应用程序某些权限。如果您可以通过安全连接向每个请求发送一个手机号码,那么您可以拒绝所有手机号码未知的请求。只是在这里大声思考...
CodeART

这可能有效,但是这将要求用户登录并将该号码绑定到该用户帐户。否则,服务器将没有任何可用来验证数字的依据。
Supercell

您将如何安装移动应用程序?
CodeART

他们将可以通过应用商店(谷歌,苹果,微软)
超级单体

0

它不是太安全,但是您可以添加某种秘密代码,甚至是数字签名。缺点:它必须包含在应用程序中,如果您知道自己的工作,就可以轻松获取它。


0

据我所知,HTTPS仅用于验证与之通信的服务器的身份。

实际上,您可以使用SSL来验证客户端和服务器。或者换句话说,“是的,您可以使用客户端证书”。

你需要...

  • 查看您用来确定如何在移动设备中指定客户端证书的SSL库,
  • 编写代码或配置HTTPS服务器,使其仅接受来自受信任的注册客户端的连接。
  • 提出了一种将受信任的客户端证书添加到服务器中的机制
  • 提出了一种从服务器中删除不再受信任的客户端证书的机制

您可以让移动应用程序将证书存储在任意位置。由于需要特定于应用程序的身份验证,因此应考虑将证书存储在受保护的磁盘位置(在Android上,您可以在SQLite数据库中创建一个“ config”表,并为证书创建一行,为私钥创建一行) 。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.