免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 15701 | 回复: 4
打印 上一主题 下一主题

解析PHP默认的session id生成算法 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-08-18 14:21 |只看该作者 |倒序浏览
解析PHP默认的session id生成算法




作为一个web程序猿,我们对session肯定都不陌生,session id是我们各自在服务器上的一个唯一标志,这个id串既可以由php自动来生成,也可以由我们来赋予。你们可能和我一样,很关心php自动生成的那个id串是怎么来的,冲突的概率有多大,以及容不容易被别人计算出来,所以有了下文。

我们下载一份php5.3.6的源码,进入/ext/session目录,生成session id的函数位于session.c文件的345行,下面详细介绍一下这个函数。为了方面理解,我调整了一些代码的顺序。

  1. PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
  2. {

  3. //这几行行定义了些散列函数所需的数据,直接越过~
  4. PHP_MD5_CTX md5_context;
  5. PHP_SHA1_CTX sha1_context;

  6. #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
  7. void *hash_context;
  8. #endif

  9. unsigned char *digest;
  10. int digest_len;
  11. int j;

  12. char *buf, *outid;

  13. zval **array;
  14. zval **token;

  15. //用来记录$_SERVER['REMOTE_ADDR']的值
  16. char *remote_addr = NULL;

  17. //一个timeval结构,用来记录当前的时间戳及毫秒数
  18. struct timeval tv;
  19. gettimeofday(&tv, NULL);

  20. //如果可能的话,就对remote_ADDR进行赋值,用php伪代码表示便是:
  21. //if(isset($_SERVER['REMOTE_ADDR']))
  22. //{remote_addr = $_SERVER['REMOTE_ADDR'];}
  23. //备注:在cli模式下是没有的~
  24. if (
  25.      zend_hash_find(
  26.          &EG(symbol_table),
  27.          "_SERVER",
  28.          sizeof("_SERVER"),
  29.          (void **) &array
  30.      ) == SUCCESS
  31.      && Z_TYPE_PP(array) == IS_ARRAY
  32.      && zend_hash_find(
  33.          Z_ARRVAL_PP(array),
  34.          "REMOTE_ADDR",
  35.          sizeof("REMOTE_ADDR"),
  36.          (void **) &token
  37.      ) == SUCCESS
  38. )
  39. {
  40.      remote_addr = Z_STRVAL_PP(token);
  41. }

  42. /* maximum 15+19+19+10 bytes */
  43. //生成所需的session id,当然后面还需要后续的处理~
  44. //格式为:%.15s%ld%ld%0.8F,每一段的含义如下:
  45. //%.15s    remote_addr ? remote_addr : "" 这一行很容易理解
  46. //%ld        tv.tv_sec    当前的时间戳
  47. //%ld        (long int)tv.tv_usec 当前毫秒数
  48. //%0.8F    php_combined_lcg(TSRMLS_C) * 10 一个随机数
  49. spprintf(
  50.      &buf,
  51.      0,
  52.      "%.15s%ld%ld%0.8F",
  53.      remote_addr ? remote_addr : "",
  54.      tv.tv_sec,
  55.      (long int)tv.tv_usec,
  56.      php_combined_lcg(TSRMLS_C) * 10
  57. );

  58. //下面对buf字符串的值进行散列处理
  59. //检测session配置中的散列函数
  60. /*
  61. 300行:    enum{
  62.             PS_HASH_FUNC_MD5,
  63.             PS_HASH_FUNC_SHA1,
  64.             PS_HASH_FUNC_OTHER
  65.         };
  66. 812行:
  67. PHP_INI_ENTRY("session.hash_function","0",PHP_INI_ALL,OnUpdateHashFunc)
  68. 738行:
  69. static PHP_INI_MH(OnUpdateHashFunc)
  70. {
  71.     ......
  72.     ......
  73.     val = strtol(new_value, &endptr, 10);
  74.     if (endptr && (*endptr == '\0'))
  75.     {
  76.         /* Numeric value */
  77.          PS(hash_func) = val ? 1 : 0;
  78.          return SUCCESS;
  79.      }
  80.      ......
  81.      ......
  82. 可知PS(hash_func)的默认值为0,即PS_HASH_FUNC_MD5。
  83. */

  84. switch (PS(hash_func))
  85. {
  86.      //如果是md5,则用md5算法对我们的buf串进行散列处理。
  87.      case PS_HASH_FUNC_MD5:
  88.          PHP_MD5Init(&md5_context);
  89.          PHP_MD5Update(&md5_context, (unsigned char *) buf, strlen(buf));
  90.          digest_len = 16;
  91.          break;

  92.      //如果是SHA1,则用SHA1算法对我们的buf串进行散列处理。
  93.      case PS_HASH_FUNC_SHA1:
  94.          PHP_SHA1Init(&sha1_context);
  95.          PHP_SHA1Update(&sha1_context, (unsigned char *) buf, strlen(buf));
  96.          digest_len = 20;
  97.          break;

  98. #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
  99.      case PS_HASH_FUNC_OTHER:
  100.          if (!PS(hash_ops))
  101.          {
  102.              php_error_docref(
  103.                  NULL TSRMLS_CC,
  104.                  E_ERROR,
  105.                  "Invalid session hash function"
  106.              );
  107.              efree(buf);
  108.              return NULL;
  109.          }

  110.          hash_context = emalloc(PS(hash_ops)->context_size);
  111.          PS(hash_ops)->hash_init(hash_context);
  112.          PS(hash_ops)->hash_update(hash_context, (unsigned char *) buf, strlen(buf));
  113.          digest_len = PS(hash_ops)->digest_size;
  114.          break;
  115. #endif /* HAVE_HASH_EXT */

  116.      //如果没有散列函数,则报错,还是E_ERROR级别的,囧~
  117.      default:
  118.          php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid session hash function");
  119.          efree(buf);
  120.          return NULL;
  121. }

  122. //释放buf~
  123. //囧,那内容呢,内容已经去我们的hash_context里,比如md5_context、sha1_context。。。。。。
  124. efree(buf);

  125. /*
  126. session.entropy_file 给出了一个到外部资源(文件)的路径,
  127. 该资源将在会话 ID 创建进程中被用作附加的熵值资源。
  128. 例如在许多 Unix 系统下都可以用 /dev/random 或 /dev/urandom。
  129. session.entropy_length 指定了从上面的文件中读取的字节数。默认为 0(禁用)。

  130. 如果entropy_length这个配置大于0,则:
  131. */
  132. if (PS(entropy_length) > 0)
  133. {
  134. #ifdef PHP_WIN32
  135.      unsigned char rbuf[2048];
  136.      size_t toread = PS(entropy_length);
  137.      if (php_win32_get_random_bytes(rbuf, (size_t) toread) == SUCCESS)
  138.      {
  139.          switch (PS(hash_func))
  140.          {
  141.              case PS_HASH_FUNC_MD5:
  142.                  PHP_MD5Update(&md5_context, rbuf, toread);
  143.                  break;
  144.              case PS_HASH_FUNC_SHA1:
  145.                  PHP_SHA1Update(&sha1_context, rbuf, toread);
  146.                  break;
  147. # if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
  148.              case PS_HASH_FUNC_OTHER:
  149.                  PS(hash_ops)->hash_update(hash_context, rbuf, toread);
  150.                  break;
  151. # endif /* HAVE_HASH_EXT */
  152.          }
  153.      }
  154. #else
  155.      int fd;
  156.      fd = VCWD_OPEN(PS(entropy_file), O_RDONLY);
  157.      if (fd >= 0)
  158.      {
  159.          unsigned char rbuf[2048];
  160.          int n;
  161.          int to_read = PS(entropy_length);
  162.          while (to_read > 0) {
  163.              n = read(fd, rbuf, MIN(to_read, sizeof(rbuf)));
  164.              if (n hash_update(hash_context, rbuf, n);
  165.                      break;
  166. #endif /* HAVE_HASH_EXT */
  167.              }
  168.              to_read -= n;
  169.          }
  170.          close(fd);
  171.      }
  172. //结束entropy_length>0时的逻辑
  173. #endif
  174. }

  175. //还是散列计算的一部分,看来我们的hash_final(digest, hash_context);
  176.          efree(hash_context);
  177.          break;
  178. #endif /* HAVE_HASH_EXT */
  179. }

  180. /*
  181. session.hash_bits_per_character允许用户定义将二进制散列数据转换为可读的格式时每个字符存放多少个比特。
  182. 可能值为 '4'(0-9,a-f),'5'(0-9,a-v),以及 '6'(0-9,a-z,A-Z,"-",",")。
  183. */
  184. if (PS(hash_bits_per_character) < 4
  185.          || PS(hash_bits_per_character) > 6) {
  186.      PS(hash_bits_per_character) = 4;
  187.      php_error_docref(
  188.          NULL TSRMLS_CC,
  189.          E_WARNING,
  190.          "The ini setting hash_bits_per_character is out of range (should be 4, 5, or 6) - using 4 for now"
  191.      );
  192. }

  193. //将我们的散列后的二进制数据digest用字符串表示成可读的形式,并放置在outid字符串里
  194. outid = emalloc((size_t)((digest_len + 2) * ((8.0f / PS(hash_bits_per_character)) + 0.5)));
  195. j = (int) (bin_to_readable((char *)digest, digest_len, outid, (char)PS(hash_bits_per_character)) - outid);
  196. efree(digest);
  197. if (newlen) {
  198.      *newlen = j;
  199. }

  200. //返回outid
  201. return outid;
  202. }
复制代码
所以我们可以得出结论,php的默认session_id生成算法还是比较随机的,除非攻击者都够同时猜中时间戳、毫秒数、后面的那个随机数。

论坛徽章:
0
2
发表于 2011-08-18 20:56
原文地址:http://walu.sinaapp.com/?p=7
转文章应该注明出处~~

论坛徽章:
0
3 [报告]
发表于 2011-08-18 22:18 |只看该作者
谢谢分享

论坛徽章:
59
2015七夕节徽章
日期:2015-08-24 11:17:25ChinaUnix专家徽章
日期:2015-07-20 09:19:30每周论坛发贴之星
日期:2015-07-20 09:19:42ChinaUnix元老
日期:2015-07-20 11:04:38荣誉版主
日期:2015-07-20 11:05:19巳蛇
日期:2015-07-20 11:05:26CU十二周年纪念徽章
日期:2015-07-20 11:05:27IT运维版块每日发帖之星
日期:2015-07-20 11:05:34操作系统版块每日发帖之星
日期:2015-07-20 11:05:36程序设计版块每日发帖之星
日期:2015-07-20 11:05:40数据库技术版块每日发帖之星
日期:2015-07-20 11:05:432015年辞旧岁徽章
日期:2015-07-20 11:05:44
4 [报告]
发表于 2011-08-19 09:16 |只看该作者
厉害。学习了啊。

论坛徽章:
0
5 [报告]
发表于 2013-10-27 17:29 |只看该作者
如果并发很高呢?多用户的session id会不会重复?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP