保护可从Android访问的REST API

我们正在为Android构build一个需要访问Web服务的游戏 – 所以我们在PHP中编写了一个RESTful API,它运行在我们自己的服务器上。 API提供的是: 创build用户,login,下载游戏,检索游戏列表,提交分数等等。现在我想,如果一些有经验的用户获得API的URL格式 – 他/他将能够以多种方式垃圾系统:

  • 创build一个脚本并运行它来创build自动用户 – 我想我可以通过CAPTCHA或类似的东西来阻止它。 但是captcha会再次令玩家烦恼。
  • 恶意用户使用他的浏览器login,下载游戏,然后按照他的意愿提交分数 – 所有这些都是通过从浏览器中直接input来调用API。 我假设恶意用户以某种方式知道API URL调用 – 通过在应用程序发出HTTP请求时嗅探。
  • 我需要确保请求只能从安装游戏的Android设备进行。 (游戏将免费)

现在我如何防止这种滥用?

我认为你将永远无法隐藏应用程序所调用的URL(如果我正在运行一个root-android android手机,我应该可以监视所有的networkingstream量)

但是你真正的问题是你需要以某种方式validation你的api。

一种方法是实现OAUTH,但也许这是矫枉过正的。

如果你想要一个简单的机制,那么这个怎么样;

  1. 创build一个密钥
  2. build立API请求(例如https://my.example.com/users/23?fields=name,email
  3. 散列这个请求path+加上你的密钥(例如.md5(url + secret_key)==“a3c2fe167”)
  4. 添加这个散列到你的请求(现在是https:// …..?fields = name,email&hash = a3c2fe167 )
  5. 在API结束,做同样的转换(删除散列参数)
  6. 检查URL的MD5和密钥

只要秘密保密,任何人都无法伪造你的要求。

示例(以伪代码forms):

Android方面:

SECRET_KEY = "abc123" def call_api_with_secret(url, params) # create the hash to sign the request hash = MD5.hash(SECRET_KEY, url, params) # call the api with the added hash call_api(url+"&hash=#{hash}", params) end 

服务器端:

 SECRET_KEY = "abc123" def receive_from_api(url, params) # retrieve the hash url_without_hash, received_hash = retrieve_and_remove_hash(url) # check the hash expected_hash = MD5.hash(SECRET_KEY, url_without_hash, params) if (expected_hash != received_hash) raise our exception! end # now do the usual stuff end 

其他人在这里提出的解决scheme称为安全通过默默无闻 。 基本上他们试图掩盖协议并隐藏实现。 这可能会工作,直到有人能够足够的反汇编应用程序和反向工程的协议。 黑客非常有能力。

问题是如果你的应用程序是值得开裂的? 像iTunes,DVD或索尼PS3networking等计划显然是值得的。 如果没有人能够开始关心,那么晦涩的方法可能会奏效。 只是不要欺骗自己,这是不可行的。

由于您不能相信设备或您的应用程序,您必须信任用户。 为了信任用户,需要用户识别和授权系统。 基本上是login到您的应用程序。 而是使用第三方系统:OpenID(谷歌帐户)或OAuth(脸谱,微博)来滚动您自己的识别系统(使用确认电子邮件等进行login)。 在Facebook的情况下使用服务器端的authenticationscheme。

我会做什么:

  1. 允许用户自由地玩游戏,直到他们想在服务器上“保存”结果。
  2. 保存结果之前,他们通过上述方法login。
  3. 使用HTTPS将数据发送到您的服务器。 从受信任的CA购买SSL证书,因此您不必处理自签名证书。

你提到用户伪造高分。 如果您的用户被authentication,这仍然可能发生。 当游戏上传高分时,您可能需要上传分数certificate。 例如,来自103个臭虫的得分20100,飞行1200英里,达到3级,吃了2颗樱桃。 这绝不是完美的,但会覆盖低垂的果实。

首先你应该做的是有authentication的用户。 用户名/密码/会话令牌等,看看你是否可以find一些已经存在的框架。 一旦你有用户身份validation,确保你可以用TLS或类似的安全。

据我所知,你的服务器无法确定请求是否来自你的应用程序(这只是数据包中的一小部分),但你至less可以让别人难以恶意。

  • 在你的应用程序中build立一个秘密(如其他响应,密钥,散列盐等)
  • 在安装后的第一次执行应用程序时生成一个唯一的ID,并跟踪login的用户。 有关这个和设备的唯一ID(为什么不使用它)的详细信息可以在Android博客上find
  • 在这篇文章中讨论的一些想法如何确保/确定一个post来自iPhone / iTouch上运行的特定应用程序?
  • 检查用户代理

如果你真的想保证连接,那么你将不得不使用公钥密码学,例如RSA。 设备将使用公钥对login信息进行encryption,在服务器端,您将不得不使用私钥解密。 login后,服务器将发送一个令牌/encryption密钥(响应将是一个encryption的JSON或什么),设备将存储。 从那时起,只要会话没有过期,设备就会发送使用该令牌encryption的所有信息。 对于这个请求,你不应该使用RSA的原因,这将需要更多的时间。 您可以使用AES256(这是一种stream行的私钥encryption)和从服务器收到的encryption密钥来encryption您的请求。

为了简单起见,您可以完全放弃RSA(如果您没有发送付款信息),并使用AES256和私钥完成所有操作。 步骤应该是 –

  • 使用私钥encryption每个传出的请求。
  • 将encryption的string转换为基本64string。
  • URL编码基本64编码的string。
  • 发送它。

在服务器端

  • 做基地64解码
  • 使用私钥解密。

你的请求应该带有一个签名(例如encryption的密钥作为一个盐),以便在解密后可以识别它。 如果签名不存在,只需放弃请求。

发送回应也一样。

Android SDK应该有用AES256和Base64编码进行encryption的方法。

遵循Android团队的这些指导原则 ,通过使用通过Google API提供的Oauth令牌来保护您的后端。