2011-10-03 103 views
24

我们正在为Android构建一个需要访问Web服务的游戏 - 因此我们在PHP中编写了一个RESTful API,该API运行在我们自己的服务器上。什么API提供的是:创建用户,登录,下载游戏,检索游戏列表,提交得分...等现在我想,如果一些有经验的用户获取URL格式的API - s /他就能垃圾的系统在许多方面:保护可从Android访问的REST API

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

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

回答

27

我认为你将永远无法隐藏的URL由应用程序 叫(我是否正在运行一个根版Android手机,我应该能够窥探所有网络通信)

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

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

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

  1. 创建秘密密钥
  2. 构建的API请求(如:https://my.example.com/users/23?fields=name,email
  3. 哈希这个请求路径+加你的密钥(例如MD5(网址+ SECRET_KEY)== “a3c2fe167”)
  4. 该散列添加到您的要求(现在是https://.....?fields=name,email&hash=a3c2fe167
  5. 的API结束,做相同的转换(删除散列PARAM)
  6. 检查URL的MD5和秘密密钥

只要秘密保守秘密,没有人可以伪造你的请求。

例(伪代码):

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 
+1

非常感谢,@ matthew-rudy :)我是否为每个用户创建了“密钥”?如果那样,我该如何分配它?如果我在注册后将密钥发送给应用程序作为响应,则可以简单地从他的桌面浏览器进行API调用,并将密钥作为响应。 – giga

+6

在马修的方法中,秘密密钥绝不会通过电线发送。这是一个“盐”,附加到URL来创建一个哈希。由于哈希是单向函数,因此用户无法从中恢复密钥。它需要用户反编译APK并查看源代码以找到密钥。您可以使用代码混淆技术来使这变得更加困难,但只要有足够的时间和动力,人们总能找到钥匙。 –

+3

确实@JasonLeBrun,我添加了一个伪代码示例。但是,千兆,我建议你在PHP中搜索现有的框架,而不是创建自己的框架。我确定必须有许多现有的解决方案。 –

1

如果你真的要保护的连接,那么你就必须使用公钥密码学,例如RSA。设备将使用公钥对登录信息进行加密,并且在服务器端您必须使用私钥解密。登录后,服务器将发送令牌/加密密钥(响应将是加密的JSON或其他内容),设备将存储该令牌。从那时起,只要会话未过期,设备就会发送使用该令牌加密的所有信息。对于这个请求,你不应该使用需要更多时间的RSA原因。您可以使用AES256(这是一种流行的私钥加密)和从服务器收到的加密密钥来加密您的请求。

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

  • 使用私钥加密每个传出的请求。
  • 将加密的字符串转换为基本64字符串。
  • 对基本64编码字符串进行URL编码。
  • 发送。

在服务器端

  • DO基站64解码
  • 解密使用私钥。

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

对于发送回复也是这样。

Android SDK应该具有使用AES256和Base 64编码进行加密的方法。

+0

那么HTTPS会更好吗? –

+0

我从来没有告诉过它。如果您没有HTTPS认证,这是一种解决方法。 – rushafi

+0

如果您不愿意从CA购买证书几美元,您可以使用HTTPS自签名证书。你也提出了一个私钥解决方案,所以这在技术上是相同的,只是更难实施。 –

3

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

您应该做的第一件事就是通过身份验证的用户。 Userid /密码/会话令牌等,看看你是否可以找到一些已经存在的框架。一旦你有用户身份验证,确保你可以用TLS或类似的方法安全地做到这一点。

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

+0

感谢您的评论。不是上传总分,而是将其分成几个部分,听起来不那么有害。 – giga

+0

@giga这不仅仅是分手。您希望他们提交分数,并证明提交的分数是由知道分数如何工作(您的应用程序会知道这一点)生成的。如果你只是分割它,那么恶意用户仍然可以提交任意值。你想迫使他们不得不对你的应用程序进行反向工程,而不是只是窥探通信。 – goldsz

7

,其他人已经在这里提出的解决方案是讨论的一些想法被发现称为security through obscurity。基本上他们试图掩盖协议并隐藏实现。这可能会工作,直到有人能够足够的反汇编应用程序和反向工程协议。黑客非常有能力。

问题是如果你的应用程序是值得开裂的?像iTunes,DVD或索尼PS3网络等计划显然是值得的。如果没有人能够解决问题,那么默默无闻的方法可能会奏效。只是不要欺骗自己,这是不可行的。

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

我会做什么:

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

按照these guidelines从Android团队,以确保你的后端,通过使用通过谷歌的API提供OAuth凭证。