简体   繁体   English

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

[英]How to protect service from gzip bomb?

I have test.gzip file with json 我有带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 filed contains 1GB of 0 , zipped filesize ~1Mb full_name包含1GB的0 ,压缩文件大小〜1Mb

how can I protect my service during unpacking so that my memory is not over? 打开包装时如何保护我的服务,以使我的记忆没有结束?

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)
}

In this case output can kill my service by OOMKiller 在这种情况下,输出可能会破坏OOMKiller的服务

What can be deceiving is that the compressed size may be significantly smaller that the allowed size (the size you can or you wish to handle). 可以欺骗的是,压缩后的大小可能大大小于允许的大小(您可以或希望处理的大小)。 In your example the input is about 1 MB, while the uncompressed size is about 1 GB. 在您的示例中,输入约为1 MB,而未压缩的大小约为1 GB。

While reading the uncompressed data you should stop after reaching a reasonable limit. 在读取未压缩的数据时,应在达到合理限制后停止。 To easily do that, you may use io.LimitReader() where you can specify the max amount of bytes you wish to read. 为了轻松做到这一点,您可以使用io.LimitReader()来指定希望读取的最大字节数。 Yes, you have to wrap the unzipped stream, not the original, compressed stream. 是的,您必须包装解压缩的流,而不是原始的压缩流。

This is an example how it would look like: 这是一个示例,看起来像:

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

s, err := ioutil.ReadAll(limited)

The above example limits the readable data to 2 MB. 上面的示例将可读数据限制为2 MB。 What happens when the unzipped data is more than that? 当解压缩的数据超过此数量时会发生什么? The io.Reader returned by io.LimitReader() (which is by the way an io.LimitedReader ) will report io.EOF . io.Reader通过返回io.LimitReader()这是由一种方式io.LimitedReader )将报告io.EOF This protects your server from the attack, but might not be the best way to handle it. 这样可以保护您的服务器免受攻击,但是可能不是处理它的最佳方法。

Since you mentioned this is for a rest API, a better suited solution would be the similar http.MaxBytesReader() . 既然您提到这是针对其余API的,那么更适合的解决方案将是类似的http.MaxBytesReader() This wraps the passed reader to read up until a given limit, and if that is reached, it returns an error, and also sends an error back to the HTTP client, and also closes the underlying read-closer. 这会将通过的读取器包装起来,直到指定的限制为止,如果达到该限制,它将返回错误,还将错误发送回HTTP客户端,并关闭基础的读取器。 If the default behavior of http.MaxBytesReader() is not suitable for you, check its sources, copy it and modify it, it's relatively simple. 如果http.MaxBytesReader()的默认行为不适合您,请检查其源代码,对其进行复制和修改,这相对简单。 Tune it to your needs. 根据您的需要进行调整。

Also note that you should not read everything (the uncompressed data) into memory. 另请注意,您不应将所有内容(未压缩的数据)读入内存。 You may pass the "limited reader" to json.NewDecoder() which will read from the given reader while decoding the input JSON. 您可以将“受限阅读器”传递给json.NewDecoder() ,在解码输入JSON时,该阅读器将从给定阅读器读取。 Of course if the passed limited reader reports an error, the decoding will fail. 当然,如果通过的受限阅读器报告错误,则解码将失败。

Don't read everything into memory. 不要将所有内容读入内存。 Operate on a stream if possible. 如果可能,在流上进行操作。 This is 100% possible in your example: 在您的示例中,这是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)
}

This Decode approach has the side effect (which may or may not be desirable) of ignoring any garbage in the stream after the first valid JSON object. 这种Decode方法具有副作用(可能会或可能不会理想),即忽略第一个有效JSON对象之后的流中的任何垃圾。 In your case, this seems like a benefit. 对于您而言,这似乎是一种好处。 In some cases, it may not be. 在某些情况下,可能并非如此。

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

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