如何在 Android 中保护调用 web 服务
Posted
技术标签:
【中文标题】如何在 Android 中保护调用 web 服务【英文标题】:How to secure calling webservice in Android 【发布时间】:2014-10-19 19:27:35 【问题描述】:您好,我正在开发 android 应用程序,在该应用程序中我需要执行一些 https Web 服务,因此我所有的 Web 服务 URL 和 Web API KEY 都在代码和服务器的 IP 地址中。当任何人对我的应用程序进行逆向工程时,那个人可以获取我的 Web 服务 URL 以及 API KEY,然后可以使用 rest 客户端简单地点击它。
如何确保任何攻击者无法获得我在strings.xml
中定义的WEB API KEY
<string name="WEB_API_KEY">XXXXXXXXXXXXXXXXXXXXXXXXXXX</string>
提前致谢。
【问题讨论】:
根本不可能区分来自您的应用的请求和攻击者的请求。 这根本不可能。 如果您的应用可以读取该字符串,那么可以运行该应用的攻击者也可以读取该字符串。如果您的应用程序可以发出请求,攻击者可以发出相同的请求,而您无法区分。你到底希望做什么? 您的 API KEY 将在您的代码中。所以每个人都可以反编译你的应用程序并阅读它。您无法保护它,因为您的应用需要访问它。 是的。完全不可能做到这一点。相反,将密钥放在您的服务器中,并验证请求以确保它们不会做您不想要的事情。 【参考方案1】:我也遇到过同样的问题。 首先确保这是我创建的应用程序仅调用 Web 服务。即使他们通过崇敬工程获得了钥匙。 其次,有效用户正在调用应用程序。 以下检查在服务器中完成。
1)验证它确实是由 Google 签名的。
2)验证它是否真的适合你。
你需要使用谷歌开发者控制台https://console.developers.google.com/project?authuser=0
在 Menu API & auth 下创建两个客户端 ID(一个用于服务器,另一个用于 android 应用程序。)。
要为 android 应用程序创建客户端 ID,您可以使用 keytool
keytool -exportcert -alias <your-key-name> -keystore <your-key-store-file> -v -list
我已经按照here的步骤进行了
服务器端 php 示例如下
function checkSession($token)
$result = Array();
if(isset($_SERVER['HTTPS']))
if ($_SERVER["HTTPS"] == "on")
$secure_connection = true;
if($secure_connection)
try
$client = new Google_Client();
$client->setClientId(CLIENT_ID);
$client->setClientSecret(CLIENT_SECRET);
$ticket = $client->verifyIdToken($token);
$validtocken = false;
if($ticket)
$token_data = $ticket->getAttributes();
if($token_data ["payload"]["aud"]==CLIENT_ID &&
$token_data ["payload"]["azp"]==ANDROID_ID)
$validtocken = true;
$result["Email"]=$token_data ["payload"]["email"];
else
log_message(serialize($token_data));
catch (Exception $e)
$result["Details"]=$e->getMessage();
【讨论】:
它是否只适用于 AppEngine 或任何服务器? 代码在php中,可以在任何支持php的服务器上运行。该实用程序功能也可用于其他编程语言,如 ruby 和 java。谷歌自己提供这些。【参考方案2】:最好的方法是 在首次打开应用时从服务器接收 Web api 密钥。
在这种情况下,应用程序将密钥存储在内部存储中,而不是在 apk 中。
【讨论】:
黑客可以点击该 url 获取 web api 密钥,然后访问其他 URL 接入服务可以添加imei参数。你必须为每个imei号生成web api密钥。并在其他 url 访问时同时检查(imei 和 web api 密钥)。 这是一个原型,所以你必须在发送请求之前加密所有参数细节【参考方案3】:您不能依赖代码中硬编码的内容来识别请求是来自您的应用还是来自恶意客户端。您必须做的是让 web 服务在接受任何请求之前需要某种身份验证。一个例子是需要用户名/密码对,或者根据他们的 SSL 证书让 web 服务接受/拒绝客户端。这样只有授权用户才能使用网络服务。
【讨论】:
但是如果我也使用 ssl 证书并根据客户端拒绝/接受,那么攻击者可以创建另一个 android 应用程序,他们也可以从中访问 Web API。同样,这将是不安全的方式 SSL 证书将只提供给授权客户端,并且不会在应用程序中进行硬编码。这适用于只有授权用户才能使用服务的场景 但是如果我将该 ssl 证书复制到我的项目中,那么他们在进行逆向工程时可以从中获取该证书文件 也许我没有说清楚。 SSL 证书不会在应用程序内分发。那将是愚蠢的。它们将仅分发给授权用户。例如,在获得访问权限后将其下载到他的手机中 啊,我知道了,但没有人想下载和添加会增加复杂性的证书。用户不会这样做意味着用户不会使用应用程序【参考方案4】:在应用程序中存储 API_KEY 不是一个好主意,它很容易被逆向工程,尤其是在 XML 资源中时。您必须将其放在服务器端,然后调用服务。 App --- Call ---> [Service1.php which has API_KEY ] --- Call --> Payment Service。
App
这就像在支付服务和您的应用之间有中间件层。这样黑客就无法拿到钥匙了。
【讨论】:
但是这里黑客可以直接攻击Service1.php
。黑客通过逆向工程可以得到这个 URL。然后它将执行与应用程序相同的过程,并且可以有正确的响应。如何阻止这种情况?
在我的情况下,没有登录过程,所以没有会话令牌 ID。
您使用的是什么支付服务
啊,没有付款服务。我正在开发没有登录过程的应用程序。
您可以定义一个随机密钥来识别特定会话。【参考方案5】:
如前所述,仅使用硬编码凭据保护应用是不安全的。
我应该建议你使用一些类似登录的结构。 您首先要求输入用户名/密码的地方。
然后,您使用在运行时编译的签名构建 API 调用。通过这样做,您永远不需要通过开放的网络发送用户密码。
你可以通过这样的调用来实现:
APIkey = "a specicic APIkey"; //To identify the specifik app "not secret"
Username = "usersname"; //To identify witch user trying to make the call
Request = "you needed request data"; //Your actual requst parameters.
Timestamp = "Current_timestamp"; //Current timestamp user to get unique signatures for every call
Signature = sha256_hash(APIkey + Username + Request + Timestamp + Password); //Signature using the users password(Secret).
然后,您可以通过重新编译签名服务器端以及使用存储在数据库中的密码来验证调用。 如果签名匹配,则呼叫应该是真实的。 您还应该设置一个时间限制并拒绝每个旧的呼叫。
注意:您可能需要调整并更改为您的语言的工作代码,但您明白了。
【讨论】:
以上是关于如何在 Android 中保护调用 web 服务的主要内容,如果未能解决你的问题,请参考以下文章
如何从 android 调用 RESTful Web 服务?