12

I use shell script to communicate to a MySQL database. MySQL supports specifying query as a shell argument, like this:

mysql my_db -B -N -e "select id from Table"

However, if I have a parameter, which I'd like to use in a query, how can I get protection against injection attacks?

A naive way is to just paste variable value to the request, but it's not very secure:

mysql my_db -B -N -e "select id from Table where name='$PARAM'"

Are there any tricks or documented interfaces to make an injection-safe queries from command line?

P Shved
  • 96,026
  • 17
  • 121
  • 165
  • Is a web app passing these `params` to the shell script? I would suggest looking at cleaning the data in the web app. – Jakub Dec 15 '10 at 14:33

4 Answers4

8

You can base64 encode the value, and then base64 decode it once it's in MySQL. There are UDFs in MySQL for converting Base64 data to common data. Additionally, most systems either have uuencode, or the 'base64' command for base64 encoding data.

Sargun Dhillon
  • 1,788
  • 2
  • 18
  • 24
  • Great answer, example would help too: `ENCODED=$(echo $1 | base64); echo "SELECT * FROM T WHERE V=FROM_BASE64('$ENCODED');" | mysql` – brablc Oct 25 '16 at 09:33
  • I was wondering _why_ this works? Does `FROM_BASE64` escape the output? Is it because the result of `FROM_BASE64` is a binary string? Is it because the function is invoked in a phase in MySQL that is no longer vulnerable to SQL injection? – Stefan van den Akker Feb 08 '20 at 14:13
  • 1
    @StefanvandenAkker By base64-encoding the string, you have replaced its character set with the base64 character set, which is all upper and lowercase letters, numbers, +, /, and =. Since most SQL injection attacks rely on mishandling the input when creating an SQL statement, you've created a safe way to pass any input into mysql, which is then able to get the original text with FROM_BASE64, thus essentially eliminating the SQL injection attack surface. – Eric Reed Oct 08 '20 at 15:56
0

The answer by Sargun Dhillon pointed me to the right direction. Unfortunately is FROM_BASE64 not available before MySQL 5.6 so I went with UNHEX.

The script below is an example to query the details of a Redmine user from a shell. I still wouldn't sleep well if untrusted users had access to this script but it is safe enough in my case. (It is also limited to string values and you shouldn't have a question mark in your query but those limitations are fine with me.)

#!/bin/bash

MYSQL_OPTS='--defaults-file=/etc/redmine/mysql.cnf'

mysql_query() {
  local db=$1
  local sql=$2
  shift 2 || return 1
  declare -a args=("$@")

  sql=${sql//[%]/%%}
  sql=${sql//[?]/UNHEX('%s')}
  for ((i=0; i<${#args[@]}; i++)); do
    args[$i]=$(echo -n "${args[$i]}" | hexdump -v -e '/1 "%02X"')
  done
  sql=$(printf "$sql" "${args[@]}")
  mysql $MYSQL_OPTS "$db" -e "$sql" || return $?
}

for u in "$@"; do
  mysql_query redmine 'select * from users where login=?\G' "$u" 
done

If you discover any SQL or Shell injection I missed, please comment.

mss
  • 1,804
  • 1
  • 17
  • 18
0

Your application is succeptible to an SQL Injection attack any time you are construct SQL by concatenating parameters (as in your example). There is a writeup about this on wikipedia at this link: http://en.wikipedia.org/wiki/SQL_injection

I suspect that you will want to write a unix filter to construct your SQL query using the mysql_real_escape_string function mentioned in the article.

Consider passing the SQL as the first parameter and the variable(s) as the subsequent parameters then have it return the constructed SQL. If you name your filter "blobbo" the command like for your example might look like this:

blobbo "select id from Table where name=%s" $PARAM

DwB
  • 37,124
  • 11
  • 56
  • 82
0

You not only have to protect against SQL injection, but against shell injection as well. You may want to write the query (after sanitizing any dynamic parts) out to a file and then redirect that file into mysql, rather than hoping the query won't break the shell. Consider:

PARAM="name'\"; rm -rf / ; echo 'pwn3d U"

becoming

mysql my_db -B -N -e "select id from Table where name='name'"; rm -rf / ; echo 'pwn3d U'

or:

command 1: mysql my_db -B -N -e "select id from Table where name='name'"
command 2: rm -rf /
command 3: echo 'pwn3d U'

Instead, do something like:

 cat <<EOT > query.sql
 select .... blah blaah blah .... sanitized query here
 EOT
 mysql my_db -B -N < query.sql

This would prevent any user-specified data from ever appearing within the shell command itself, prevent at least one level of injection vulnerability. But then, you still have to handle the SQL injection problem.

Marc B
  • 356,200
  • 43
  • 426
  • 500
  • Thank you for mentioning this kind of issue. However, the code posted in the question *is* protected from shell injection—if it's executed by shell itself. Only if I invoke the command from another script (which I don't), like this: `system("mysql -e '$PARAM'");` it has this kind of vulnerability. – P Shved Dec 15 '10 at 14:53