Skip to content

Latest commit

 

History

History
82 lines (57 loc) · 2.86 KB

0002-max-http-header.md

File metadata and controls

82 lines (57 loc) · 2.86 KB

Max HTTP Header Size

ใครที่เอา code server ขึ้นโดยที่ไม่ได้เอา reverse proxy มาคั่น ต้องระวังเรื่อง Max HTTP Request Header Size

HTTP Header คืออะไร

HTTP header คือ metadata ที่เราส่งไปด้วยกับ request หรือ response

เช่น

GET / HTTP/1.1
Host: example.com
Accept: */*

header ก็คือ Host และ Accept

ปัญหา

ถ้าเราสร้าง HTTP server แล้วไม่ได้กำหนด Max size ค่า default อาจจะเยอะมาก ๆ เช่น

package main

import (
	"log"
	"net/http"
	"runtime"
	"time"
)

func main() {
	s := http.Server{}
	s.Addr = ":8080"
	s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("ok"))
	})
	go func() {
		for {
			var m runtime.MemStats
			runtime.ReadMemStats(&m)
			log.Printf("alloc: %d MiB", m.Alloc/1024/1024)
			time.Sleep(2 * time.Second)
		}
	}()
	log.Fatal(s.ListenAndServe())
}

ดูเหมือนไม่มีอะไร แต่ถ้าเราลองยิง request ที่ header ใหญ่ ๆ เข้าไปหล่ะ

server-memory-usage

จะเห็นว่าตัว server ของเราจะใช้ memory เยอะมาก ๆ

ซึ่งผมเคยโดน DoS (Denial of Service) Attack มารอบนึง คนที่โจมตีไม่ได้ส่ง request/s มาเยอะเลย แต่ส่ง header มาใหญ่มาก ๆ ซึ่งค่า default ของ Go ก็คือ 1 MiB

แสดงว่าคนโจมตีแค่ส่งมาแค่ 1,000 request แต่ส่ง header มา request ละ 1 MiB เราก็ต้องใช้ memory 1 GiB มารับ requests เหล่านั้น ทำให้เครื่อง server ram หมด

วิธีแก้

ง่ายมาก เราก็แค่ใส่ max header size ไปตอนที่สร้าง server เช่น

// ...
func main() {
	s := http.Server{
		MaxHeaderBytes: 1 << 14, // 16 KiB
	}
    // ...

ทำให้คนที่จะโจมตี server เรา สามารถส่ง header มาได้แค่ 16 KiB เราก็จะสามารถรับ request ได้เยอะขึ้น โดยใช้ memory น้อยลง

server-memory-usage-2

สรุป

Server ที่รับ request ตรงจาก user อย่าลืมตั้ง Max Header Size ไม่ว่าจะเป็น reverse proxy หรือว่าจะเป็น code ของเราเองก็ตาม