G110:减压炸弹的潜在 DoS 漏洞 (gosec)

我收到以下 golintci 消息:

testdrive/utils.go:92:16: G110: Potential DoS vulnerability via decompression bomb (gosec)
    if _, err := io.Copy(targetFile, fileReader); err != nil {
                 ^

阅读相应的 CWE ,我不清楚如何纠正这一点。

请指点。

func unzip(archive, target string) error {
    reader, err := zip.OpenReader(archive)
    if err != nil {
        return err
    }

    for _, file := range reader.File {
        path := filepath.Join(target, file.Name) // nolint: gosec
        if file.FileInfo().IsDir() {
            if err := os.MkdirAll(path, file.Mode()); err != nil {
                return err
            }
            continue
        }

        fileReader, err := file.Open()
        if err != nil {
            return err
        }
        defer fileReader.Close() // nolint: errcheck

        targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
        if err != nil {
            return err
        }
        defer targetFile.Close() // nolint: errcheck

        if _, err := io.Copy(targetFile, fileReader); err != nil {
            return err
        }
    }

    return nil
}
stack overflow G110: Potential DoS vulnerability via decompression bomb (gosec)
原文答案
author avatar

接受的答案

基于提供的各种指针,替换

if _, err := io.Copy(targetFile, fileReader); err != nil {
  return err
}

for {
  _, err := io.CopyN(targetFile, fileReader, 1024)
  if err != nil {
    if err == io.EOF {
      break
    }
    return err
  }
}

PS 虽然这有助于内存占用,但这无助于 DDOS 攻击复制非常长和/或无限的流......


答案:

作者头像

您收到的警告来自 gosec 中提供的规则。

该规则专门检测文件解压缩时 io.Copy 的使用。

这是一个潜在问题,因为 io.Copy

src 复制到 dst 直到在 src 上达到 EOF 或发生错误。

因此,恶意负载可能会导致您的程序解压缩大量数据并耗尽内存,从而导致警告消息中提到的拒绝服务。

特别是,gosec 将检查 ( source ) 程序的 AST,并警告您使用 io.Copyio.CopyBuffer 以及以下任何一项:

  • "compress/gzip".NewReader
  • "compress/zlib".NewReaderNewReaderDict
  • "compress/bzip2".NewReader
  • "compress/flate".NewReaderNewReaderDict
  • "compress/lzw".NewReader
  • "archive/tar".NewReader
  • "archive/zip".NewReader
  • "*archive/zip".File.Open

使用 io.CopyN 删除警告,因为(引用)它“ copies n bytes (or until an error) from src to dst ”,从而让您(程序编写者)控制要复制多少字节。因此,您可以根据应用程序的可用资源或 n 传递任意大的 copy in chunks

作者头像

假设您正在处理压缩数据,您需要使用 io.CopyN
您可以尝试使用 --nocompress 标志的解决方法。但这会导致数据未压缩。

请参阅以下 PR 和相关问题: https://github.com/go-bindata/go-bindata/pull/50