PHPのデフォルトのセッションを解析id生成アルゴリズム

6394 ワード

Webプログラムの猿として、私たちはsessionに対してきっとよく知らないに違いありません.session idは私たちがそれぞれサーバー上の唯一の標識で、このid列はphpで自動的に生成することができて、私たちが与えることができます.あなたたちは私と同じように、phpが自動的に生成したid列がどのように来たのか、衝突の確率がどれだけ大きいのか、他の人に計算されにくいのかに関心を持っているかもしれません.php 5をダウンロードします.3.6のソースコードは/ext/sessionディレクトリに入り、session idを生成する関数はsessionにある.cファイルの345行ですが、この関数について詳しく説明します.理解のために、いくつかのコードの順序を調整しました.
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;
#endif
unsigned char *digest;
int digest_len;
int j;
char *buf, *outid;
zval **array;
zval **token;
//    $_SERVER['REMOTE_ADDR']  
char *remote_addr = NULL;
//  timeval  ,              
struct timeval tv;
gettimeofday(&tv, NULL);
//      ,  remote_ADDR    , php       :
//if(isset($_SERVER['REMOTE_ADDR']))
//{remote_addr = $_SERVER['REMOTE_ADDR'];}
//  : cli       ~
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 */
//     session id,            ~
//   :%.15s%ld%ld%0.8F,        :
//%.15s    remote_addr ? remote_addr : ""         
//%ld        tv.tv_sec          
//%ld        (long int)tv.tv_usec      
//%0.8F    php_combined_lcg(TSRMLS_C) * 10      
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
);
//   buf           
//  session        
/*
300 :    enum{
            PS_HASH_FUNC_MD5,
            PS_HASH_FUNC_SHA1,
            PS_HASH_FUNC_OTHER
        };
812 :
PHP_INI_ENTRY("session.hash_function","0",PHP_INI_ALL,OnUpdateHashFunc)
738 :
static PHP_INI_MH(OnUpdateHashFunc)
{
    ......
    ......
    val = strtol(new_value, &endptr, 10);
    if (endptr && (*endptr == '\0'))
    {
        /* Numeric value */
         PS(hash_func) = val ? 1 : 0;
         return SUCCESS;
     }
     ......
     ......
  PS(hash_func)     0, PS_HASH_FUNC_MD5。
*/
switch (PS(hash_func))
{
     //   md5,  md5      buf       。
     case PS_HASH_FUNC_MD5:
         PHP_MD5Init(&md5_context);
         PHP_MD5Update(&md5_context, (unsigned char *) buf, strlen(buf));
         digest_len = 16;
         break;
     //   SHA1,  SHA1      buf       。
     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 */
     //        ,   ,  E_ERROR   , ~
     default:
         php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
         efree(buf);
         return NULL;
}
//  buf~
// ,    ,        hash_context ,  md5_context、sha1_context。。。。。。
efree(buf);
/*
session.entropy_file           (  )   ,
        ID                。
      Unix         /dev/random   /dev/urandom。
session.entropy_length                 。    0(  )。
  entropy_length      0, :
*/
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, (size_t) toread) == 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 hash_update(hash_context, rbuf, n);
                     break;
#endif /* HAVE_HASH_EXT */
             }
             to_read -= n;
         }
         close(fd);
     }
//  entropy_length>0    
#endif
}
//          ,     hash_final(digest, hash_context);
         efree(hash_context);
         break;
#endif /* HAVE_HASH_EXT */
}
/*
session.hash_bits_per_character                                  。
     '4'(0-9,a-f),'5'(0-9,a-v),   '6'(0-9,a-z,A-Z,"-",",")。
*/
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"
     );
}
//             digest            ,    outid    
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;
}
//  outid
return outid;
}

phpのデフォルトsession_id生成アルゴリズムは、攻撃者がタイムスタンプ、ミリ秒数、後のランダム数を同時に当てるのに十分でない限り、比較的ランダムである.