Идентификаторы сеанса PHP – как они генерируются?

Когда я вызываю session_start() или session_regenerate_id() , PHP генерирует то, что кажется случайной строкой для идентификатора сеанса. Я хочу знать, это просто случайная последовательность символов, или это как uniqid() ?

Потому что, если это просто случайные персонажи, не могли бы вы теоретически столкнуться с конфликтом? Если пользователь A вошел в систему, а затем Пользователь B вошел в систему и, хотя и маловероятен, пользователь B сгенерировал тот же идентификатор сеанса, тогда пользователь B получит доступ к учетной записи пользователя A.

Даже если PHP проверяет, существует ли еще один сеанс с тем же идентификатором, и если это так, снова восстанавливает идентификатор … Я не думаю, что мне нужна система, которая ВСЕГДА производит один и тот же идентификатор дважды, даже после сбора мусора – возможно, я хочу сохранить их таблицу и проверить против них за возможный захват или что-то еще.

Если это не уникально, как мне нужно обеспечить уникальность? Я бы предпочел реализовать его с использованием PHP-конфигурации, чем в каждом скрипте, который я делаю. Хорошая вещь о сессиях PHP не беспокоит технические детали за кулисами.

    Если вы хотите знать, как PHP генерирует идентификатор сеанса по умолчанию, проверьте исходный код на Github . Это, конечно, не случайное и основано на хеше (по умолчанию: md5) этих ингредиентов (см. Строку 310 фрагмента кода):

    1. IP-адрес клиента
    2. Текущее время
    3. PHP Linear Congruence Generatorгенератор псевдослучайных чисел (PRNG)
    4. ОС-специфический случайный источник – если ОС имеет случайный источник (например, / dev / urandom)

    Если ОС имеет случайный источник, то сила сгенерированного идентификатора для идентификатора сеанса высока ( / dev / urandom и другие случайные источники ОС – это (обычно) криптографически безопасные PRNG ). Если, однако, это не удовлетворительно.

    Целью генерации идентификации сеанса является:

    1. минимизировать вероятность генерации двух идентификаторов сеанса с одинаковым значением
    2. сделать его очень сложным вычислительным способом генерировать случайные ключи и использовать один из них .

    Это достигается подходом PHP к генерации сеансов.

    Вы не можете абсолютно гарантировать уникальность , но вероятности настолько низки, что ударяют один и тот же хэш дважды, что, вообще говоря, не стоит беспокоиться.

    Вот код, который генерирует 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; } 

    Как вы можете видеть, фактический идентификатор представляет собой хеш смеси вещей, например, время суток. Таким образом, есть возможность столкнуться с конфликтом, однако у него очень низкая вероятность. Настолько, что это не стоит беспокоиться, если у вас нет много одновременных пользователей.

    Однако, если вы действительно волнуетесь, вы можете увеличить энтропию, установив другой алгоритм хеширования session.hash_function

    Что касается мониторинга активных сеансов, этот вопрос хорошо охватывает его. Можно ли видеть активные сеансы с использованием php?

    Если вы используете один экземпляр php на одной машине, то на самом деле он имеет встроенный диспетчер сеансов, который проверяет, существует ли идентификатор перед его назначением. Однако, если вы работаете с несколькими экземплярами или несколькими машинами, у него нет способа узнать, какие идентификаторы были назначены другими машинами.