免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 23357 | 回复: 10

[Web] Boa Web Server 缺陷报告及其修正方法 [复制链接]

论坛徽章:
0
发表于 2006-09-07 17:18 |显示全部楼层
综述

Boa 作为一种轻巧实用的 WEB 服务器广泛应用于嵌入式设备上,
但 Boa 对实现动态网页的 CGI  的支持上仍存在一些缺陷,
本文描述了 Boa 对 CGI 的 Status/Location 的支持的缺陷及其修正方法.


版本: 所有版本 (0.94.13)
缺陷: BOA 解析 CGI 应答头时不能完整处理 Status 和 Location
缺陷描述:
CGI/1.1 标准规定, CGI 脚本可以通过 Status 设置 HTTP 应答状态(如, Status: 500 Internal Error) 和
Location 进行地址重定向 (如, Location: www.xxx.com), 而不管它们在应答头中的位置.
Boa 支持 Stauts 和 Location 两种应答头, 但它的实现仅能正确处理 Stauts 和 Location 在应答第一行的
CGI 应答. 这将给 CGI 程序的移植带来很多不便, 进而影响 Boa 作为Web Server 的功能的发挥.

影响功能:
ASP/PHP/JSP/Perl/... 等的 header, redirect, ... 等都会应用到 Stauts/Location 进行设置应答状态和
地址重定向. Boa 的该实现将影响 CGI 脚本正常功能的使用.

缺陷功能对比(对Status/Location的支持程序):
Apache 1.3.x/2.x         IIS 4.x/5.x/6.X        Boa 0.9x                 thttpd                 mini-httpd
完全支持                        完全支持                * 部分支持                 完全支持               完全支持

[ 本帖最后由 newzy 于 2006-9-7 18:02 编辑 ]

论坛徽章:
0
发表于 2006-09-07 17:51 |显示全部楼层

缺陷分析

缺陷分析

CGI 应用程序进行应答时, 可以 HTTP 头进行有限的控制. 如,设置客户端不缓存页面可用下面的 C 脚本,
HTTP/1.0: printf("Pragma: no-cache\n"); 或
HTTP/1.1: printf("Cache-Control: no-cache; no-store\n");
如果, 同时还需要告诉浏览器进行设置 Cookie 和控制相应状态(200 OK) 或地址重定向,
那么就必须输出多行 http 头控制语句, CGI 支持两个解析头 "Status: " 和 "Loction: ",
即协议规定, Web 服务器支持解析头时能使用 "Status: " 进行应答状态控制, 使用 "Location: " 进行地址重定向,
并为应答添加状态头 "HTTP/1.0 302 Moved Temporarily\n" 或 "HTTP/1.1 302 Found\n".
而不管它们在 CGI 应答头的什么位置.

分析 Boa Source Code:
cgi_header.c  Line 82-136 容易发现, Boa 只解析 CGI 应答的第一行, 是否为 "Status: ", "Location: ", 如下所示


  1. 23
  2. 24         int process_cgi_header(request * req)
  3. 25         {
  4. 26             char *buf;
  5. 27             char *c;
  6. 28        
  7. 29             if (req->cgi_status != CGI_DONE)
  8. 30                 req->cgi_status = CGI_BUFFER;
  9. 31        
  10. 32             buf = req->header_line;
  11. 33        
  12. 34             c = strstr(buf, "\n\r\n");
  13. 35             if (c == NULL) {
  14. 36                 c = strstr(buf, "\n\n");
  15. 37                 if (c == NULL) {
  16. 38                     log_error_time();
  17. 39                     fputs("cgi_header: unable to find LFLF\n", stderr);
  18. 40         #ifdef FASCIST_LOGGING
  19. 41                     log_error_time();
  20. 42                     fprintf(stderr, "\"%s\"\n", buf);
  21. 43         #endif
  22. 44                     send_r_bad_gateway(req);
  23. 45                     return 0;
  24. 46                 }
  25. 47             }
  26. 48             if (req->simple) {
  27. 49                 if (*(c + 1) == '\r')
  28. 50                     req->header_line = c + 2;
  29. 51                 else
  30. 52                     req->header_line = c + 1;
  31. 53                 return 1;
  32. 54             }
  33. 55             if (!strncasecmp(buf, "Status: ", 8)) {
  34. 56                 req->header_line--;
  35. 57                 memcpy(req->header_line, "HTTP/1.0 ", 9);
  36. 58             } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
  37. 59         #ifdef FASCIST_LOGGING
  38. 60        
  39. 61                 log_error_time();
  40. 62                 fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
  41. 63                         __FILE__, __LINE__, buf + 10);
  42. 64         #endif
  43. 65        
  44. 66        
  45. 67                 if (buf[10] == '/') {   /* virtual path */
  46. 68                     log_error_time();
  47. 69                     fprintf(stderr,
  48. 70                             "server does not support internal redirection: " \
  49. 71                             "\"%s\"\n", buf + 10);
  50. 72                     send_r_bad_request(req);
  51. 73        
  52. 74                     /*
  53. 75                      * We (I, Jon) have declined to support absolute-path parsing
  54. 76                      * because I see it as a major security hole.
  55. 77                      * Location: /etc/passwd or Location: /etc/shadow is not funny.
  56. 78                      *
  57. 79                      * Also, the below code is borked.
  58. 80                      * request_uri could contain /cgi-bin/bob/extra_path
  59. 81                      */
  60. 82        
  61. 83                     /*
  62. 84                        strcpy(req->request_uri, buf + 10);
  63. 85                        return internal_redirect(req);
  64. 86                      */
  65. 87                 } else {                /* URL */
  66. 88                     char *c2;
  67. 89                     c2 = strchr(buf + 10, '\n');
  68. 90                     /* c2 cannot ever equal NULL here because we already have found one */
  69. 91        
  70. 92                     --c2;
  71. 93                     while (*c2 == '\r')
  72. 94                         --c2;
  73. 95                     ++c2;
  74. 96                     /* c2 now points to a '\r' or the '\n' */
  75. 97                     *c2++ = '\0';       /* end header */
  76. 98        
  77. 99                     /* first next header, or is at req->header_end */
  78. 100                    while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
  79. 101                        ++c2;
  80. 102                    if (c2 == req->header_end)
  81. 103                        send_r_moved_temp(req, buf + 10, "");
  82. 104                    else
  83. 105                        send_r_moved_temp(req, buf + 10, c2);
  84. 106                }
  85. 107                req->status = DONE;
  86. 108                return 1;
  87. 109            } else {                    /* not location and not status */
  88. 110                char *dest;
  89. 111                int howmuch;
  90. 112                send_r_request_ok(req); /* does not terminate */
  91. 113                /* got to do special things because
  92. 114                   a) we have a single buffer divided into 2 pieces
  93. 115                   b) we need to merge those pieces
  94. 116                   Easiest way is to memmove the cgi data backward until
  95. 117                   it touches the buffered data, then reset the cgi data pointers
  96. 118                 */
  97. 119                dest = req->buffer + req->buffer_end;
  98. 120                if (req->method == M_HEAD) {
  99. 121                    if (*(c + 1) == '\r')
  100. 122                        req->header_end = c + 2;
  101. 123                    else
  102. 124                        req->header_end = c + 1;
  103. 125                    req->cgi_status = CGI_DONE;
  104. 126                }
  105. 127                howmuch = req->header_end - req->header_line;
  106. 128       
  107. 129                if (dest + howmuch > req->buffer + BUFFER_SIZE) {
  108. 130                    /* big problem */
  109. 131                    log_error_time();
  110. 130             fprintf(stderr, "Too much data to move! Aborting! %s %d\n",
  111. 131                    __FILE__, __LINE__);
  112. 132              /* reset buffer pointers because we already called
  113. 133                 send_r_request_ok... */
  114. 134              req->buffer_start = req->buffer_end = 0;
  115. 135              send_r_error(req);
  116. 136              return 0;
  117. 137          }
  118. 138          memmove(dest, req->header_line, howmuch);
  119. 139          req->buffer_end += howmuch;
  120. 140          req->header_line = req->buffer + req->buffer_end;
  121. 141          req->header_end = req->header_line;
  122. 142          req_flush(req);
  123. 143          if (req->method == M_HEAD)
  124. 144              return 0;
  125. 145      }
  126. 146      return 1;
  127. 147  }
  128. 148
  129. 149
复制代码

论坛徽章:
0
发表于 2006-09-07 17:58 |显示全部楼层
修正方法

CGI 应答头包括多行, 我们必须对其进行逐行分析, 并作出正确的应答.
下面是修改好的源程序, 即将原来的 82-136 (即相当下文#else, #endif内部分) 替换成如下代码:



  1. #if 1
  2.     while(1) {
  3.                 int         len;
  4.                 char *        pnext = NULL;
  5.                 char *         ptmp = NULL;
  6.        
  7.                 /* not find HTTP header tailer */
  8.                 if (NULL == (pnext=strchr(buf, '\n')))        /* has no '\n' */
  9.                         break;
  10.                
  11.                 /* the length of this line,  
  12.                  * include '\n'
  13.                  */
  14.                 len = pnext - buf + 1;       

  15.                 if (!strncasecmp(buf, "Location: ", 10)) {        /* got a location header */
  16.                     /* not the first one
  17.                      * exchange this line to the first line
  18.                      */
  19.                     if (buf != req->header_line)
  20.                         {                   
  21.                                 if (NULL == (ptmp=(char *)malloc(len)))
  22.                         {
  23.                                 log_error_time();
  24.                                 perror("malloc");
  25.                             send_r_error(req);
  26.                             return 0;
  27.                         }
  28.                 
  29.                         /* move Status: to line header */
  30.                         memcpy(ptmp, buf, len);
  31.                         memmove(req->header_line+len, req->header_line, buf-req->header_line);      
  32.                         memcpy(req->header_line, ptmp, len);
  33.                         free(ptmp);
  34.                     }
  35.                     
  36.                     /* force pointer header */
  37.                     buf = req->header_line;
  38.                     
  39. #ifdef FASCIST_LOGGING
  40.        
  41.                 log_error_time();
  42.                 fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
  43.                         __FILE__, __LINE__, buf + 10);
  44. #endif
  45.        
  46.        
  47.                 if (buf[10] == '/') {   /* virtual path */
  48.                     log_error_time();
  49.                     fprintf(stderr,
  50.                             "server does not support internal redirection: " \
  51.                             "\"%s\"\n", buf + 10);
  52.                     send_r_bad_request(req);
  53.        
  54.                     /*
  55.                      * We (I, Jon) have declined to support absolute-path parsing
  56.                      * because I see it as a major security hole.
  57.                      * Location: /etc/passwd or Location: /etc/shadow is not funny.
  58.                      *
  59.                      * Also, the below code is borked.
  60.                      * request_uri could contain /cgi-bin/bob/extra_path
  61.                      */
  62.        
  63.                     /*
  64.                        strcpy(req->request_uri, buf + 10);
  65.                        return internal_redirect(req);
  66.                      */
  67.                 } else {                /* URL */
  68.                     char *c2;
  69.                     c2 = strchr(buf + 10, '\n');
  70.                     /* c2 cannot ever equal NULL here because we already have found one */
  71.        
  72.                     --c2;
  73.                     while (*c2 == '\r')
  74.                         --c2;
  75.                     ++c2;
  76.                     /* c2 now points to a '\r' or the '\n' */
  77.                     *c2++ = '\0';       /* end header */
  78.        
  79.                     /* first next header, or is at req->header_end */
  80.                     while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
  81.                         ++c2;
  82.                     if (c2 == req->header_end)
  83.                         send_r_moved_temp(req, buf + 10, "");
  84.                     else
  85.                         send_r_moved_temp(req, buf + 10, c2);
  86.                 }
  87.                 req->status = DONE;
  88.                 return 1;
  89.             }  else if (!strncasecmp(buf, "Status: ", 8)) {
  90.                     /* not the first one
  91.                      * exchange this line to the first line
  92.                      */
  93.                     if (buf != req->header_line)
  94.                         {                   
  95.                                 if (NULL == (ptmp=(char *)malloc(len)))
  96.                         {
  97.                                 log_error_time();
  98.                                 perror("malloc");
  99.                             send_r_error(req);
  100.                             return 0;
  101.                         }
  102.                 
  103.                         /* move Status: to line header */
  104.                         memcpy(ptmp, buf, len);
  105.                         memmove(req->header_line+len, req->header_line, buf-req->header_line);      
  106.                         memcpy(req->header_line, ptmp, len);
  107.                         free(ptmp);
  108.                     }
  109.                     
  110.                 req->header_line--;
  111.                 memcpy(req->header_line, "HTTP/1.0 ", 9);
  112.                 return 1;
  113.             }
  114.             
  115.             /* pointer to next line */
  116.             buf = pnext + 1;
  117.             
  118.             /* reach the end of HTTP header */
  119.             if ('\0' == buf[0] || '\n' == buf[0] || '\r' == buf[0])
  120.                     break;
  121.         }
  122.        
  123.     if (1) {        /* always done */
  124. #else
  125.     if (!strncasecmp(buf, "Status: ", 8)) {
  126.         req->header_line--;
  127.         memcpy(req->header_line, "HTTP/1.0 ", 9);
  128.     } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
  129. #ifdef FASCIST_LOGGING

  130.         log_error_time();
  131.         fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
  132.                 __FILE__, __LINE__, buf + 10);
  133. #endif


  134.         if (buf[10] == '/') {   /* virtual path */
  135.             log_error_time();
  136.             fprintf(stderr,
  137.                     "server does not support internal redirection: " \
  138.                     "\"%s\"\n", buf + 10);
  139.             send_r_bad_request(req);

  140.             /*
  141.              * We (I, Jon) have declined to support absolute-path parsing
  142.              * because I see it as a major security hole.
  143.              * Location: /etc/passwd or Location: /etc/shadow is not funny.
  144.              *
  145.              * Also, the below code is borked.
  146.              * request_uri could contain /cgi-bin/bob/extra_path
  147.              */

  148.             /*
  149.                strcpy(req->request_uri, buf + 10);
  150.                return internal_redirect(req);
  151.              */
  152.         } else {                /* URL */
  153.             char *c2;
  154.             c2 = strchr(buf + 10, '\n');
  155.             /* c2 cannot ever equal NULL here because we already have found one */

  156.             --c2;
  157.             while (*c2 == '\r')
  158.                 --c2;
  159.             ++c2;
  160.             /* c2 now points to a '\r' or the '\n' */
  161.             *c2++ = '\0';       /* end header */

  162.             /* first next header, or is at req->header_end */
  163.             while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
  164.                 ++c2;
  165.             if (c2 == req->header_end)
  166.                 send_r_moved_temp(req, buf + 10, "");
  167.             else
  168.                 send_r_moved_temp(req, buf + 10, c2);
  169.         }
  170.         req->status = DONE;
  171.         return 1;
  172.     } else {                    /* not location and not status */
  173. #endif

复制代码

论坛徽章:
0
发表于 2006-09-15 16:32 |显示全部楼层
老大..佩服中..顶一下..

论坛徽章:
0
发表于 2006-09-25 18:53 |显示全部楼层
厉害。。。。

论坛徽章:
0
发表于 2006-09-25 20:14 |显示全部楼层
我喜欢看unified diff……

论坛徽章:
0
发表于 2007-12-29 10:36 |显示全部楼层
好贴!!!!!!!!!!!!!!

论坛徽章:
0
发表于 2012-04-01 22:44 |显示全部楼层
2006年9月,那年我刚上大学。。。2012年4月,我遇到了这个问题。。。

论坛徽章:
0
发表于 2012-04-25 16:48 |显示全部楼层
兄台!!!感谢啊!!!啥也不说了!!

论坛徽章:
1
CU十二周年纪念徽章
日期:2013-10-24 15:41:34
发表于 2014-06-05 12:19 |显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

SACC2021中国系统架构师大会

【数字转型 架构重塑】
2022年1月13-15日,由IT168旗下ITPUB企业社区平台主办的第十四届中国系统架构师大会(SACC2021)将在云端进行网络直播。
为期3天的议程,涉及20+专场,近120个主题,完整迁移到线上进行网络直播对会议组织来说绝非易事;但考虑到云端会议的直播形式可以实现全国各地技术爱好者的参与,也使ITPUB作为技术共享交流平台得到更好的普及,我们决定迎难而上。

大会官网
  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP