【问题标题】:Protect script from bots and unwanted requests posting data保护脚本免受机器人和不需要的请求发布数据
【发布时间】:2012-03-19 14:07:05
【问题描述】:

我正在修改一个通过 webview 使用 webapp 的 Android 应用。目前,webapp 的代码库是用 ColdFusion 编写的——所以所有的会话管理都是在 CF 中完成的。 webapp 中的某些钩子会强制 Android 应用程序执行本机功能,有时还会在 PHP 中调用外部脚本。

这些 php 脚本获取发布给它们的数据(userid、friendid 等) - 目前 php 脚本只是确保发布的数据有效,然后在数据存在且有效时处理请求。

我正在寻找方法来提高这些 php 脚本的安全性,以防止机器人/恶意用户向这些页面发布虚假数据 - 在这一点上,没有什么能阻止任何人发送正确的用户 ID/朋友 ID 并让脚本执行。

会话管理将是第一道防线,但由于 webapp 使用不同的语言,我不能使用它 - 有时 php 脚本完全位于不同的域(虽然是同一台服务器)。

我考虑的另一种方法是在注册时创建一个用户令牌以与用户关联,并将其保存在 Android 端 - 然后在请求这些 php 脚本时发送用户 ID 和令牌。并验证该用户的令牌在远程数据库中是否匹配——这将使猜测恶意用户的发布凭据变得更加困难。显然不是最好的,因为令牌存储在本地并通过网络传输,但我离题了。

问题有没有更好的方法来保护这些单独的 php 脚本不被执行,而不使用会话管理?我的象征性想法有意义吗?

注意:我可以在任何/所有请求上使用 SSL。

【问题讨论】:

  • 目标是避免 DOS 攻击,还是清除虚假请求?如果它避免 DOS,那么在给定时间内允许的最大请求数方面有哪些限制(应该有)?如果要避免虚假请求,您已经有什么可以区分“真实”请求和虚假请求(登录名/密码、白名单等)?即使有会话,您仍然需要一些机制来确定好人与坏人,或者在 DOS 的情况下,知道多少太多了。

标签: php android token


【解决方案1】:

如果您能胜任这项任务,我确切地知道您需要什么。您的 API 需要实现 OAuth2.0。

OAuth 可以为您提供一种安全的方式来将信息传入和传出您的服务,同时确保所有机密信息都是私密的,并且只有正确的人才能访问该信息。它为每个用户提供了一个独特的签名。

OAuth 被 Facebook、Google、Twitter 等机构用来为开发人员提供一种访问信息的安全方式,同时防止每个人做他们不应该做的事情。

OAuth 支持 ColdFusion、Java、PHP、C#、dotNet、VB.net、LIST、Javascript、Perl、Python、Ruby 等。

http://oauth.net/

【讨论】:

  • 我之前研究过 OAuth,目前正在寻找内部解决方案。感谢史蒂文的想法!
  • 我知道这是大多数情况下的“正确”答案:)
  • 如果您的应用程序的所有查询都由注册用户完成,这是正确的答案。如果你的应用可以被客人使用,那就比较复杂了,一般会使用基于签名的方案。
【解决方案2】:

会话管理或 OAuth 是最好的解决方案,但不是最简单的。一种更简单的方法是在您的应用程序和 PHP 脚本中实现哈希算法。当应用程序准备一个请求时,您使用您的秘密方法散列一些正在发送到服务器的值。此哈希随请求一起发送。服务器做同样的事情并比较两个哈希值。当它们相同时,它知道请求来自应用程序(或破解您的算法的人)。如果不是,服务器可以简单地忽略该请求。

一个例子:

数据: userid = 2042;名字=约翰多;电子邮件 = john-doe@someprovider.com

Hash(在 PHP 中,但您也应该在应用程序中实现它):

<?php

$userid = 2042;
$name = 'JohnDoe'
$email = 'john-doe@someprovider.com';

// Remove some letters with other letters
$name = str_replace(array('a', 'd', 'g'), array('E', 'x', '9'), $name);

// Reverse a string
$email = strrev($email);

// Make a super secret hash (with salt!)
$hash = sha1('fnI87' . $useris . '87bk;s.' . $name . 'unf%gd' . $email);

// Some more fun
$hash = str_rot13($hash);

?>

请求: http://www.your-server.com/script.php?userid=2042&name=JohnDoe&email=john-doe@someprovider.com&hash=YOUR-GENERATED-HASH

现在服务器可以应用相同的散列方法并将生成的散列与请求发送的散列进行比较。

【讨论】:

  • 我喜欢这种方法,但不幸的是,在 Android 应用程序的世界里——我不得不假设它会被逆向工程,并且我的哈希算法会被发现。考虑到这一点,我认为我上面的令牌想法会更安全,因为这样攻击者将只有一个用户的令牌,而不是拥有可以应用于每个用户的算法。想法?
  • 这个想法很好,但是为了提高安全性,您必须通过添加盐和使用散列函数来增加熵。在您的示例中,使用反向工程强制您的 $hash 相对容易。
  • @Julien 在我的示例中,令牌将是在服务器端生成的 100 多个随机字符......您是说可以轻松反转吗?我有点困惑。
  • 可以使用用户唯一令牌与随机会话盐和哈希相结合。但是,任何基于共享密钥(对称加密)的方法都可以很容易地被最终用户破解。如果您不能信任您的最终用户,请不要让他们与您的服务器对话(通过应用程序或任何其他方式)。为了确保没有其他人可以与最终用户客户端应用程序对话,您需要公钥加密,不幸的是,这并不容易实现。在这种情况下,客户端应用程序可以验证消息是否真的是由您的服务器发送的。您仍然不能相信您收到的任何消息都来自您的应用程序。
【解决方案3】:

我想建议一种更抽象的方法,但类似于 Jonathan 的方法。

我做了以下假设:

  • 您有一个任何人都可以调用的 PHP 脚本(如果他们知道 URL/嗅探网络数据包)。
  • 你的安卓应用是闭源的;这意味着如果你有一个散列算法,但你会知道它是什么。
  • 您希望阻止任何人直接调用 PHP 脚本 - 绕过应用程序和您可能在其中内置的任何安全性。

您需要的是识别您的应用正在发送请求的方法,而不是其他人。

这个想法是您为每个请求生成一个签名,只有 您的 应用可以发出(即盐 + 哈希)。

$input = array(
    "userid" => 1234,
    "friendid" => 2345
    "etc" => "..."
);

$salt = "s3kr4tsal7"; // this is essentially your app signature
$signature = md5($salt . serialize($input));  // you could also use json_encode or any other to-string serialization
                                              // pick whichever is easy to do in PHP and in your app

$request = array(
    "input" => $input,
    "signature" => $signature
); // send this

然后在您的 PHP 脚本中检查签名是否与计算的签名匹配。这类似于 Jonathan 的解决方案,但它允许任何输入,它不依赖于 $email 或任何其他属性。我也不认为您需要过于复杂的散列算法,只需 md5 加盐就“够难”了。

您应该注意另一种类型的攻击,那就是重放攻击。

如果您查看越过线路的 RAW 数据,您可以捕获它并简单地再次播放。如果你知道什么动作有什么输出,你可以简单地重复输出。

重放攻击的典型解决方案是试验和响应。 SSL 会为您执行此操作,但您也可以进行自定义实现(但这要复杂得多)。

【讨论】:

  • 海友!我的 sn-p 只是一个例子;在实际脚本中应用时,它应该适用于所有形式的输入。如果有人真的想破解,你不觉得单一的盐太容易破解吗?通过添加更多混淆(使用str_replacestrrevstr_rot13 等函数),对散列方法进行逆向工程变得更加困难。
  • 哈希方法的本质是它很难反转,添加更多步骤没有意义。 strrev 不会增加熵,str_replace 实际上会减少熵。 rot13 旋转字符集,因此它可能会增加熵但不会显着增加。然而,添加盐会向输入中添加更多信息,这确实会增加熵。
【解决方案4】:

像往常一样,这取决于您需要什么级别的保护,以及您愿意投资多少。由于您不能使用会话,因此您需要某种无状态的方式来进行身份验证。通常有两种方法可以做到这一点:每次发布凭据(例如,基本身份验证)或发送某种令牌(顺便说一句,会话 ID 就是这样,一个链接到服务器上实时会话的令牌)。

生成令牌时,最好使用标准且经过验证的算法,而不是自己发明和/或依赖晦涩难懂的算法。即使它看起来很安全,也可能不安全。例如,针对上述 MD5 思想的已知攻击(很容易在不知道密钥的情况下将数据附加到消息中,并获得另一个有效的 MAC)。 HMAC-SHA1 旨在避免这些。

第一件事:如果可以,对所有请求使用 SSL。这将同时完成几件事:

  • 用户(您的应用程序)可以确保他们将数据发布到正确的位置(即您的网络应用程序)。 SSL 服务器身份验证负责这一点。
  • 它将确保您发布的任何凭证/令牌都被自动加密。
  • 重放攻击几乎不可能了

您似乎已经拥有经过身份验证的用户,因此颁发令牌应该相对容易。您可能想考虑要实现的协议,但随着您考虑更多案例,您将越来越接近重新发明 OAuth 和朋友。需要考虑的一些事项:

  • 令牌的有效期:这样即使有人持有令牌,他们也不能无限期地使用它。
  • 一种撤销令牌的方法
  • 对于 web 应用的不同部分(服务)可能有不同的令牌,因此您可以仅授予/撤销对必要服务的访问权限

为了确保您(即您的 web 应用程序)是唯一可以发行所述令牌的人,您希望使用只有您拥有的密钥对其进行签名。由于签名者和验证者是相同的(您),您不必使用公钥加密,HMAC 应该这样做。例如,您可以连接用户名、发布时间和任何其他相关信息,并将它们用作 HMAC 的输入。将这些参数与签名(HMAC 输出)一起打包以创建令牌,并让应用程序随每个请求一起发送它。在服务器上验证并在有效时允许访问,如果过期则需要重新登录(新令牌),否则拒绝访问。

或者,如果您只想对应用进行身份验证,而不是在其中混入用户信息,您可以使用类似的方法在客户端(应用端)签署请求。如果您选择这种方式,请使用标准算法。当然,这将要求签名密钥(以某种形式)存在于应用程序中,因此如果有人(通过逆向工程等)掌握了它,他们可以发出任意数量的请求。不过有一些方法可以缓解这种情况:

  • 在本机代码中实现签名逻辑
  • 不存储原始密钥,而是在运行时从存储在不同位置的点点滴滴派生出来

当然,最简单的方法是在服务器上要求基本或摘要式身份验证(当然是通过 SSL),并将用户名和密码嵌入应用程序中(充分混淆)。在只需要更改服务器配置的服务器端,在客户端添加了几行。不利的一面是,如果这些凭据遭到破坏,则实际上无法更改这些凭据(没有发布新版本并阻止旧版本的访问以迫使人们进行更新;不漂亮)。

【讨论】:

  • 根据您的第 5 段 - 这样就足够了: $token = sha1($timestamp . $userid . $salt); ??通过盐,我只会有一个随机的静态字符字符串..
  • 还是php hash_hmac() 比sha1() 好?
  • 盐在这里可能是错误的术语。根据定义,Salt 不是秘密,它的目的是确保即使输入相同,您也会得到不同的输出(密码、密钥等)。否则,您只需查看哈希就可以确定输入是相同的。您想保留部分输入机密,因此它实际上是一个密钥。您可以使用 hash_hmac() 来实现 HMAC-SHA1,这应该对您有用。请参阅此处的示例:php.net/manual/en/function.hash-hmac.php
  • 永远不要相信客户端软件。 应用程序是在本机代码中运行签名逻辑还是在运行时计算共享密钥都没有关系。 始终假设攻击者拥有应用程序所拥有的所有知识。 公钥加密也无济于事。公钥加密可以验证你的服务器发送给应用程序的消息是真实的,但它不能验证你收到的消息是由你的应用程序生成的(而不是由伪装成应用程序的攻击者生成的)。
  • @Mikko 你的意思是?您需要对应用程序进行身份验证,因此该应用程序需要具有某种密钥或令牌。您可以尽最大努力保护它和/或使其成为临时的,但仅此而已。
猜你喜欢
  • 2014-05-02
  • 2021-05-08
  • 2018-12-26
  • 2011-07-29
  • 1970-01-01
  • 2012-08-23
  • 2016-01-17
  • 1970-01-01
  • 2012-12-20
相关资源
最近更新 更多