这样可以安全地防止SQL注入吗?

时间:2018-03-24 20:03:44

标签: oracle sql-injection

以下表单是否可以安全地防止SQL注入?我必须执行此操作,因为p_select_statement将是一个简单的select * from table where lastModifiedTime > :p_asof and <conditions>,但调用getByFilter的代码希望列的顺序为col1, col2, col3select * }可能会返回col2, col3, col1

OPEN CURSOR <dynamic_select_statement> USING <bind variable>

示例...

PROCEDURE getByFilter (
    p_select_statement IN VARCHAR2,
    p_asof IN TIMESTAMP,
    p_cur OUT SYS_REFCURSOR
) AS
    full_select_statement VARCHAR2(32767);
BEGIN
    full_select_statement := 'SELECT
        col1,
        col2,
        col3
    FROM (' || p_select_statement || ')';
    OPEN p_cur FOR full_select_statement USING p_asof;
END getByFilter;

2 个答案:

答案 0 :(得分:1)

不,如果可以动态设置条件,则不行:

VARIABLE cur REFCURSOR;

DECLARE
  conditions VARCHAR2(4000)
    := '1 = 0 UNION ALL '
     || 'select username AS col1, password AS col2, other_column AS col3 '
     || 'FROM your_secret_password_table';
  sql        VARCHAR2(4000)
    := 'select * from table where lastModifiedTime > :p_asof and ' || conditions;
BEGIN
  PROCEDURE getByFilter (
    p_select_statement => sql,
    p_asof             => SYSTIMESTAMP,
    p_cur              => :curr
  );
END;
/

PRINT cur;

答案 1 :(得分:0)

Yes, your example is clearly an example of SQL injection. It counts as SQL injection if your procedure executes some input verbatim as a query (or part of a query, as in this case).

To answer your question about whether unsafe input could do damage, I can think of two possibilities:

  • The SELECT passed as an argument to the procedure could be designed to be very resource-intensive. For example a query with a large cartesian join:

    SELECT * FROM AnyTable
    CROSS JOIN AnyTable
    CROSS JOIN AnyTable
    CROSS JOIN AnyTable
    CROSS JOIN AnyTable
    ORDER BY 1;
    

    Generates a result set of a few trillion rows, and attempts to sort it. That could be a denial-of-service attack.

  • The SELECT includes calling a stored function that modifies data.

    SELECT SomeFunctionThatDeletes() FROM AnyTable;
    

Whether your procedure is unsafe depends on whether the calling code allows untrusted input to become the argument to your procedure.

Suppose you had some safeguard like this (written in PHP, but the same applies to any language):

$user_option = $_GET['option'];
$queries = [
  1 => 'select * from table where lastModifiedTime > :p_asof and <conditions>',
  2 => 'select * from table where lastModifiedTime < :p_asof and <conditions>'
];

$stmt = $db->prepare('call getByFilter(?)');
$stmt->execute([$queries[$user_option]);

Now the user can pick with an input of '1' or '2' which query to run, but they have to pick one or the other of these hard-coded queries. They can't do anything mischievous like change the SQL query that is run.

That's a type of whitelisting. In other words, your application code must limit the choices so that the user's input can pick only safe choices. The user input should never be allowed to be executed as SQL directly without some kind of whitelisting like this.

You could also implement the whitelisting inside your procedure.