当我调用session_start()
或session_regenerate_id()
时,PHP会生成会话ID的随机字符串。我想知道的是,它只是一个随机的字符序列,还是像uniqid()
函数一样?
因为如果它只是随机字符,理论上你不能遇到冲突吗?如果用户A登录,然后用户B登录,虽然用户B生成相同的会话ID,但用户B最终不会访问用户A的帐户。
即使PHP检查是否已经存在具有相同ID的会话,如果是,再次重新生成ID ...我不认为我想要一个EVER生成相同ID两次的系统,即使在垃圾之后收藏 - 也许我想存储一张桌子并检查它们是否有可能被劫持或其他什么。
如果它不是唯一的,我应该如何强制执行唯一性?我宁愿使用PHP配置来实现它,而不是在我制作的每个脚本中实现它。 PHP会话的好处不是担心幕后的技术细节。
答案 0 :(得分:47)
如果您想知道默认情况下PHP如何生成会话ID,请查看Github上的源代码。 肯定不是随机的,并且基于这些成分的哈希值(默认值:md5)(参见代码段的第310行):
如果操作系统有可用的随机源,那么为了成为会话ID而生成的ID的强度很高( / dev / urandom和其他OS随机源(通常)是加密安全的PRNG )。但如果它没有那么它是令人满意的。
生成会话标识的目标是:
这是通过PHP的会话生成方法实现的。
你不能绝对保证唯一性,但概率很低,只能达到两次相同的哈希值,一般来说,不值得担心。
答案 1 :(得分:4)
以下是生成id的代码: Session.c
特别是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;
}
正如您所看到的,实际的id是混合事物的散列,比如一天中的时间。因此,有可能遇到冲突,但是它的可能性很小。除此之外,除非你有很多并发用户,否则不值得担心。
但是,如果您真的担心可以通过设置不同的哈希算法来增加熵session.hash_function
就监控活动会话而言,这个问题很好地涵盖了Is it possible to see active sessions using php?
如果您在一台计算机上使用单个php实例,那么它实际上有一个内置的会话管理器,可以在分配之前检查id是否已存在。但是,如果您正在运行多个实例或多台计算机,则无法知道其他计算机已分配了哪些ID。