Áp chót của sê-ri. Bài này về công cụ bảo mật mà Go có sẵn và các ngôn ngữ khác chưa có tương đương.

govulncheck và phân tích khả năng gọi tới

go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

Tôi chạy trên một dự án ghim golang.org/x/text@v0.3.7 — bản có lỗ hổng đã biết:

  === Symbol Results ===
  No vulnerabilities found.

  Your code is affected by 0 vulnerabilities.
  This scan also found 4 vulnerabilities in packages you import and 36
  vulnerabilities in modules you require, but your code doesn't appear to call
  these vulnerabilities.

Ba tầng số liệu, và đó là điểm khác biệt:

36 lỗ hổng trong module bạn yêu cầu — con số mà mọi máy quét khác đưa ra.

4 lỗ hổng trong package bạn thật sự import.

0 lỗ hổng mà mã của bạn gọi tới hàm bị ảnh hưởng.

govulncheck phân tích đồ thị lời gọi từ main xuống, và chỉ báo động khi có đường đi thật tới hàm có lỗ hổng.

Đặt cạnh bài 99 sê-ri Java: OSV-Scanner báo 73 lỗ hổng trên một pom.xml năm dòng, không kèm thông tin nào về việc mã có gọi tới hay không. Danh sách đó đúng nhưng khó ưu tiên.

Với govulncheck, con số bạn phải xử lý ngay là số thứ ba — và nó thường nhỏ hơn nhiều.

Đưa vào CI:

- run: go install golang.org/x/vuln/cmd/govulncheck@latest
- run: govulncheck ./...

Nó trả mã thoát khác 0 khi tìm thấy lỗ hổng gọi tới được, nên build đỏ đúng lúc cần.

Lưu ý: nó chỉ phân tích tĩnh, nên mã gọi qua phản chiếu hoặc plugin sẽ không được theo dõi. Với những chỗ đó, vẫn cần nâng cấp phòng ngừa.

Năm chỗ khác cần kiểm

SQL injection. Bài 49 đã nói: luôn dùng $1, không nối chuỗi. Và tên bảng, tên cột không thay bằng tham số được — phải đối chiếu danh sách cho phép.

db.QueryContext(ctx, `select * from don where ma = $1`, ma)   // đúng

Duyệt đường dẫn. Bài 44 đã nói, và nó đáng nhắc lại:

dich := filepath.Join(goc, tenDoNguoiDungNhap)
if !strings.HasPrefix(filepath.Clean(dich), goc) {
	return errors.New("đường dẫn không hợp lệ")
}

Từ Go 1.24 có os.Root giới hạn mọi thao tác trong một thư mục, an toàn hơn hẳn — nhưng trên 1.23 thì mẫu trên là thứ bạn có.

Mật mã. math/rand không dùng cho token, khoá, mật khẩu — nó đoán được. Dùng crypto/rand:

b := make([]byte, 32)
if _, err := rand.Read(b); err != nil { return err }   // crypto/rand

Băm mật khẩu thì golang.org/x/crypto/bcrypt hoặc argon2, không dùng sha256 — nó quá nhanh.

So sánh chuỗi bí mật phải dùng subtle.ConstantTimeCompare để tránh tấn công theo thời gian.

Giới hạn đầu vào. Bài 45 và 46:

r.Body = http.MaxBytesReader(w, r.Body, 1<<20)
io.LimitReader(r, 10<<20)

Không có nó, một request 10GB làm hết bộ nhớ máy chủ.

Timeout ở mọi nơi. Bài 46 đã đo: http.ListenAndServe không có timeout nào, và một kết nối gửi header dở dang giữ goroutine của bạn vĩnh viễn. Đây vừa là vấn đề hiệu năng vừa là lỗ hổng từ chối dịch vụ.

Ba thứ Go giúp sẵn

Đáng ghi nhận, vì chúng loại bỏ cả họ lỗ hổng:

Không có tràn bộ đệm. Truy cập slice ngoài biên là panic, không phải ghi đè bộ nhớ. Đây là khác biệt lớn nhất so với C.

html/template tự thoát ký tự theo ngữ cảnh:

t := template.Must(template.New("x").Parse(`<div>{{.}}</div>`))
t.Execute(w, "<script>alert(1)</script>")

Nó biết đang ở trong HTML, trong thuộc tính, trong JavaScript hay trong URL, và thoát cho đúng. Dùng html/template cho HTML — không dùng text/template, cái đó không thoát gì cả.

Nhưng nó không cứu được nếu bạn tự ghép chuỗi HTML rồi dùng template.HTML để bỏ qua kiểm tra.

Đua dữ liệu bắt được bằng -race — bài 36.

Ba thứ Go không giúp

Đọc quá phần tử của slice trong chỉ số tự tính vẫn panic lúc chạy — chết dịch vụ nếu không recover.

Tràn số im lặng — bài 5 đã đo: int8(300) cho 44 mà không cảnh báo. Với dữ liệu từ ngoài vào kiểu hẹp, phải kiểm biên tường minh.

Bí mật trong log. Bài 57 đã nói: cài slog.LogValuer để kiểu tự che.

Ba việc làm ngay

govulncheck ./...            # lỗ hổng gọi tới được
go vet ./...                 # lỗi mà trình biên dịch không bắt
gosec ./...                  # mẫu mã không an toàn

Cộng Dependabot hoặc Renovate để nâng cấp tự động — bài 99 sê-ri Java đã đo: 66 trong 73 lỗ hổng chỉ cần nâng phiên bản.

Thử ba mươi giây

go install golang.org/x/vuln/cmd/govulncheck@latest && govulncheck ./...

Ba dòng số ở cuối cho bạn biết ngay mức độ khẩn: con số thứ ba khác 0 là phải xử lý hôm nay; hai con số đầu là danh sách nâng cấp dần.

Ngày mai là bài cuối: nhìn lại sáu mươi bài và bản đồ đi tiếp.