简体   繁体   English

保护可从Android访问的REST API

[英]securing a REST API accessible from Android

We're building a game for Android, which needs access to web services - so we wrote a RESTful API in PHP that runs on our own server. 我们正在构建一款Android游戏,它需要访问Web服务 - 因此我们在PHP中编写了一个RESTful API,它在我们自己的服务器上运行。 What the API offers is: creating user, logging in, downloading games, retrieving game list, submitting score... etc. Now I'm thinking, if some experienced user gets the URL format of the API - s/he will be able to trash the system in many ways: API提供的是: 创建用户,登录,下载游戏,检索游戏列表,提交分数......等等。现在我想,如果一些有经验的用户获得API的URL格式 - 他/他将能够以多种方式废弃系统:

  • Create a script & run it to create automatic users - I think I can prevent it by CAPTCHA or someting like that. 创建一个脚本并运行它来创建自动用户 - 我想我可以通过CAPTCHA或类似的东西来阻止它。 But again, captcha will annoy game players. 但同样,验证码会惹恼游戏玩家。
  • Malicious user logs in using his browser, downloads game & then submits score as he wish - all via calling the API by simply typing it from his browser. 恶意用户使用他的浏览器登录,下载游戏然后按照自己的意愿提交分数 - 所有这一切都可以通过简单地从浏览器中输入来调用API。 I assume malicious user somehow knows API urls to call - by sniffing when the application was making HTTP requests. 我假设恶意用户以某种方式知道要调用的API URL - 通过在应用程序发出HTTP请求时嗅探。
  • I need to ensure that requests are made only from Android device that installed the game. 我需要确保仅从安装游戏的Android设备发出请求。 (The game will be free) (游戏将免费)

Now How do I prevent such abuses? 现在我该如何防止这种滥用?

I think you will never be able to hide the urls being called by the application (if I am running a root-ed android phone, I should be able to spy on all network traffic) 我想你永远无法隐藏应用程序调用的URL(如果我运行的是ro​​ot用户手机,我应该可以监视所有网络流量)

But your real problem is that you need to authenticate your api in some way. 但你真正的问题是你需要以某种方式验证你的api。

One way would be to implement OAUTH, but maybe this'd be overkill. 一种方法是实施OAUTH,但也许这有点过分。

If you want a simple mechanism, how about this; 如果你想要一个简单的机制,那么这个怎么样;

  1. create a secret key 创建一个密钥
  2. build the api request (eg. https://my.example.com/users/23?fields=name,email ) 构建api请求(例如https://my.example.com/users/23?fields=name,email
  3. hash this request path + plus your secret key (eg. md5(url+secret_key) == "a3c2fe167") 哈希此请求路径+加上您的密钥(例如.md5(url + secret_key)==“a3c2fe167”)
  4. add this hash to your request (now it is https://.....?fields=name,email&hash=a3c2fe167 ) 将此哈希添加到您的请求中(现在它是https:// .....? fields = name,email&hash = a3c2fe167
  5. on the api end, do the same conversion (remove the hash param) 在api端,执行相同的转换(删除哈希参数)
  6. check the md5 of the url and the secret key 检查网址的md5和密钥

As long as the secret remains secret, no one can forge your requests. 只要秘密保密,没有人可以伪造你的要求。

Example (in pseudo-code): 示例(伪代码):

Android side: 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

Server side: 服务器端:

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

Solutions that others have presented here are called security through obscurity . 其他人在这里介绍的解决方案通过默默无闻称为安全性 Basically they are trying to obscure the protocol and hide the implementation. 基本上他们试图掩盖协议并隐藏实现。 This might work until someone capable enough disassembles the app and reverse-engineers the protocol. 这可能会有效,直到有足够能力拆卸应用程序并对协议进行逆向工程。 Hackers are very capable at that. 黑客非常有能力做到这一点。

The question is if your app is worth cracking? 问题是你的应用程序是否值得破解? Schemes like iTunes, DVD or Sony PS3 network were obviously worth the effort. 像iTunes,DVD或索尼PS3网络这样的计划显然是值得的。 The obscurity approach might work if no one capable of cracking cares. 如果没有人能够解决问题,那么默默无闻的方法可能会奏效。 Just don't fool yourself that it is not doeable. 只是不要欺骗自己,这是不可行的。

Since you can not trust the device or your app, you must trust the user. 由于您无法信任该设备或您的应用,因此您必须信任该用户。 In order to trust the user, you need user identification and authorization system. 为了信任用户,您需要用户识别和授权系统。 Basically a login to your app. 基本上是您的应用程序的登录。 Instead rolling you own indentification system (login with confirmation emails, etc..), use a 3rd party system: OpenID (google accounts) or OAuth (facebook, twitter). 而是滚动你自己的识别系统(登录确认电子邮件等),使用第三方系统:OpenID(谷歌帐户)或OAuth(Facebook,推特)。 In case of facebook use the server-side auth scheme. 如果Facebook使用服务器端身份验证方案。

What I'd do: 我要做的是:

  1. Allow users to freely play the game until they want to "save" the results on server. 允许用户自由玩游戏,直到他们想要在服务器上“保存”结果。
  2. Before saving their results have them login via above mentioned method. 在保存结果之前,让他们通过上述方法登录。
  3. Use HTTPS to send the data to your server. 使用HTTPS将数据发送到服务器。 Buy a ssl certificate from trusted CA, so you don't have to deal with self-signed certs. 从受信任的CA购买ssl证书,因此您无需处理自签名证书。

You mentioned users faking the high scores. 你提到用户伪造高分。 This could still happen if your users are authenticated. 如果您的用户经过身份验证,这仍可能发生。 When the game is uploading the high scores you may want to have it also upload a proof of the score. 当游戏上传高分时,您可能希望它也可以上传分数证明。 For example Score 20100 from 103 bugs squished, 1200 miles flown, level 3 reached, and 2 cherries were eaten. 例如,得分为20100,从103个虫子中挤出,飞行1200英里,达到3级,吃掉2个樱桃。 This is by no means perfect but would cover the low hanging fruit. 这绝不是完美的,但会涵盖低悬的果实。

The first you should do is have authenticated users. 您应该做的第一件事是经过身份验证的用户。 Userid/password/session token etc., see if you can find some already existing frameworks. 用户ID /密码/会话令牌等,看看你是否能找到一些已经存在的框架。 Once you have user authentication make sure you can do it securely with TLS or similar. 一旦您拥有用户身份验证,请确保您可以使用TLS或类似功能安全地执行此操作。

As far as I know there is no way your server can be certain that the request is coming from your application (it's all just bits in packets) but you can at least make it hard for someone to be malicious. 据我所知,您的服务器无法确定请求是来自您的应用程序(它只是数据包中的位),但您至少可以让某人变得非常恶意。

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

If you really want to secure the connection then you'll have to use public key cryptography, eg RSA. 如果您确实想要保护连接,那么您将不得不使用公钥加密技术,例如RSA。 The device will encrypt the log in information using the public key and in the server end you will have to decrypt using the private key. 设备将使用公钥加密登录信息,在服务器端,您必须使用私钥解密。 After login the server will send a token/encryption key (the response will be an encrypted JSON or something) and the device will store that. 登录后,服务器将发送令牌/加密密钥(响应将是加密的JSON或其他东西),设备将存储该密钥。 From then as long as the session is not expired the device will send all the information encrypted using that token. 从那时起,只要会话未到期,设备就会发送使用该令牌加密的所有信息。 For this requests you should not use RSA cause that will take more time. 对于此请求,您不应使用需要更多时间的RSA原因。 You can use AES256 (which is a popular private key encryption) with that encryption key received from server to encrypt your requests. 您可以使用AES256(这是一种流行的私钥加密)与从服务器接收的加密密钥来加密您的请求。

For sake of simplicity you can drop RSA altogether (If you are not sending payment information) and do everything using AES256 with a private key. 为简单起见,您可以完全放弃RSA(如果您不发送付款信息)并使用带有私钥的AES256执行所有操作。 The steps should be - 步骤应该是 -

  • Encrypt every outgoing request with a private key. 使用私钥加密每个传出请求。
  • Convert the encrypted string to a base 64 string. 将加密的字符串转换为基本64字符串。
  • URL encode the base 64 encoded string. URL对base 64编码的字符串进行编码。
  • Send it over. 发送过来。

On the server end 在服务器端

  • Do base 64 decode 做base 64解码
  • Decrypt using the private key. 使用私钥解密。

Your request should carry a signature (eg the encryption key appended as a salt) so that it becomes possible to identify it after decrypting. 您的请求应带有签名(例如,附加为盐的加密密钥),以便在解密后识别它。 If the signature is not present simply discard the request. 如果签名不存在,则只丢弃请求。

For sending responses do the same. 对于发送响应也一样。

Android SDK should have methods for Encrypting with AES256 and Base 64 encoding. Android SDK应该具有使用AES256和Base 64编码进行加密的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM