Подтвердить что ты не робот

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

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

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

Даже если PHP проверяет, существует ли уже сеанс с тем же идентификатором, и если это так, снова восстанавливает идентификатор... Я не думаю, что я хочу, чтобы система, КОГДА-ЛИБО, выдает тот же ID дважды, даже после мусора коллекция - возможно, я хочу сохранить их таблицу и проверить против них за возможный захват или что-то еще.

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

4b9b3361

Ответ 1

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

  • IP-адрес клиента
  • Текущее время
  • Генератор линейных конгруэнций PHP - генератор псевдослучайных чисел (PRNG)
  • случайный источник, специфичный для ОС - если ОС имеет доступный случайный источник (например,/dev/urandom)

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

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

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

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

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

Ответ 2

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

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

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