场景:
项目中集成了NanoHttp,用于在Android端搭建了http与https服务访问。
问题:
①:新版本开发时,某一次安全扫描中,出现了一次设备执行网络安全扫描后,http服务无法访问,但是https服务能正常访问,重启设备后恢复。当时版本由于安全修改了安全策略,日志中无法打印请求头信息,并且后续再次触发安全扫描也没复现。
②:2个月后,在老版本维护项目中,在安全扫描后,出现http服务能访问,但设备响应较慢问题。同样,https服务访问正常,没有出现响应慢问题。由于老版本还没在日志中限制对请求头的打印,于是可以进行模拟请求来复现与排查。
排查:
①:通过两个份日志排查出,在出现http服务响应异常时,均为一条“/src/acloglogin.php”的url一直在循环请求,导致http通路一直被占用。但在设备处于死循环状态时,通过tcpdump对设备http端口抓包,但并没有抓到对应的请求,说明并非外部循环请求导致,而是内部代码逻辑出现了问题。


②:通过第2份日志可以获取到该条http请求中的header内容,异常点:headers中,参数字符长度非常长。使用Http服务的demo进行模拟复现,通过ApiFox来模拟该条“/src/acloglogin.php”请求,并结合tcpdump抓包。


③:通过抓包可以看出,整个请求的长度为8343 byte,实际请求一次,就会出现死循环问题。


④:使用同样的请求,删减掉 headers 中大部分字段长度,发起请求,没有出现死循环问题。
由上述信息可证实,由 headers 中超长串的参数字符导致。

⑤:于是,查看 nanohttp 源码的请求解析过程,发现其 buffer 最大为 8kb。并且通过 debug
调试,接收超长 header 后,一直循环在执行 execute 解析过程。

⑥:通过 debug 调试可以分析出,核心问题为 nanohttp 对流处理不恰当导致。由于请求头
header 长度已经超过了 buffer 的大小,导致解析 header 时,未能找请求头末尾节点,
splitbyte=0,进而导致后续判断中,调用 inputstream 的 skip 方法时一直传入的是 0(这里
调用 skip 的目的是为了跳过已读取的流),导致出现死循环读取。

⑦:Github 上最新源码中,该问题依然存在

解决:
①:加入测试代码,在 splitbyte == 0 时,认为需要跳过整个 buffer 长度,测试发现不在出现
死循环问题。

②:修改方案:采用服务端拒绝连接方式,计算请求中的 headers 总长度 totalHeaderSize。
当 totalHeaderSize >= 阈值 MAX_HEADERS_PARAM_SIZE 时,则返回 http 标准错误码 431,
对应的。


③:修改后的抓包请求,存在 431 返回值,修改完成。

2万+

被折叠的 条评论
为什么被折叠?



