PHP会话ID有多独特?

92

php的会话id有多独特?从我读过的各种资料中,我得到的印象是不应该依赖于两个用户永远不会获得相同的sessionid。那它不是全局唯一标识符(GUID)吗?

10个回答

67

默认情况下,它并不是非常独特。在默认配置中,它是各种东西的哈希结果,包括gettimeofday的结果(这并不是非常独特的),但如果您担心,应该将其配置为从/dev/urandom中提取一些熵,如下所示:

ini_set("session.entropy_file", "/dev/urandom");
ini_set("session.entropy_length", "512");
这段代码 中搜索 "php_session_create_id" 查看他们实际使用的算法。编辑以添加:有一个DFA随机数生成器,由pid(进程标识符)种子混合使用cs时间。它不是一个强制唯一条件,特别是从安全角度考虑。使用上面的熵配置。更新:从PHP 5.4.0开始,session.entropy_file默认为/dev/urandom或/dev/arandom(如果可用)。在PHP 5.3.0中,默认情况下该指令为空。参见PHP手册

1
是的,当我为一个需要对抗敌方作战人员等极端安全保障的网站签订了合同时,实际上我创建了自己的会话处理程序,并直接从random.org获取熵数据进行输入。但那个系统的要求远远超出了大多数普通人所处理的范畴。;-) - Theodore R. Smith
1
@thomas-jensen,gettimeofday 就是 Unix 时间戳,只不过它以微秒(有时候)表示。请阅读上面链接的 php_session_create_id 方法。 - djsadinoff
4
增加熵长度可以提高随机性,但并不会显著影响碰撞的可能性,因为哈希长度仍然相同。然而,改变session.hash_function允许您使用更长的哈希,例如sha512。 - ColinM
3
我觉得发生碰撞很奇怪。毕竟,PHP应该被设计成检查是否存在有效的会话ID,并随后生成不同的ID。 - Luke
1
@theodore-r-smith,从公开来源获取熵是非常不好的做法。你应该假设你的“敌方作战人员”也可以访问random.org... - avri
显示剩余2条评论

43

Session_id可以被复制,但概率非常低。如果您有一个有着适中流量的网站,它可能只会在整个网站的生命周期内发生一次,并且仅会影响一个用户的一个session。

除非您打算建立一个非常高流量的网站或银行业务服务,否则这不值得关注。


4
我听说有些场所发生了许多碰撞事故。 - ColinM
22
问题是在将近4年前提出的。 了解一下session id算法是否有改进会很有趣... - Sliq
1
显然,目前(MD5/SHA1哈希)基于用户的远程地址、本地时间和一些随机数(LCG)。 - Caramiriel
即使碰撞的概率很小,难道就没有办法检测这些碰撞并避免它们吗? - humanityANDpeace
3
我不需要去捣乱手机,手机自己就经常出故障。 :) - hakre
显示剩余3条评论

13
如果想了解PHP默认如何生成会话ID,请查看Github上的源代码。它并不是随机生成的,而是基于这些要素的哈希值(默认为md5)生成的(参见代码段的273-330行):
  1. 加密安全伪随机数发生器 CPRNG
如果操作系统有可用的随机源,则用于会话ID的生成强度很高(/dev/urandom和其他操作系统随机源通常是加密安全的伪随机数发生器)。但如果没有则足够了。
会话标识生成的目标是:
  1. 尽可能减少生成两个具有相同值的会话ID的概率
  2. 使计算随机密钥并命中正在使用的密钥非常具有挑战性。
PHP的会话生成方法可以实现这一目标。 不能绝对保证唯一性,但是命中相同哈希值的概率非常低,因此一般来说不值得担心。

11

2
准确地说,对于 PHP 5.3 及以上版本,设置“session.hash_function = sha512”可使用512位哈希。这应该可以解决问题。默认情况下,在高流量网站上发生碰撞是很常见的。 - ColinM

6

session_id的大小
假设session_id是均匀分布的,大小为128位。假设地球上每个人每天都会登录一次,持续使用一个新的session,1000年。

num_sesion_ids  = 1000*365.25 *7*10**9 < 2**36
collission_prob < 1 - (1-1/2**82)**(2**36)  ≈ 1 - e**-(1/2**46) 
                ≈ 1/2**46 

因此,一个或多个冲突的概率小于七十万亿分之一。因此,会话ID的128位大小足够大。正如其他评论中提到的那样,会话管理器还可以检查新会话ID是否已经存在。
随机性 因此,我认为重要的问题是会话ID是否使用良好的伪随机数生成。在这方面,您永远无法确定,但我建议使用一个已知的、经常使用的标准解决方案来实现此目的(正如您可能已经做的那样)。
即使通过检查避免了冲突,随机性和会话ID的大小也很重要,以便黑客不能以大概率猜测并找到活动的会话ID。

3
我不是数学家,但我认为你忘记了“生日悖论”,所以即使碰撞的可能性很小,仍比你所示的要大得多。此外,正如djsadinoff建议的那样,默认情况下,PHP并不一定使用好的生成随机数的方法。 - ColinM
不,估计是正确的。上述计算是一个简化的估计,我们估计session_id编号为i的碰撞概率=1/282(尽管上面应该是1/292 =打字错误)。实际上,只要没有发生先前的碰撞,概率就是(i-1)/2128。1/292仅适用于最后一个session_id。 - MrJ

3
我没有找到关于这个的确认,但我相信在创建带有该ID的会话之前,php会检查会话ID是否已经存在。
人们担心的会话劫持问题是当某人发现活动用户的会话ID时。这可以通过许多方式来防止,有关更多信息,请参见php.net上的此页面关于会话固定的这篇论文

2
如果你只是在几个PHP服务器中的其中一个,那么不能保证该服务器具有足够的知识来确定sessionID是否已被使用。 - djsadinoff
如果我在两个不同的PHP服务器中获得相同的会话ID,这会有什么影响呢?假设这是两个不同的域,那么会话cookie只能从每个域访问...? - daremon
3
在多服务器环境下,防止重复会话的最简单方法是通过Memcached会话处理程序将会话存储在Memcached中。问题得到解决,您的用户可以在不丢失其内容的情况下在不同服务器之间切换。 - Theodore R. Smith
@daremon 他正在谈论一个域名的多个服务器。 - gtd
这是完全不正确的。PHP在生成新的会话ID时并不会检查现有的会话ID。查看任何PHP会话处理程序代码,都没有实现此目的的方法。 - ColinM
@ColinM 您是正确的。在此处创建函数php_session_create_id http://git.php.net/?p=php-src.git;a=blob;f=ext/session/session.c;h=0c08d496816cf92a36b66fbcbafb0d4be3286232;hb=HEAD - Ólafur Waage

2
不,会话ID不是GUID,但两个用户不应该获得相同的会话ID,因为它们存储在服务器端。

2
可能是因为服务器端存储没有任何方式来保证唯一性。唯一性是一件事 - 如果发生冲突,无论会话存储在哪里,它都会发生冲突。 - user14038
不是我做的,我感激你的回复(以及其他人的回复)。-- Jalov - Jalov
2
会话 ID 存储在服务器和客户端两侧。会话内容存储在服务器端。这个事实与会话 ID 的唯一性几乎没有关系。 - YudhiWidyatama

0

我知道这篇文章已经很旧了。然而,我在这里添加我的答案,因为我无法找到一个相关的解决方案来回答这个问题,即使我自己发布了一个类似的问题。然而,我从回复中得到了线索。对于那些感兴趣的人,算法和解决方案在这里解释。它使用会话和不同的cookie的组合。

简要算法如下:

使用自定义类'MySessionHandler'和DB进行会话处理

  1. 就在session_start之前,将cookie cookie_start_time设置为当前时间。此cookie的生命周期将与会话相同。两者都使用变量$this->cookieLifeTime来设置生命周期。

  2. 在会话'_write'中,我们将该值设置为与$this->cookieStartTime相同的db表字段cookie_start_time

  3. 在会话'_read'中,我们进行检查

如果($getRowsOfSession[0]['cookie_start_time'] != $this->cookieStartTime)。

如果返回true,表示这是一个重复的会话,用户将被重定向以销毁该会话,然后再次重定向开始新会话。(总共2次重定向)

1
请在答案中发布链接的相关部分。 - Sfili_81

0

您可以选择将各种会话与由数据库生成的唯一字段一起存储在数据库中;合并两者并将其保存在会话变量中,然后检查该变量而不是会话ID。


欢迎来到stackoverflow。很遗憾,您的答案并没有尝试回答问题。OP问了会话ID有多独特。将数据存储在数据库中,然后使用会话变量检索它只会增加问题的复杂性,不是吗? - Noah Boegli
@NoahBoegli,其他解决方案提出了更加复杂的方法。 - Pernicious

-4
<?php
session_start();
$_SESSION['username']="username";
?>

<!DOCTYPE html>
<html>
<head>
    <title>Update</title>
</head>
<body>

<table border="2">
    <tr>
        <th>Username</th>
        <th>Email</th>
        <th>Edit</th>
    </tr>
<?php
     $conn=mysqli_connect("localhost","root","","telephasic");
     $q2="select * from register where username = '".$_SESSION['username']."'";
     $run=mysqli_query($conn, $q2);
     while($row=mysqli_fetch_array($run))
     {
         $name=$row[1];
         $email=$row[2];
     ?>

    <tr>
        <td><?php echo $name; ?></td>
        <td><?php echo $email; ?></td>
        <td><a href="edit.php"> Edit </a></td>
    </tr>
 <?php } ?>
 </table> 
 </body>

如果您的用户名不同或唯一,您可以使用此代码进行会话。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接