Delphi - prevent against SQL injection

一个人想着一个人 提交于 2019-11-28 07:18:44

Safe

query.SQL.Text := 'select * from table_name where name=:Name';

This code is safe because you are using parameters.
Parameters are always safe from SQL-injection.

Unsafe

var Username: string;
...
query.SQL.Text := 'select * from table_name where name='+ UserName;

Is unsafe because Username could be name; Drop table_name; Resulting in the following query being executed.

select * from table_name where name=name; Drop table_name;

Also Unsafe

var Username: string;
...
query.SQL.Text := 'select * from table_name where name='''+ UserName+'''';

Because it if username is ' or (1=1); Drop Table_name; -- It will result in the following query:

select * from table_name where name='' or (1=1); Drop Table_name; -- '

But this code is safe

var id: integer;
...
query.SQL.Text := 'select * from table_name where id='+IntToStr(id);

Because IntToStr() will only accept integers so no SQL code can be injected into the query string this way, only numbers (which is exactly what you want and thus allowed)

But I want to do stuff that can't be done with parameters

Parameters can only be used for values. They cannot replace field names or table names. So if you want to execute this query

query:= 'SELECT * FROM :dynamic_table '; {doesn't work}
query:= 'SELECT * FROM '+tableName;      {works, but is unsafe}

The first query fails because you cannot use parameters for table or field names.
The second query is unsafe but is the only way this this can be done.
How to you stay safe?

You have to check the string tablename against a list of approved names.

Const
  ApprovedTables: array[0..1] of string = ('table1','table2');

procedure DoQuery(tablename: string);
var
  i: integer;
  Approved: boolean;
  query: string;
begin
  Approved:= false;
  for i:= lo(ApprovedTables) to hi(ApprovedTables) do begin
    Approved:= Approved or (lowercase(tablename) = ApprovedTables[i]);
  end; {for i}
  if not Approved then exit;
  query:= 'SELECT * FROM '+tablename;
  ...

That's the only way to do this, that I know of.

BTW Your original code has an error:

query.SQL.Text := 'select * from table_name where name=:Name where id=:ID'; 

Should be

query.SQL.Text := 'select * from table_name where name=:Name and id=:ID'; 

You cannot have two where's in one (sub)query

If you allow the user to influence only the value of parameters that will be bound into an sql command text with placeholders, then you don't really need to inspect what the user enters: the simplest way to avoid SQL injection, as you mention, is to avoid concatenated SQL, and using bound variables (or calling procedures) does this (it also has the advantage - mileage/relevance depends on the database - of allowing the engine to re-use query plans).

If you are using Oracle, then you need to have a really good reason for not using bound variables: Tom Kyte has a ton of good information about this on his site http://asktom.oracle.com. Just enter "bound variables" in the search box.

This will assure me a minimum of security level?

Yes parametrized queries should protect you from SQL injection which would be easy to test. Simply input some dangerous string in the name variable and see what happens. Normally you should get 0 rows returned and not an error.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!