บทที่ 21 · Part 6 — Evidence-Driven Quality

Fuzzing and Secure Boundaries

ใช้ fuzzing ตรวจ parser และ untrusted input พร้อมกำหนด limits ก่อนข้อมูลเข้าสู่ business logic

parser อาจผ่าน unit test 30 cases แต่ยัง panic เมื่อเจอ UTF-8 ที่ตัดกลาง, JSON ซ้อนลึก หรือเลขขนาดใหญ่ ผู้โจมตีไม่จำเป็นต้องรู้ business logic เพียงหาข้อมูลที่ใช้ CPU/memory มากหรือทำให้ boundary ตีความ ต่างกัน Fuzzing ช่วยสำรวจ input space แต่ต้องทำคู่กับ limits และ threat model

จบบทนี้คุณจะ

  • เขียน fuzz target จาก invariant และเก็บ regression input ที่มีความหมาย
  • วาง size/time/count limits ก่อน decode หรือ allocate
  • แยก validation, authentication และ authorization ที่ trust boundary

Fuzz Property ไม่ใช่ Random Unit Test

เลือก target ที่รับ untrusted bytes และรันเร็วแบบ deterministic เช่น parser, decoder, normalizer, ID/path handling หรือ round trip serialization เพิ่ม seeds ที่แทน valid/invalid categories แล้ว assert property เช่นไม่ panic, round trip คงค่า, reject input เกิน limit หรือผลลัพธ์ผ่าน invariant:

func FuzzParseBatchID(f *testing.F) {
    f.Add("batch-001")
    f.Add("")
    f.Add("ก้อนงาน-01")

    f.Fuzz(func(t *testing.T, input string) {
        id, err := ParseBatchID(input)
        if err != nil {
            return
        }
        if id.String() == "" {
            t.Fatal("valid batch id became empty")
        }
        reparsed, err := ParseBatchID(id.String())
        if err != nil || reparsed != id {
            t.Fatalf("round trip = (%v, %v), want %v", reparsed, err, id)
        }
    })
}

เมื่อ fuzz engine พบ failure มันลด input และบันทึก corpus เพื่อให้ go test รัน regression ต่อไป ตรวจ corpus ก่อน commit ว่าไม่มี secret/ข้อมูลส่วนบุคคล และอย่า fuzz network/DB จริงเพราะช้า, ไม่ deterministic และอาจสร้างผลกระทบภายนอก ให้แยก pure boundary function ออกมา

go test -fuzz=FuzzParseBatchID -fuzztime=30s ./internal/httpapi
go test ./...

Limit ก่อน Parse เมื่อทำได้

HTTP server ควรมี header/read/write/idle timeouts และ handler จำกัด body ด้วย http.MaxBytesReader ก่อน decode กำหนด maximum array items, string length, nesting/recursion และ batch size ตาม use case อย่า preallocate จาก count ที่ client ส่งโดยไม่มี upper bound แม้ schema type จะเป็น integer ถูกต้อง

validation ตอบว่า input มี shape/value ที่รับได้, authentication ตอบว่าใครเรียก และ authorization ตอบว่า identity นี้ทำ operation กับ resource นี้ได้หรือไม่ 3 ชั้นแทนกันไม่ได้ OpenAPI validation ไม่บังคับ authorization และข้อมูลจาก DB/queue ของ service อื่นยังเป็นข้อมูลข้าม trust boundary

Injection และ Sensitive Sinks

ใช้ parameterized SQL query แยก code จาก data ไม่ต่อ string แม้ input ผ่าน regex แล้ว ใช้ exec.CommandContext พร้อม arguments ไม่ส่ง user inputผ่าน shell ใช้ html/template สำหรับ HTML และ Go 1.24+ os.Root เมื่อ file operation ต้องถูกจำกัดใน directory root การ normalize path อย่างเดียว ไม่ป้องกัน symlink/race ครบ

สำหรับ token/key ใช้ crypto/rand ไม่ใช้ math/rand; ใช้ algorithm/protocol จาก vetted library ไม่ออกแบบ crypto เอง และเช็ก error ทุกครั้ง การเปรียบเทียบ secret ใช้ constant-time primitive เมื่อ protocol ต้องการ

Security policy ไม่ได้เกิดจาก code sample

rate limit, retention, allowed file type, authentication strength และข้อมูลที่ห้าม log ต้องมีเจ้าของ policy พร้อมเอกสารและวันที่ Engineering แปลง policy เป็น control/test/monitor แต่ไม่ควรคิดตัวเลข compliance เอง และการใช้ library ใดไม่ทำให้ระบบผ่านมาตรฐานโดยอัตโนมัติ

Fuzzing อยู่ตรงไหนใน Test Portfolio

unit test บอก known examples, fuzzing สำรวจ unknown combinations, integration test ตรวจ adapter จริง, race detector หา unsynchronized access และ security review มอง data flow/blast radius ไม่มีตัวใดแทนกัน ใน CI ให้รัน seed corpus ทุกครั้งและทำ longer fuzzing ตาม schedule หรือก่อน release พร้อม budget ที่ชัด

Production Toolbox

Default: native Go fuzzing + explicit limits + stdlib secure primitives ใช้ gosec เป็น SAST signal และ govulncheck ดู reachable known vulnerabilities แต่ผล tool ต้อง trace data flowก่อนจัด severity fuzz target ต้องเล็ก, pure และมี invariant ที่แข็งแรง

Checklist ของ Secure Boundary

  • ระบุ trust boundary, attacker-controlled fields และ blast radius
  • จำกัด bytes/items/depth/time ก่อน allocate หรือทำงานแพง
  • fuzz target มี meaningful seeds และ property ไม่ใช่แค่ไม่ panic
  • corpus ไม่มี secret และ failure ถูกเก็บเป็น regression
  • SQL/command/template/path ใช้ safe API ที่เหมาะกับ sink
  • authn, authz และ business validation แยกหน้าที่
  • error/log/trace ไม่รั่ว payload หรือ credential
  • dependency และ Go toolchain อยู่บน supported patched version

อ่านเพิ่ม: Go Fuzzing, Fuzzing tutorial, Go Security Best Practices และ os.Root