物联网设备常见的web服务器——uhttpd源码分析(一)
字数 2061 2025-08-05 11:39:33
uHTTPd Web服务器源码分析教学文档
0x00 前言
uHTTPd 是一个为OpenWrt/LUCI开发者设计的轻量级Web服务器,主要特点包括:
- 专为嵌入式设备设计,资源占用低
- 与OpenWrt配置框架(UCI)深度整合
- 默认用于OpenWrt的Web管理接口LuCI
- 提供常规Web服务器所需的所有功能
0x01 为什么分析uHTTPd
分析uHTTPd的主要原因:
- 在物联网设备中极为常见
- 物联网设备漏洞多出现在Web服务器上
- 理解开源Web服务器开发流程有助于挖掘物联网漏洞
- 事半功倍的效果
源码下载地址:https://git.openwrt.org/?p=project/uhttpd.git;a=summary (点击snapshot下载)
0x02 主函数main分析
主要功能流程
- 初始化默认参数
- 设置信号屏蔽
- 解析用户输入参数
- 解析配置文件
- 初始化工作目录
- 初始化默认主页和CGI路径
- 初始化TLS(HTTPS)支持
- 初始化插件(Lua/UBUS)
- 创建守护进程
- 运行服务器主循环
关键代码结构
int main(int argc, char **argv) {
// 初始化
uh_dispatch_add(&cgi_dispatch);
init_defaults_pre();
signal(SIGPIPE, SIG_IGN);
// 参数解析
while ((ch = getopt(...)) != -1) {
// 处理各种参数
}
// 配置解析
uh_config_parse();
// 目录和默认设置
if (!conf.docroot) {
// 设置工作目录
}
init_defaults_post();
// TLS初始化
#ifdef HAVE_TLS
if (n_tls) {
uh_tls_init(tls_key, tls_crt, tls_ciphers);
}
#endif
// 插件初始化
#ifdef HAVE_LUA
uh_plugin_init("uhttpd_lua.so");
#endif
#ifdef HAVE_UBUS
uh_plugin_init("uhttpd_ubus.so");
#endif
// 守护进程
if (!nofork) {
fork();
}
// 主循环
return run_server();
}
0x03 信号处理
关键信号处理:
signal(SIGPIPE, SIG_IGN);
作用:
- SIGPIPE默认会导致进程终止
- 在发送/接收数据时可能产生SIGPIPE信号
- 设置SIG_IGN忽略该信号,避免进程意外退出
参考资料:
0x04 配置文件解析
uh_config_parse()函数功能:
- 默认配置文件路径:/etc/httpd.conf
- 支持解析的配置项:
- 认证信息:
/path:user:pass - 默认主页:
I:index.html - 错误处理:
E404:/error.html - 解释器:
*.ext:/path/to/interpreter
- 认证信息:
解析逻辑:
while (fgets(line, sizeof(line) - 1, c)) {
if ((line[0] == '/') && (strchr(line, ':') != NULL)) {
// 处理认证信息
uh_auth_add(line, col1, col2);
} else if (!strncmp(line, "I:", 2)) {
// 处理默认主页
uh_index_add(strdup(col1));
} else if (!strncmp(line, "E404:", 5)) {
// 处理错误页面
conf.error_handler = strdup(col1);
} else if ((line[0] == '*') && (strchr(line, ':') != NULL)) {
// 处理解释器
uh_interpreter_add(col1, col2);
}
}
0x05 默认设置初始化
init_defaults_post()函数功能:
- 设置默认主页文件:
- index.html
- index.htm
- default.html
- default.htm
- 初始化CGI路径:
- 将docroot与cgi_prefix拼接
- 如:/var/www + /cgi-bin = /var/www/cgi-bin
关键代码:
void init_defaults_post(void) {
uh_index_add("index.html");
uh_index_add("index.htm");
uh_index_add("default.html");
uh_index_add("default.htm");
if (conf.cgi_prefix) {
char *str = malloc(strlen(conf.docroot) + strlen(conf.cgi_prefix) + 1);
strcpy(str, conf.docroot);
strcat(str, conf.cgi_prefix);
conf.cgi_docroot_path = str;
conf.cgi_prefix_len = strlen(conf.cgi_prefix);
};
}
0x06 插件系统
uh_plugin_init()函数功能:
- 动态加载.so插件
- 查找并调用插件初始化函数
- 支持插件:Lua、UBUS等
关键流程:
- 使用dlopen加载.so文件
- 使用dlsym查找初始化函数
- 调用插件初始化函数
代码实现:
int uh_plugin_init(const char *name) {
// 加载动态库
dlh = dlopen(name, RTLD_LAZY | RTLD_GLOBAL);
// 查找初始化函数
sym = "uhttpd_plugin";
p = dlsym(dlh, sym);
// 调用初始化
list_add(&p->list, &plugins);
return p->init(&ops, &conf);
}
dlopen函数
- 功能:打开动态链接库
- 默认路径:/usr/lib/
- 失败返回NULL
- 参考:dlopen手册
dlsym函数
- 功能:获取符号地址
- 可获取函数或变量地址
- uHTTPd中用于获取初始化函数
- 参考:dlsym百科
0x07 服务器主循环
run_server()函数(待续):
- 主要功能:
- 初始化事件循环
- 设置监听socket
- 处理客户端连接
- 分发HTTP请求
关键参数说明
uHTTPd支持的主要命令行参数:
| 参数 | 功能描述 |
|---|---|
| -p | 指定监听端口 |
| -h | 指定文档根目录 |
| -H | 添加处理程序脚本 |
| -E | 指定错误处理程序 |
| -I | 添加索引页面 |
| -S | 禁用符号链接 |
| -D | 禁用目录列表 |
| -x | 设置CGI路径前缀 |
| -y | 添加别名 |
| -i | 添加解释器 |
| -t | 设置脚本超时 |
| -T | 设置网络超时 |
| -f | 禁止fork(前台运行) |
| -c | 指定配置文件 |
安全相关配置
-
认证配置:
- 格式:
/path:user:pass - 存储在/etc/httpd.conf中
- 格式:
-
解释器配置:
- 格式:
*.ext:/path/to/interpreter - 用于指定特定扩展名的处理程序
- 格式:
-
安全限制:
-S禁用符号链接(防止目录遍历)-D禁用目录列表(防止信息泄露)-R启用RFC1918过滤(限制内网访问)
开发建议
-
插件开发:
- 实现
uhttpd_plugin结构体 - 提供初始化函数
- 放置在/usr/lib/目录下
- 实现
-
功能扩展:
- 通过dispatch_handlers链表添加处理器
- 支持CGI、Lua等多种扩展方式
-
安全注意事项:
- 正确处理信号
- 验证所有输入路径
- 限制脚本执行权限
总结
uHTTPd作为物联网设备常用的轻量级Web服务器,其设计特点包括:
- 模块化架构,支持插件扩展
- 精简的代码实现,适合嵌入式环境
- 灵活的配置方式,支持命令行和配置文件
- 完善的安全控制选项
通过分析其源码,可以深入理解:
- 嵌入式Web服务器的设计原理
- HTTP协议的处理流程
- 插件系统的实现方式
- 安全防护的最佳实践
这为物联网设备的安全分析和漏洞挖掘提供了坚实基础。