What NobbZ is saying is never build a query with the parameter values built into the string. Your select statement should look like this:
selectStmt := `SELECT job_id, job_name FROM job WHERE job_id = ?;`
Assuming you’re using the database/sql
package and you already have a *sql.DB
created from sql.Open
(or sql.OpenDB
, etc.), you then execute your SQL like this:
func getJobIDAndName(ctx context.Context, db *sql.DB, id string) (id, name string, error) {
const selectStmt = `SELECT job_id, job_name FROM job WHERE job_id = ?;`
var jobName string
if err := db.QueryRowContext(ctx, selectStmt, id).Scan(&id, &jobName); err != nil {
return "", "", err
}
return id, jobName, nil
}
Separating the parameter values from the actual select statement ensures the values are handled as values by the driver and database engine.
The worst way you could do this is with fmt.Sprintf
. Here’s how to implement getJobIDAndName
in a way that could get you demoted, fired, sued for criminal negligence, etc.:
func prayWeGetJobIDAndName(ctx context.Context, db *sql.DB, id string) (id, name string, error) {
const selectStmt = `SELECT job_id, job_name FROM job WHERE job_id = '%s';`
var jobName string
if err := db.QueryRowContext(ctx, fmt.Sprintf(selectStmt, id)).Scan(&id, &jobName); err != nil {
return "", "", err
}
return id, jobName, nil
}
If you used this version, if someone calls it like this (at least on MS SQL. I don’t know any other syntax):
id, name, err := prayWeGetJobIDAndName("'; drop table job; --")
Then fmt.Sprintf
would turn your query into:
SELECT job_id, job_name FROM job WHERE job_id = ''; drop table job; --';
Then your job table could be gone.
Of course, nobody would put that in code. What’s more likely is you build a web service where you expect someone to issue an HTTP GET request for a job ID and you use that ID to query your job table. They could use this malicious “job ID” string to destroy your data.
If you use the first getJobIDAndName
function implementation, and try to pass "'; drop table job; --"
as the job ID, it would be executed on the database server as (again, I’m using MS SQL syntax):
SELECT job_id, job_name FROM job WHERE job_id = ' ''; drop table job; --';
This query has its single quotes escaped so that the whole job_id
parameter is treated as just one parameter. The query would then come back with no results found.
TL;DR: NEVER NEVER NEVER use fmt.Sprintf to fill in SQL query parameters.