搜索
首页后端开发php教程PHP 使用 rand() 函数生成令牌安全吗?

Web 应用经常需要创建一个难以被猜解的令牌,例如,会话令牌、CSRF 令牌或者在忘记密码功能中用于邮件重置密码使用的令牌。这些令牌本应该被进行加密保护,但实际中却经常被使用多次调用 rand函数并转换输出为字符串来表示。本文将展示预测使用 rand产生的令牌其实并不那么困难。

rand 是如何工作的

在 PHP 中,函数 rand 用于产生伪随机数,而初始化随机数的种子是由 srand 产生的。如果用户不选择调用 srand,那么 PHP 会将一个非常难以猜测的数字作为随机数生成器的种子。由 srand产生的种子将完全决定了 rand函数生成的随机数。

随机数生成器会保持由 srand初始化过后的状态,并在每次调用 rand后改变。这个状态与进程状态相关,所以两个进程通常不会返回相同的 rand随机数。

示例程序

我们使用的实例程序为 EZChatter,一个用于生成 CSRF 令牌的小程序,但在创建时没有很好的考虑安全性:

public static function gen($len = 5){    $token = '';    while($len--){        $choose = rand(0, 2);        if ($choose === 0)            $token .= chr(rand(ord('A'), ord('Z')));        else if($choose === 1)            $token .= chr(rand(ord('a'), ord('z')));        else            $token .= chr(rand(ord('0'), ord('9')));    }    return $token;}

如你所见,上述代码首先调用 rand来决定是使用大写字母、小写字母还是数字,然后在选择一个特定的字母或数字。每当我们请求 index.php 时都会产生一个新的 CSRF 令牌,所以我们可以随意的发出请求以产生我们需要的令牌。我们的目标是预测分配给用户的令牌,这样我们就可以进行 CSRF 攻击了。

猜解种子

正如前文所说,随机数序列完全由种子所决定的,所以我们可以简单的尝试每一个可能的数字作为 srand的参数来猜测到正确的随机数生成器状态。但请注意,这在 Linux 上只有服务器进程是新创建的才能奏效,如果服务器已经产生了很多 rand调用,那么我们就需要在破解程序中重复同样次数的调用以获取到相同的状态。而在 Windows 上,随机数生成器的状态只与 srand的参数有关,所以就不需要进行一个重复的过程了。

如果想从一个新创建的进程中获取到一个令牌,那么可以使用下列的 PHP 脚本来进行破解:

for ($i = 0; $i < PHP_INT_MAX; $i++) {    srand($i);    if (Token::gen(10) == "2118Jx9w3e") {        die("Found: $i \n");    }}

为了搜索 srand总计 4294967295 个可能的参数值,大概要花费12小时。然而,由于 PHP 仅仅只是调用了 glibc 的 rand函数,我们可以重新转换 PHP 代码为 C 来加快运行速度。这里给出两个版本的代码,一个调用的是 glibc rand,另一个模仿的是 Windows rand。它是基于 token.php中的 PHP 代码,使用 PHP 中的宏 ext/standard/rand.c来循环尝试找出可能的种子。在 Windows 上大约只需要十来分钟,Linux 上大约需要几个小时。

一旦攻击完成,就拥有了跟服务器处在相同状态下的随机数生成器,从而可以同服务器产生相同的令牌。通过将自己生成的令牌与服务器返回的令牌进行比对,就可以知道哪些令牌已经被分配给了用户,进而可以开始攻击。

Linux 上的状态攻击

在 Windows 上,猜解 srand参数和随机数生成器的状态是一回事,但在 Linux 上则不同。glibc 的 rand()保持一系列的数字,并决定下一个状态像下面这样:

state[i] = state[i-3] + state[i-31]return state[i] >> 1

所以每一个输出近似是之前 3 和 31 的结果之和。考虑如下令牌:

  • 6ZF5kNgonV
  • 9h3byovpGR
  • gGt0A94U92

现在,该决定下一个随机数是否为大写字母、小写字母还是数字。这将由之前第 3 和 31 次的结果决定, gGt0A94U92中的 9 和 9h3byovpGR中的 y。所以,我们预计下一个 rand(0, 2)输出会近似于⌊10/10 + 25/26 × 3⌋ = 2 mod 3,这意味着我们将得到一个数字。下面假设我们可以预测到这个数字,则下一次调用 rand得到的数字将由之前第 3 次调用 rand得到的数字和之前第 31 次调用的 rand得到小写字母决定。这个数字将介于 ⌊2/3 + 1/3 × 10⌋ = 0 mod 10 和  ⌊3/3 + 2/3 × 10⌋ = 6 mod 10 之间。因此我们预计值将介于 0 到6,最终结果为 4:

  • 43J2d2ew31

如你所见,我们没法通过这种方法精确的预测下一个随机数,但也很清楚,我们可以预测到大致的范围,你已经不可以称其为随机数了。另外也可以使用同样的方式猜解 glibc的随机数生成器的状态,尽管这里没有在进行尝试。

总结

应当使用安全加密的随机数生成器。如果使用 rand产生随机数,很多情况下是可以对其随机数生成器进行猜解的,这样令牌将变得可以被预测。在 Linux 上预测令牌会相对困难一些,但这仍然并不安全。Windows 上的随机数生成器相对更容易被利用,因为其随机数生成器状态可以在数分钟内被猜解出来。

*原文: sjoerdlangkemper.nl,FB小编xiaix编译,转自须注明来自FreeBuf黑客与极客(FreeBuf.COM)

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
简单地说明PHP会话的概念。简单地说明PHP会话的概念。Apr 26, 2025 am 12:09 AM

phpsessionstrackuserdataacrossmultiplepagerequestsusingauniqueIdStoredInacookie.here'showtomanageThemeffectionaly:1)startAsessionWithSessionwwithSession_start()和stordoredAtain $ _session.2)

您如何循环中存储在PHP会话中的所有值?您如何循环中存储在PHP会话中的所有值?Apr 26, 2025 am 12:06 AM

在PHP中,遍历会话数据可以通过以下步骤实现:1.使用session_start()启动会话。2.通过foreach循环遍历$_SESSION数组中的所有键值对。3.处理复杂数据结构时,使用is_array()或is_object()函数,并用print_r()输出详细信息。4.优化遍历时,可采用分页处理,避免一次性处理大量数据。这将帮助你在实际项目中更有效地管理和使用PHP会话数据。

说明如何使用会话进行用户身份验证。说明如何使用会话进行用户身份验证。Apr 26, 2025 am 12:04 AM

会话通过服务器端的状态管理机制实现用户认证。1)会话创建并生成唯一ID,2)ID通过cookies传递,3)服务器存储并通过ID访问会话数据,4)实现用户认证和状态管理,提升应用安全性和用户体验。

举一个如何在PHP会话中存储用户名的示例。举一个如何在PHP会话中存储用户名的示例。Apr 26, 2025 am 12:03 AM

Tostoreauser'snameinaPHPsession,startthesessionwithsession_start(),thenassignthenameto$_SESSION['username'].1)Usesession_start()toinitializethesession.2)Assigntheuser'snameto$_SESSION['username'].Thisallowsyoutoaccessthenameacrossmultiplepages,enhanc

哪些常见问题会导致PHP会话失败?哪些常见问题会导致PHP会话失败?Apr 25, 2025 am 12:16 AM

PHPSession失效的原因包括配置错误、Cookie问题和Session过期。1.配置错误:检查并设置正确的session.save_path。2.Cookie问题:确保Cookie设置正确。3.Session过期:调整session.gc_maxlifetime值以延长会话时间。

您如何在PHP中调试与会话相关的问题?您如何在PHP中调试与会话相关的问题?Apr 25, 2025 am 12:12 AM

在PHP中调试会话问题的方法包括:1.检查会话是否正确启动;2.验证会话ID的传递;3.检查会话数据的存储和读取;4.查看服务器配置。通过输出会话ID和数据、查看会话文件内容等方法,可以有效诊断和解决会话相关的问题。

如果session_start()被多次调用会发生什么?如果session_start()被多次调用会发生什么?Apr 25, 2025 am 12:06 AM

多次调用session_start()会导致警告信息和可能的数据覆盖。1)PHP会发出警告,提示session已启动。2)可能导致session数据意外覆盖。3)使用session_status()检查session状态,避免重复调用。

您如何在PHP中配置会话寿命?您如何在PHP中配置会话寿命?Apr 25, 2025 am 12:05 AM

在PHP中配置会话生命周期可以通过设置session.gc_maxlifetime和session.cookie_lifetime来实现。1)session.gc_maxlifetime控制服务器端会话数据的存活时间,2)session.cookie_lifetime控制客户端cookie的生命周期,设置为0时cookie在浏览器关闭时过期。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具