PHP 会话 ID——它们是如何生成的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18937651/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP session IDs -- how are they generated?
提问by M Miller
When I call session_start()or session_regenerate_id(), PHP generates what appears to be a random string for the session ID. What I want to know is, is it just a random sequence of characters, or is it like the uniqid()function?
当我调用session_start()or 时session_regenerate_id(),PHP 会为会话 ID 生成一个随机字符串。我想知道的是,它只是一个随机的字符序列,还是像uniqid()函数一样?
Because if it's just random characters, couldn't you theoretically run into a conflict? If User A logged in and then User B logged in and, though highly unlikely, User B generated the same session ID, then User B would end up accessing User A's account.
因为如果只是随机字符,理论上你不会遇到冲突吗?如果用户 A 登录,然后用户 B 登录,并且用户 B 生成相同的会话 ID(尽管可能性很小),则用户 B 最终将访问用户 A 的帐户。
Even if PHP checks to see if a session with the same ID already exists and, if so, regenerates an ID again... I don't think I want a system that EVER produces the same ID twice, even after garbage collection -- maybe I want to store a table of them and check against them for possible hiHymaning or whatever.
即使 PHP 检查是否已存在具有相同 ID 的会话,如果存在,则再次重新生成 ID ......我不认为我想要一个系统,即使在垃圾收集之后,也不会产生两次相同的 ID——也许我想存储一张它们的表格并检查它们是否可能被劫持或其他什么。
If it isn't unique, how should I go about enforcing uniqueness? I'd rather implement it using PHP configuration than in every script I make. Nice thing about PHP sessions is not worrying about the technical details behind the scenes.
如果它不是唯一的,我应该如何强制执行唯一性?我宁愿使用 PHP 配置来实现它,而不是在我制作的每个脚本中。PHP session 的好处是不用担心幕后的技术细节。
回答by GordyD
If you want to know how PHP generates a session ID by default check out the source code on Github. It is certainly not randomand is based on a hash (default: md5) of these ingredients (see line 310 of code snippet):
如果您想知道 PHP 如何默认生成会话 ID,请查看Github上的源代码。它当然不是随机的,并且基于这些成分的散列(默认值:md5)(参见代码片段的第 310 行):
- IP addressof the client
- Current time
- PHP Linear Congruence Generator- a pseudo random number generator (PRNG)
- OS-specific random source- if the OS has a random source available (e.g. /dev/urandom)
- 客户端的IP地址
- 当前时间
- PHP Linear Congruence Generator- 一个伪随机数生成器 (PRNG)
- 操作系统特定的随机源- 如果操作系统有可用的随机源(例如 /dev/urandom)
If the OS has a random source available then strength of the generated ID for the purpose of being a session ID is high (/dev/urandom and other OS random sources are (usually) cryptographically secure PRNGs). If however it does not then it is satisfactory.
如果操作系统有一个可用的随机源,那么为了成为会话 ID 的目的而生成的 ID 的强度很高(/dev/urandom 和其他操作系统随机源(通常)是加密安全的 PRNGs)。然而,如果它没有,那么它是令人满意的。
The goal with session identification generation is to:
会话标识生成的目标是:
- minimise the probability of generating two session IDs with the same value
- make it very challenging computationally to generate random keys and hit an in use one.
- 最小化生成具有相同值的两个会话 ID 的概率
- 使生成随机密钥并命中使用中的密钥在计算上变得非常具有挑战性。
This is achieved by PHP's approach to session generation.
这是通过 PHP 的会话生成方法实现的。
You cannot absolutely guarantee uniqueness, but the probabilities are so low of hitting the same hash twice that it is, generally speaking, not worth worrying about.
你不能绝对保证唯一性,但是两次命中相同哈希的概率很低,一般来说,不值得担心。
回答by 1321941
Here is the code that generates the id: Session.c
这是生成 id 的代码: Session.c
Specifically the php_session_create_idfunction:
具体php_session_create_id功能:
PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
{
PHP_MD5_CTX md5_context;
PHP_SHA1_CTX sha1_context;
#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
void *hash_context = NULL;
#endif
unsigned char *digest;
int digest_len;
int j;
char *buf, *outid;
struct timeval tv;
zval **array;
zval **token;
char *remote_addr = NULL;
gettimeofday(&tv, NULL);
if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &array) == SUCCESS &&
Z_TYPE_PP(array) == IS_ARRAY &&
zend_hash_find(Z_ARRVAL_PP(array), "REMOTE_ADDR", sizeof("REMOTE_ADDR"), (void **) &token) == SUCCESS
) {
remote_addr = Z_STRVAL_PP(token);
}
/* maximum 15+19+19+10 bytes */
spprintf(&buf, 0, "%.15s%ld%ld%0.8F", remote_addr ? remote_addr : "", tv.tv_sec, (long int)tv.tv_usec, php_combined_lcg(TSRMLS_C) * 10);
switch (PS(hash_func)) {
case PS_HASH_FUNC_MD5:
PHP_MD5Init(&md5_context);
PHP_MD5Update(&md5_context, (unsigned char *) buf, strlen(buf));
digest_len = 16;
break;
case PS_HASH_FUNC_SHA1:
PHP_SHA1Init(&sha1_context);
PHP_SHA1Update(&sha1_context, (unsigned char *) buf, strlen(buf));
digest_len = 20;
break;
#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
case PS_HASH_FUNC_OTHER:
if (!PS(hash_ops)) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
efree(buf);
return NULL;
}
hash_context = emalloc(PS(hash_ops)->context_size);
PS(hash_ops)->hash_init(hash_context);
PS(hash_ops)->hash_update(hash_context, (unsigned char *) buf, strlen(buf));
digest_len = PS(hash_ops)->digest_size;
break;
#endif /* HAVE_HASH_EXT */
default:
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
efree(buf);
return NULL;
}
efree(buf);
if (PS(entropy_length) > 0) {
#ifdef PHP_WIN32
unsigned char rbuf[2048];
size_t toread = PS(entropy_length);
if (php_win32_get_random_bytes(rbuf, MIN(toread, sizeof(rbuf))) == SUCCESS){
switch (PS(hash_func)) {
case PS_HASH_FUNC_MD5:
PHP_MD5Update(&md5_context, rbuf, toread);
break;
case PS_HASH_FUNC_SHA1:
PHP_SHA1Update(&sha1_context, rbuf, toread);
break;
# if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
case PS_HASH_FUNC_OTHER:
PS(hash_ops)->hash_update(hash_context, rbuf, toread);
break;
# endif /* HAVE_HASH_EXT */
}
}
#else
int fd;
fd = VCWD_OPEN(PS(entropy_file), O_RDONLY);
if (fd >= 0) {
unsigned char rbuf[2048];
int n;
int to_read = PS(entropy_length);
while (to_read > 0) {
n = read(fd, rbuf, MIN(to_read, sizeof(rbuf)));
if (n <= 0) break;
switch (PS(hash_func)) {
case PS_HASH_FUNC_MD5:
PHP_MD5Update(&md5_context, rbuf, n);
break;
case PS_HASH_FUNC_SHA1:
PHP_SHA1Update(&sha1_context, rbuf, n);
break;
#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
case PS_HASH_FUNC_OTHER:
PS(hash_ops)->hash_update(hash_context, rbuf, n);
break;
#endif /* HAVE_HASH_EXT */
}
to_read -= n;
}
close(fd);
}
#endif
}
digest = emalloc(digest_len + 1);
switch (PS(hash_func)) {
case PS_HASH_FUNC_MD5:
PHP_MD5Final(digest, &md5_context);
break;
case PS_HASH_FUNC_SHA1:
PHP_SHA1Final(digest, &sha1_context);
break;
#if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
case PS_HASH_FUNC_OTHER:
PS(hash_ops)->hash_final(digest, hash_context);
efree(hash_context);
break;
#endif /* HAVE_HASH_EXT */
}
if (PS(hash_bits_per_character) < 4
|| PS(hash_bits_per_character) > 6) {
PS(hash_bits_per_character) = 4;
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ini setting hash_bits_per_character is out of range (should be 4, 5, or 6) - using 4 for now");
}
outid = emalloc((size_t)((digest_len + 2) * ((8.0f / PS(hash_bits_per_character)) + 0.5)));
j = (int) (bin_to_readable((char *)digest, digest_len, outid, (char)PS(hash_bits_per_character)) - outid);
efree(digest);
if (newlen) {
*newlen = j;
}
return outid;
}
As you can see the actual id is a hash of a mixture of things, like the time of day. So there is a possibility of running into a conflict, however, it has a very low possibility. So much so, it is not worth worrying about unless you have lots of concurrent users.
正如你所看到的,实际的 id 是混合的东西的哈希,比如一天中的时间。所以有可能会发生冲突,但是可能性很小。这么多,除非你有很多并发用户,否则不值得担心。
However, if you really are worried you can increase the entropy by setting a different hash algorithm session.hash_function
但是,如果你真的很担心你可以通过设置不同的哈希算法来增加熵session.hash_function
As far as monitoring active sessions, this question covers it well Is it possible to see active sessions using php?
至于监控活动会话,这个问题很好地涵盖了它是否可以使用php查看活动会话?
If you are using a single instance of php on a single machine, then it actually has a built in session manager that checks whether an id already exists before assigning it. However, if you are running multiple instances or multiple machines it has no way of knowing what ids have been assigned by other machines.
如果你在一台机器上使用单个 php 实例,那么它实际上有一个内置的会话管理器,它在分配一个 id 之前检查它是否已经存在。但是,如果您正在运行多个实例或多台机器,则无法知道其他机器分配了哪些 ID。

