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 ...
随机推荐
- Ubuntu16.04下安装virtualbox,配置及卸载
我是通过添加源的方式安装 将下边的命令添加到/etc/apt/source.list中 deb https://download.virtualbox.org/virtualbox/debian xe ...
- ATMS中去拉起新的进程,并在新进程启动后调用attachApplication时,resume待启动的Activity
相关文章: ATMS中去pause Activity A. 目录 ATMS拉起新进程 堆栈 resumeTopActivityInnerLocked:1684, ActivityStack start ...
- Borrowers UVA - 230
I mean your borrowers of books - those mutilators of collections, spoilers of the symmetry of shel ...
- ELK安装和配置及常用插件安装
环境 CentOS 7.3 root 用户 JDK 版本:1.8(最低要求),主推:JDK 1.8.0_121 以上 关闭 firewall systemctl stop firewalld.serv ...
- Python中如何生成requirements.txt文件
Python项目中一般都包含一个名为 requirements.txt 文件,用来记录当前项目所有的依赖包和版本号,在一个新环境下通过该文件可以更方便的构建项目所需要的运行环境. 生成requirem ...
- 硬件篇-03-SLAM移动底盘电气设计
最近因为在忙毕设,专栏已经1个多月没更,对于托更我很抱歉.不过这几周真的没什么时间,Rick&Morty的最新集我到现在都还没看哈哈. 现在毕设已经搞得差不多了,水专栏文章的快乐生 ...
- 【布隆过滤器】基于Hutool库实现的布隆过滤器Demo
布隆过滤器出现的背景: 如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,存储 ...
- hdu4503 概率
题意: 湫湫系列故事--植树节 Time Limit: 1000/500 MS (Java/Others) Memory ...
- hdu4740 不错的简单搜索
题意: 给你一个n*n的图,给你驴和老虎的初始坐标和方向,已知他们的速度相同,他们走动的时候都是走直线,如果不能走,驴往右拐,老虎往左拐,如果拐了一次还走不了就原地不动,问他们的最早相遇位置 ...
- hdu 3721 树的最小直径
题意: 给你一棵树,让你改变一条边,改变之后依然是一棵树,然后问你怎样改变才能让树的直径最短.这里的改变一条边指的是指把一条边长度不变,连在别的两个点上. 思路: 首先求出树的 ...