繁体   English   中英

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

[英]How to protect service from gzip bomb?

我有带json的test.gzip文件

{"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的服务

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

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

这是一个示例,看起来像:

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

s, err := ioutil.ReadAll(limited)

上面的示例将可读数据限制为2 MB。 当解压缩的数据超过此数量时会发生什么? io.Reader通过返回io.LimitReader()这是由一种方式io.LimitedReader )将报告io.EOF 这样可以保护您的服务器免受攻击,但是可能不是处理它的最佳方法。

既然您提到这是针对其余API的,那么更适合的解决方案将是类似的http.MaxBytesReader() 这会将通过的读取器包装起来,直到指定的限制为止,如果达到该限制,它将返回错误,还将错误发送回HTTP客户端,并关闭基础的读取器。 如果http.MaxBytesReader()的默认行为不适合您,请检查其源代码,对其进行复制和修改,这相对简单。 根据您的需要进行调整。

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

不要将所有内容读入内存。 如果可能,在流上进行操作。 在您的示例中,这是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对象之后的流中的任何垃圾。 对于您而言,这似乎是一种好处。 在某些情况下,可能并非如此。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM