Avoiding SQL injection risk
You can avoid an SQL injection risk by providing SQL parameter values as sql
package function arguments. Many functions in the sql
package provide
parameters for the SQL statement and for values to be used in that statement’s
parameters (others provide a parameter for a prepared statement and parameters).
Code in the following example uses the ?
symbol as a placeholder for the
id
parameter, which is provided as a function argument:
// Correct format for executing an SQL statement with parameters.
rows, err := db.Query("SELECT * FROM user WHERE id = ?", id)
sql
package functions that perform database operations create prepared
statements from the arguments you supply. At run time, the sql
package turns
the SQL statement into a prepared statement and sends it along with the
parameter, which is separate.
Note: Parameter placeholders vary depending on the DBMS and driver
you’re using. For example, pq driver
for Postgres accepts a placeholder form such as $1
instead of ?
.
You might be tempted to use a function from the fmt
package to assemble the
SQL statement as a string with parameters included – like this:
// SECURITY RISK!
rows, err := db.Query(fmt.Sprintf("SELECT * FROM user WHERE id = %s", id))
This is not secure! When you do this, Go assembles the entire SQL statement,
replacing the %s
format verb with the parameter value, before sending the
full statement to the DBMS. This poses an
SQL injection risk because the
code’s caller could send an unexpected SQL snippet as the id
argument. That
snippet could complete the SQL statement in unpredictable ways that are
dangerous to your application.
For example, by passing a certain %s
value, you might end up with something
like the following, which could return all user records in your database:
SELECT * FROM user WHERE id = 1 OR 1=1;