繁体   English   中英

PHP - 如何通过随机令牌限制客户端访问其他 URL

[英]PHP - How to restrict clients from accessing other URL by random token

来自“domain1.com?secretkey”的客户端应该可以访问“domain2.com”。 这些域位于具有不同 websspace 和数据库的不同服务器上。

我在这里找到了 3 种限制网页的方法: wmtips.com/php/simple-ways-restrict-access-webpages-using.htm

我不可能使用用户名和密码进行身份验证,所以我想我必须使用Token-URL-Parameter和这样的函数:

//This function returns True if query string contains secretkey and secretvalue.

//Otherwise it returns False

function CheckAccess()

{

  return $_GET['secretkey']=='secretvalue';

}

但是如何生成一个单独的密钥呢?

如果我在隐藏字段中使用硬编码令牌,我会知道该怎么做,但是如何使用更安全的密钥?

使用 URL 参数发送令牌的常见加密方式有哪些?

所以我想我必须使用Token-URL-Parameter

这是一个明智的(如果不完美的话)选择。 您需要在两个域之间共享一个不与浏览器共享的秘密。

这可以通过数据库完成,有两种可行的偏执方法。

方法一:随机性

设置:

domain1.com设置了一个密钥(见下文),这个值与身份验证标准一起存储在数据库中,例如 IP 地址、日期时间、浏览器标识的散列( $_SERVER['HTTP_USER_AGENT'] ),以及任何其他想要在验证密钥时更具选择性不仅仅是侥幸。

  • 数据库

理想情况下,数据库应该与域位于同一台服务器上,并且只能在本地访问。 但这是关于您真正想要的安全程度以及您想在此主题上投入多少时间/金钱/资源的浮动比例。

如您所示,此密钥通过您的 ( urlencode d) GET参数传递到domain2.com

  • 编码:

base64_不是必需的,但如果你想使用它也没有什么坏处。 在链接 URL 中设置密钥时,最好(必需)使用urlencode($variable)

注意:您不需要在收到的$_GET变量上使用urldecode()

  • 如何生成个人密钥?

密钥不应包含上述任何可识别信息,并且是纯随机的。 它应该很长。 这里的另一个答案引用了 8 个字符。 这几乎会立即被打破。 您希望生成尽可能长的密钥字符串。 您可能只受到存储相同密钥的数据库的VARCHAR列上的索引系统的限制。 所以你需要一个 184 个字符的密钥,当完全解码时。

  • 获取钥匙:

使用random_bytes()openssl_random_pseudo_bytes() ,具体取决于您的 PHP 版本。

在循环中运行它以生成密钥长度并使用上面手册页中的示例使用bin2hex或类似方法将其设置为字母数字字符串。

不要使用 SHA1 或 MD5 来生成哈希。

(“为什么不,夏洛克?”)

  • 确保唯一性

获得密钥后,您需要检查它是否在数据库中。 最简单的方法是构建数据库,使密钥列是UNIQUE INDEXMySQL ),因此如果存在重复,则不会插入。 您必须构建逻辑,以便如果未插入密钥,则应重新启动并构建新密钥。

将密钥保存到数据库时,您还应该保存上面提到的其他元数据、IP 地址、浏览器哈希、日期时间等。

完成此过程后,您可以继续检查给定的 URL 是否有效。

获得:

  • 验证随机字符串:

您在$_GET PHP 值中有键。 将此密钥传递给您的数据库(当然, 使用 Prepared Statements )并检查它是否存在。

  • 验证和限定元数据:

一旦发现它存在,您可以检查元详细信息并确认它们是正确的。 例如,调用请求的时间不超过数据库中用于行创建的日期时间值之后的 x 分钟数。

您检查哪些不一致以及您对哪些做出反应取决于您,但例如,值得检查浏览器哈希值是否相同,以便您可以相当确定用于访问domain1.com的浏览器与访问的浏览器相同domain2.com

  • 如何处理不一致?

那么,浏览器请求一个不存在的字符串? 在踢出主页之前,在该条件下敲击 5 秒sleep()语句。 浏览器要求一个历史字符串(旧的日期时间值),在踢出主页之前在它上面敲一个sleep(5) 等等等等

如果您愿意,您可以记录失败尝试的 IP 地址,并使用它来计算(在另一个数据库表中)任何反复尝试破解您的哈希的 IP 块,然后您可以使用更多代码来添加时间损失,例如205.453.345.xxx IP 有 25 次失败的尝试,然后您可以向来自该 IP 的任何访问添加额外的sleep() ,或者甚至对于更高的数字,只需踢该 IP 块,甚至无需检查哈希值。

不要向用户/浏览器反馈任何特定哈希令牌失败的原因 探测攻击者可以使用这些数据来计算出有关您的哈希或元数据检查过程的更多信息。

警告:IP

由于使用 IP 地址的人在不断变化,因此在阻止或过度暂停 IP 时要格外小心。 如果您按块[123.456.678.xxx]或按地址记录 IP 故障计数,则存储计数不应超过 7 天。 否则,一个月前历史上属于黑客的 IP 将属于真正的用户,这些用户仍然因没有自己的过错而受到处罚。

在每个地址的基础上存储 IP 故障在磁盘空间中代价高昂,而且相对无用。 块跟踪更有效。

  • 清理

一旦您验证了密钥,并且元数据足够一致,您就对它感到满意,请在domain2.com上设置 cookie/会话以允许该浏览器访问,然后从数据库中删除哈希密钥,保留其唯一性访问能力。

重要:

上面提到的一切都可以充分利用不完美的情况。 KIKO Software评论值得关注

您不能仅通过提供 URL 参数来构建安全系统,请参阅:“ 带有令牌参数的 https url 有多安全? ”我知道这与您所做的不完全相同,但想法是相同的。 这个问题是可以解决的,但是需要大量的解释和注意。

这个特定主题比这个答案中的例子要多得多,但它需要奉献精神、阅读和学习。 永远不会有一个完全安全的系统(就像试图捕捉云),所以你需要考虑你的风险是什么,它们有多重要,以及值得寻找缓解措施的时间和精力。

大多数安全性不是问题的解决方案,而是缓解问题。 在上面的例子中,有可能破坏某个密钥,但它的可能性 -在允许的时间窗口内破坏一个密钥,并使用一个完全相同的浏览器并使用相同的 IP 地址- 非常小,但绝对不是不可能


方法二:加密。

不推荐。 加密处理能力很重,与上述相同,但具有更大的攻击窗口。

我在这里看到的使用加密的唯一理由是,如果出于某种愚蠢的原因您无法使用数据库。 因此,您可以制作元数据(IP、日期、时间等)的密文,然后将其发送到domain2.com ,以便domain2.com上的 PHP 检查密文的比较。

比如说,例如您通常放置的元数据是:

$plainText = $_SERVER['REMOTE_ADDR'].date("Y-m-d").md5($_SERVER['HTTP_USER_AGENT']);
$options = [  'cost' => 12,];

$cypher = password_hash($plainText, PASSWORD_BCRYPT, $option ); 
$urlCypher = urlencode($cypher);

并在domain2.com上阅读它,您将使用:

$checker = $_SERVER['REMOTE_ADDR'].date("Y-m-d").md5($_SERVER['HTTP_USER_AGENT']);

if(password_verify($checker,$_GET['keystring'])){
      //values compare ok.
}

虽然这有效,但它也适用于任何填写正确密码详细信息的远程计算机,因为最终用户浏览器已经知道检查的所有数据部分。

与数据库场景不同,没有独立的观察者。

不建议使用此方法,因为它占用大量处理器并且可能更容易受到攻击


一些源码链接:

当数据然后to token是随机的时,token是随机的。 使用时间来数据有一些弱点。 时间很难过几小时,几天,...,此时任何公开的url和相同的Resource都将相同,安全性降低。 再一次,时间更改密钥 4:00,如果在 4:00 进行身份验证,则任何 url 然后 3:59 都是假的。

我经常随机数组键并保存在环境中。 然后 url 时,选择随机一个键 ==> 传递给数据令牌并加密。 验证时,我检查此数组中的密钥 ==> pass 它涵盖了随机时间的所有弱点,您会对此感到满意,谢谢:D

暂无
暂无

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

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