Bạn có thể tránh rủi ro SQL injection bằng cách truyền giá trị tham số SQL vào dưới dạng đối số của hàm trong package sql. Nhiều hàm trong package sql cung cấp tham số cho câu lệnh SQL và tham số cho các giá trị sẽ được dùng trong những tham số của câu lệnh đó (một số hàm khác thì cung cấp tham số cho prepared statement và các tham số của nó).
Mã trong ví dụ sau dùng ký hiệu ? làm placeholder cho tham số id, giá trị của nó được truyền vào dưới dạng đối số của hàm:
// Correct format for executing an SQL statement with parameters.
rows, err := db.Query("SELECT * FROM user WHERE id = ?", id)
Các hàm của package sql thực hiện thao tác cơ sở dữ liệu sẽ tạo ra prepared statement từ những đối số bạn cung cấp. Khi chạy, package sql biến câu lệnh SQL thành một prepared statement rồi gửi nó đi kèm với tham số, và tham số được tách riêng.
Lưu ý: Placeholder cho tham số thay đổi tuỳ theo DBMS và driver bạn đang dùng. Ví dụ, driver pq dành cho Postgres chấp nhận dạng placeholder như $1 thay cho ?.
Bạn có thể bị cám dỗ dùng một hàm của package fmt để ghép câu lệnh SQL thành chuỗi có sẵn tham số bên trong – như thế này:
// SECURITY RISK!
rows, err := db.Query(fmt.Sprintf("SELECT * FROM user WHERE id = %s", id))
Cách này không an toàn! Khi bạn làm vậy, Go ghép toàn bộ câu lệnh SQL, thay động từ định dạng %s bằng giá trị tham số, trước khi gửi trọn câu lệnh tới DBMS. Điều này tạo ra rủi ro SQL injection, vì phía gọi mã của bạn có thể gửi một đoạn SQL ngoài dự kiến vào làm đối số id. Đoạn đó có thể hoàn thiện câu lệnh SQL theo những cách không lường trước được và nguy hiểm cho ứng dụng của bạn.
Ví dụ, bằng cách truyền vào một giá trị %s nhất định, bạn có thể nhận được thứ gì đó như sau, và nó có thể trả về toàn bộ bản ghi người dùng trong cơ sở dữ liệu của bạn:
SELECT * FROM user WHERE id = 1 OR 1=1;