Boa Web Server 缺陷报告及其修正方法
综述
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
完全支持 完全支持 * 部分支持 完全支持 完全支持
缺陷分析
| 缺陷分析 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: ", 如下所示 |
- 23
- 24 int process_cgi_header(request * req)
- 25 {
- 26 char *buf;
- 27 char *c;
- 28
- 29 if (req->cgi_status != CGI_DONE)
- 30 req->cgi_status = CGI_BUFFER;
- 31
- 32 buf = req->header_line;
- 33
- 34 c = strstr(buf, "\n\r\n");
- 35 if (c == NULL) {
- 36 c = strstr(buf, "\n\n");
- 37 if (c == NULL) {
- 38 log_error_time();
- 39 fputs("cgi_header: unable to find LFLF\n", stderr);
- 40 #ifdef FASCIST_LOGGING
- 41 log_error_time();
- 42 fprintf(stderr, "\"%s\"\n", buf);
- 43 #endif
- 44 send_r_bad_gateway(req);
- 45 return 0;
- 46 }
- 47 }
- 48 if (req->simple) {
- 49 if (*(c + 1) == '\r')
- 50 req->header_line = c + 2;
- 51 else
- 52 req->header_line = c + 1;
- 53 return 1;
- 54 }
- 55 if (!strncasecmp(buf, "Status: ", 8)) {
- 56 req->header_line--;
- 57 memcpy(req->header_line, "HTTP/1.0 ", 9);
- 58 } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
- 59 #ifdef FASCIST_LOGGING
- 60
- 61 log_error_time();
- 62 fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
- 63 __FILE__, __LINE__, buf + 10);
- 64 #endif
- 65
- 66
- 67 if (buf[10] == '/') { /* virtual path */
- 68 log_error_time();
- 69 fprintf(stderr,
- 70 "server does not support internal redirection: " \
- 71 "\"%s\"\n", buf + 10);
- 72 send_r_bad_request(req);
- 73
- 74 /*
- 75 * We (I, Jon) have declined to support absolute-path parsing
- 76 * because I see it as a major security hole.
- 77 * Location: /etc/passwd or Location: /etc/shadow is not funny.
- 78 *
- 79 * Also, the below code is borked.
- 80 * request_uri could contain /cgi-bin/bob/extra_path
- 81 */
- 82
- 83 /*
- 84 strcpy(req->request_uri, buf + 10);
- 85 return internal_redirect(req);
- 86 */
- 87 } else { /* URL */
- 88 char *c2;
- 89 c2 = strchr(buf + 10, '\n');
- 90 /* c2 cannot ever equal NULL here because we already have found one */
- 91
- 92 --c2;
- 93 while (*c2 == '\r')
- 94 --c2;
- 95 ++c2;
- 96 /* c2 now points to a '\r' or the '\n' */
- 97 *c2++ = '\0'; /* end header */
- 98
- 99 /* first next header, or is at req->header_end */
- 100 while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
- 101 ++c2;
- 102 if (c2 == req->header_end)
- 103 send_r_moved_temp(req, buf + 10, "");
- 104 else
- 105 send_r_moved_temp(req, buf + 10, c2);
- 106 }
- 107 req->status = DONE;
- 108 return 1;
- 109 } else { /* not location and not status */
- 110 char *dest;
- 111 int howmuch;
- 112 send_r_request_ok(req); /* does not terminate */
- 113 /* got to do special things because
- 114 a) we have a single buffer divided into 2 pieces
- 115 b) we need to merge those pieces
- 116 Easiest way is to memmove the cgi data backward until
- 117 it touches the buffered data, then reset the cgi data pointers
- 118 */
- 119 dest = req->buffer + req->buffer_end;
- 120 if (req->method == M_HEAD) {
- 121 if (*(c + 1) == '\r')
- 122 req->header_end = c + 2;
- 123 else
- 124 req->header_end = c + 1;
- 125 req->cgi_status = CGI_DONE;
- 126 }
- 127 howmuch = req->header_end - req->header_line;
- 128
- 129 if (dest + howmuch > req->buffer + BUFFER_SIZE) {
- 130 /* big problem */
- 131 log_error_time();
- 130 fprintf(stderr, "Too much data to move! Aborting! %s %d\n",
- 131 __FILE__, __LINE__);
- 132 /* reset buffer pointers because we already called
- 133 send_r_request_ok... */
- 134 req->buffer_start = req->buffer_end = 0;
- 135 send_r_error(req);
- 136 return 0;
- 137 }
- 138 memmove(dest, req->header_line, howmuch);
- 139 req->buffer_end += howmuch;
- 140 req->header_line = req->buffer + req->buffer_end;
- 141 req->header_end = req->header_line;
- 142 req_flush(req);
- 143 if (req->method == M_HEAD)
- 144 return 0;
- 145 }
- 146 return 1;
- 147 }
- 148
- 149
修正方法
CGI 应答头包括多行, 我们必须对其进行逐行分析, 并作出正确的应答.
下面是修改好的源程序, 即将原来的 82-136 (即相当下文#else, #endif内部分) 替换成如下代码:
- #if 1
- while(1) {
- int len;
- char * pnext = NULL;
- char * ptmp = NULL;
- /* not find HTTP header tailer */
- if (NULL == (pnext=strchr(buf, '\n'))) /* has no '\n' */
- break;
- /* the length of this line,
- * include '\n'
- */
- len = pnext - buf + 1;
- if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
- /* not the first one
- * exchange this line to the first line
- */
- if (buf != req->header_line)
- {
- if (NULL == (ptmp=(char *)malloc(len)))
- {
- log_error_time();
- perror("malloc");
- send_r_error(req);
- return 0;
- }
- /* move Status: to line header */
- memcpy(ptmp, buf, len);
- memmove(req->header_line+len, req->header_line, buf-req->header_line);
- memcpy(req->header_line, ptmp, len);
- free(ptmp);
- }
- /* force pointer header */
- buf = req->header_line;
- #ifdef FASCIST_LOGGING
- log_error_time();
- fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
- __FILE__, __LINE__, buf + 10);
- #endif
- if (buf[10] == '/') { /* virtual path */
- log_error_time();
- fprintf(stderr,
- "server does not support internal redirection: " \
- "\"%s\"\n", buf + 10);
- send_r_bad_request(req);
- /*
- * We (I, Jon) have declined to support absolute-path parsing
- * because I see it as a major security hole.
- * Location: /etc/passwd or Location: /etc/shadow is not funny.
- *
- * Also, the below code is borked.
- * request_uri could contain /cgi-bin/bob/extra_path
- */
- /*
- strcpy(req->request_uri, buf + 10);
- return internal_redirect(req);
- */
- } else { /* URL */
- char *c2;
- c2 = strchr(buf + 10, '\n');
- /* c2 cannot ever equal NULL here because we already have found one */
- --c2;
- while (*c2 == '\r')
- --c2;
- ++c2;
- /* c2 now points to a '\r' or the '\n' */
- *c2++ = '\0'; /* end header */
- /* first next header, or is at req->header_end */
- while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
- ++c2;
- if (c2 == req->header_end)
- send_r_moved_temp(req, buf + 10, "");
- else
- send_r_moved_temp(req, buf + 10, c2);
- }
- req->status = DONE;
- return 1;
- } else if (!strncasecmp(buf, "Status: ", 8)) {
- /* not the first one
- * exchange this line to the first line
- */
- if (buf != req->header_line)
- {
- if (NULL == (ptmp=(char *)malloc(len)))
- {
- log_error_time();
- perror("malloc");
- send_r_error(req);
- return 0;
- }
- /* move Status: to line header */
- memcpy(ptmp, buf, len);
- memmove(req->header_line+len, req->header_line, buf-req->header_line);
- memcpy(req->header_line, ptmp, len);
- free(ptmp);
- }
- req->header_line--;
- memcpy(req->header_line, "HTTP/1.0 ", 9);
- return 1;
- }
- /* pointer to next line */
- buf = pnext + 1;
- /* reach the end of HTTP header */
- if ('\0' == buf[0] || '\n' == buf[0] || '\r' == buf[0])
- break;
- }
- if (1) { /* always done */
- #else
- if (!strncasecmp(buf, "Status: ", 8)) {
- req->header_line--;
- memcpy(req->header_line, "HTTP/1.0 ", 9);
- } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
- #ifdef FASCIST_LOGGING
- log_error_time();
- fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
- __FILE__, __LINE__, buf + 10);
- #endif
- if (buf[10] == '/') { /* virtual path */
- log_error_time();
- fprintf(stderr,
- "server does not support internal redirection: " \
- "\"%s\"\n", buf + 10);
- send_r_bad_request(req);
- /*
- * We (I, Jon) have declined to support absolute-path parsing
- * because I see it as a major security hole.
- * Location: /etc/passwd or Location: /etc/shadow is not funny.
- *
- * Also, the below code is borked.
- * request_uri could contain /cgi-bin/bob/extra_path
- */
- /*
- strcpy(req->request_uri, buf + 10);
- return internal_redirect(req);
- */
- } else { /* URL */
- char *c2;
- c2 = strchr(buf + 10, '\n');
- /* c2 cannot ever equal NULL here because we already have found one */
- --c2;
- while (*c2 == '\r')
- --c2;
- ++c2;
- /* c2 now points to a '\r' or the '\n' */
- *c2++ = '\0'; /* end header */
- /* first next header, or is at req->header_end */
- while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
- ++c2;
- if (c2 == req->header_end)
- send_r_moved_temp(req, buf + 10, "");
- else
- send_r_moved_temp(req, buf + 10, c2);
- }
- req->status = DONE;
- return 1;
- } else { /* not location and not status */
- #endif
转载连接:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=824840
Boa Web Server 缺陷报告及其修正方法的更多相关文章
- 简易web server之python实现
网络编程一项基本功是socket编程,包括TCP socket,UDP socket的客户端.服务器端编程. 应用层的各路协议如http,smtp,telnet,ftp等都依赖于传输层的TCP或者UD ...
- Tomcat建立多个应用(Web Server),多个主机,多个站点的方法
https://blog.csdn.net/chungle2011/article/details/52317433 http://piperzero.iteye.com/blog/1475773 转 ...
- 咏南中间件当作WEB SERVER使用方法
咏南中间件当作WEB SERVER使用方法 1)开启咏南中间件 2)浏览器打开http://localhost:5566/web?page=echo.html
- [Windows Server 2008] IIS配置伪静态方法(Web.config模式的IIS rewrite)
★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:安装伪静态(w ...
- VisualSVN Server的配置和使用方法(转)
1.为什么要用VisualSVN Server,而不用Subversion? 回答: 因为如果直接使用Subversion,那么在Windows 系统上,要想让它随系统启动,就要封装SVN Serve ...
- Web Server PROPFIND Method internal IP Discosure
Title:Web Server PROPFIND Method internal IP Discosure --2012-11-09 09:47 Nessus扫描出来一个安全缺陷,Web Serv ...
- The Web server is configured to not list the contents of this directory.
部署一个ASP.NET MVC网站至一个全新的服务器Windows Server 2008 R2, 数据为MS SQL Server 2014 64bit Expression版本. 运行时,它第一次 ...
- 【转】推荐介绍几款小巧的Web Server程序
原博地址:http://blog.csdn.net/heiyeshuwu/article/details/1753900 偶然看到几个小巧有趣的Web Server程序,觉得有必要拿来分享一下,让大家 ...
- VisualSVN Server的配置和使用方法 图文
转载 http://www.jb51.net/article/17365.htm VisualSVN Server是免费的,而VisualSVN是收费的.VisualSVN是SVN的客户端,和Visu ...
随机推荐
- HMS Toolkit助力开发者高效集成HMS Core
当你的应用想集成华为HMS Core服务和上线华为应用市场,或当你已经开发了一个Android应用并集成了第三方移动服务,需要迁移使用HMS Core服务和上线华为应用市场的时候,如何快速.便捷.高效 ...
- dot 语法总结
在使用pprof分析go的项目时,经常会查看各项指标的有向图 原理是使用Graphviz(Graph Visualization Software)解析生成的dot脚本得到最终展示给我们的图信息. d ...
- 01- linux入门
LINUX是什么? -linux是计算机操作操作系统 -常见的操作系统有:Windows,Android,苹果iOS,Mac系统,Unix(和linux类似) 操作系统是干什么的? 管理硬件和程序的一 ...
- wordpress 自定义路由及展示页
wordpress 自定义路由及展示页 注册domain/test这个路由 wordpress 有重写url的方法,叫 add_rewrite_rule().在function.php中加入如下代码段 ...
- 让vim显示空格,tab字符,及vim多行注释
1.显示 TAB 键 文件中有 TAB 键的时候,你是看不见的.要把它显示出来: :set list 现在 TAB 键显示为 ^I,而 $显示在每行的结尾,以便你能找到可能会被你忽略的空白字符在哪里 ...
- hdu3329 二分+搜索
题意: 给你一个岛,然后岛的外侧开始涨水(内侧不涨只有外侧,也就是里面的0永远是0),问最少涨水多少才能把岛分成两个或者两个以上. 思路: 可以二分枚举水的高度(数据不大估计暴 ...
- hdu2167 方格取数 状态压缩dp
题意: 方格取数,八个方向的限制. 思路: 八个方向的不能用最大流了,四个的可以,八个的不能抽象成二分图,所以目测只能用dp来跑,dp[i][j]表示的是第i行j状态的最优,具体看 ...
- hdu4884 模拟
题意: 一个厨师,他能炒n道菜,他每次炒菜用时t分钟,每次最多可以炒同样的菜k分,有m个人来买饭,给你每个人来的时间和菜的种类以及份数,问你每个人都是什么时候离开的. 思路: ...
- nodejs-REPL/回调函数/事件循环
REPL 回调函数 事件循环 REPL----------------------------------------------------- Node.js REPL(Read Eval Prin ...
- nodejs-安装/helloworld/npm
安装---------------------------------------------------------------- http://nodejs.cn/download/ 完成之后确定 ...