为了账号安全,请及时绑定邮箱和手机立即绑定

如何保护服务免受 gzip 的侵害?

如何保护服务免受 gzip 的侵害?

Go
慕娘9325324 2023-06-26 16:37:38
我有test.gzipjson 文件{"events": [{"uuid":"56c1718c-8eb3-11e9-8157-e4b97a2c93d3","timestamp":"2019-06-14 14:47:31 +0000","number":732,"user": {"full_name":"0"*1024*1024*1024}}]}full_name 归档包含 1GB 的0压缩文件大小 ~1Mb如何在拆包过程中保护我的服务,以免我的记忆结束?func ReadGzFile(filename string) ([]byte, error) {    fi, err := os.Open(filename)    if err != nil {        return nil, err    }    defer fi.Close()    fz, err := gzip.NewReader(fi)    if err != nil {        return nil, err    }    defer fz.Close()    s, err := ioutil.ReadAll(fz)    if err != nil {        return nil, err    }    return s, nil}func main() {    b, err := ReadGzFile("test.gzip")    if err != nil {        log.Println(err)    }    var dat map[string]interface{}    if err := json.Unmarshal(b, &dat); err != nil {        panic(err)    }    fmt.Println(dat)}在这种情况下,输出可以通过 OOMKiller 终止我的服务
查看完整描述

2 回答

?
GCT1015

TA贡献1827条经验 获得超4个赞

可能具有欺骗性的是,压缩后的大小可能明显小于允许的大小(您可以或您希望处理的大小)。在您的示例中,输入约为 1 MB,而未压缩的大小约为 1 GB。

在读取未压缩的数据时,您应该在达到合理的限制后停止。为了轻松做到这一点,您可以使用io.LimitReader()可以指定要读取的最大字节数的位置。是的,您必须包装解压缩的流,而不是原始的压缩流。

这是一个示例:

limited := io.LimitReader(fz, 2*1024*1024)

s, err := ioutil.ReadAll(limited)

上面的示例将可读数据限制为 2 MB。当解压后的数据超过这个数时会发生什么?返回io.Readerio.LimitReader()(顺便说一句io.LimitedReader)将报告io.EOF。这可以保护您的服务器免受攻击,但可能不是处理攻击的最佳方法。

由于您提到这是针对 REST API 的,因此更适合的解决方案是类似的http.MaxBytesReader(). 这会包装传递的读取器以读取直到给定的限制,如果达到了限制,它将返回一个错误,并将错误发送回 HTTP 客户端,并关闭底层的读取关闭器。如果默认行为http.MaxBytesReader()不适合你,检查它的来源,复制它并修改它,相对简单。根据您的需要进行调整。

另请注意,您不应将所有内容(未压缩的数据)读入内存。您可以传递“有限读取器”,json.NewDecoder()在解码输入 JSON 时将从给定读取器读取数据。当然如果通过的受限阅读器报错,解码就会失败。


查看完整回答
反对 回复 2023-06-26
?
红颜莎娜

TA贡献1842条经验 获得超12个赞

不要将所有内容读入内存。如果可能的话,对流进行操作。在您的示例中这是 100% 可能的:


func ReadGzFile(filename string) (io.ReadCloser, error) {

    fi, err := os.Open(filename)

    if err != nil {

        return nil, err

    }


    return gzip.NewReader(fi)

}


func main() {

    b, err := ReadGzFile("test.gzip")

    if err != nil {

        log.Println(err)

    }

    defer b.Close()

    var dat map[string]interface{}

    if err := json.NewDecoder(b).Decode(&dat); err != nil {

        panic(err)

    }

    fmt.Println(dat)

}

此Decode方法具有忽略第一个有效 JSON 对象之后流中的任何垃圾的副作用(可能需要也可能不需要)。就您而言,这似乎是一个好处。在某些情况下,可能并非如此。


查看完整回答
反对 回复 2023-06-26
  • 2 回答
  • 0 关注
  • 115 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信