Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sql injection - how to sanitize program generated sql clause?

in standard Ajax, where and order by SQL clauses are provided by the program (not user), eg

var url = ".select?dd=emp&where="+escape("emp_tp='abc' and hire_dt<current_date-'2 years' and super_emp_id is distinct from emp_id")

answered on the server by

$where = (isset($_GET['where'])) ? pureClause($_GET['where']) : null;
$order = (isset($_GET['order'])) ? pureClause($_GET['order']) : null;
...
$query = $query.(($where)?" where $where":'').(($order)?" order by $order":'');

the question is what should function pureClause look like?

right now pureClause simply raises error if any of the following exist:

; select insert update delete drop create truncate

if other injection causes query failure, that's fine, as long as data undamaged.

to me this seems adequate, but in my heart, I know I'm wrong.

Clarifications:

  • prepared statements in Postgres, although very fast, are a pain to set up and maintain - they're ok for well used queries but not custom queries.
  • creating a prepared statement for each transaction is a huge db hit. much preferred if security can be attained in at the app level.

Lastly, consider the where clause

emp_tp='abc' and hire_dt=current_dt-'2 years' and super_emp_id is distinct from emp_id

how many placeholders here? this needs to be parsed correctly before being fed into a prepared statement with placeholders, right? or am I completely missing the boat?


Primary facts:

  • not practical to write a SQL clause parser for parameterized prepared statements
  • not practical to write a SQL clause sanitizer that guarantees no harm

Solution:

for SELECTS, where the random SQL can be a problem: since it's too hard to protect the database, let the database protect itself! have different users have different roles / permissions. use a read-only user for selects. for normal SQL, this guarantees no DML from these statements.

best practices: four db user accesses

  1. developer, do everything (never use as connection in web app)
  2. dml - can select / dml on almost everything (must use for dml)
  3. read - can select (use for all selects, whether prepared or text)
  4. login - can only execute login/password functions (used in login process)

password protection:

  • dml and read may not access password data, either through select or dml
  • login should access password data only through protected functions, eg,
     function login( username, password ) - returns user_id
     function set_password( usr_id, password ) - sets password
  • only login may run the login() and set_password() functions
  • depending on your database, login may need sql access to password columns
  • depending on your database, the password column may be protected itself; if not, then should be moved out of the user table into its own secure table

setting this up in mysql, using the administrator tool, took about 30 minutes, including time to write the login functions and split out the password column.

like image 792
cc young Avatar asked Sep 03 '11 06:09

cc young


People also ask

What's a recommended way of dealing with SQL injection attacks?

The only sure way to prevent SQL Injection attacks is input validation and parametrized queries including prepared statements. The application code should never use the input directly. The developer must sanitize all input, not only web form inputs such as login forms.

How do prepared statements protect against SQL injection?

Wikipedia says: Prepared statements are resilient against SQL injection, because parameter values, which are transmitted later using a different protocol, need not be correctly escaped. If the original statement template is not derived from external input, SQL injection cannot occur.

What is SQL input sanitization?

Data sanitization means that you remove all dangerous characters from an input string before passing it to the SQL engine. This is not the best defense against SQL injection, it is better to use prepared statements and never create SQL statements but string + operations.


2 Answers

What you are doing is the very definition of sql injection and cannot be sanitized. You cannot pass in a WHERE clause in a safe fashion period end of story. You must build this part of the query on the server side. The fact that you didn't recognize this means you MUST read more about sql injection, clearly asking StackOverflow is an insecure approach to this problem. The fear is that you may never learn the fundamentals of this vulnerability.

$order can be done in a secure way with a white list. For example:

if(in_array($_GET['order'],$list_of_rows)){
   $order=$_GET['order'];
}

If you are passing in a table name or column name make sure you check it against a white list, or this will be sql injection.

like image 133
rook Avatar answered Oct 27 '22 01:10

rook


Got it! Routing all of these queries through a database user (connection) who has only been granted SELECT privileges on the database!

Attempted DML will choke. This does not prevent DoS attacks (lots of ways to do that!), but does protect the data. Nor does the make for secure queries, like login. But for client generated WHERE and ORDER, with the goal of preventing DML, this should work just fine.

Ten/fifteen years ago always set up different users for different roles, but with app layer etc etc got out of the habit. It's probably a good idea to re-invest in those principles.

Unless hear differently will mark this as correct answer - it satisfies all criteria, howbeit it dodges the theoretically impossible challenge fo writing a sanitizer.

like image 37
cc young Avatar answered Oct 27 '22 03:10

cc young