防止动态 sql 注入

Preventing dynamic sql from injection

提问人:MrGoodman 提问时间:5/30/2023 更新时间:5/30/2023 访问量:124

问:

我有一个在Oracle数据库中构建帐户搜索工具的助手。我以动态 sql 结束,但我知道存在 sql 注入的风险。然后,Java 进一步处理输出游标的输出(传递给 AccountResource 并输出给询问实体)。任何人都可以分享一些意见,邪恶的黑客如何在下面注入代码以及如何正确保护我的动态查询?这只是示例代码,实际过程将有更多的输入参数、% 替换等,但功能是 100% 捕获的。我已经在论坛上搜索了答案,但主要知识是避免动态查询。

他(邪恶的黑客)能以某种方式top_secret_column吗?他能以某种方式放弃my_important_table吗?

CREATE TABLE my_accounts (account_id NUMBER, user_name VARCHAR2(30),  email VARCHAR2(30), top_secret_column VARCHAR2(30));
--CREATE TABLE my_important_table(id number);

DECLARE
  -- input variables  
  p_cur SYS_REFCURSOR; -- OUT
  p_exact_search NUMBER := 1;
  p_search_email VARCHAR2(50) := '[email protected]';
  p_search_name VARCHAR2(50) := null;
  --
  v_sql VARCHAR2(4000);
  FUNCTION get_query_text(p_column       VARCHAR2,
                          p_value        VARCHAR2,
                          p_exact_search NUMBER) RETURN VARCHAR2 IS
    v_res VARCHAR2(200);
  BEGIN
    IF p_exact_search = 1
    THEN
      v_res := ' UPPER(' || p_column || ') = UPPER(''' || p_value || ''')';
    ELSE
      v_res := ' UPPER(' || p_column || ') LIKE (UPPER(''%' || p_value ||
               '%''))';
    END IF;
    RETURN chr(10) || ' AND' || v_res;
  END;
BEGIN
  v_sql := 'SELECT account_id, user_name, email
  FROM my_accounts
 WHERE 1 = 1';
  IF p_search_email IS NOT NULL
  THEN
    v_sql := v_sql || get_query_text('email', p_search_email, p_exact_search);
  END IF;
  IF p_search_name IS NOT NULL
  THEN
    v_sql := v_sql ||
             get_query_text('user_name', p_search_name, p_exact_search);
  END IF;
  --dbms_output.put_line(v_sql);
  OPEN p_cur FOR v_sql;
  CLOSE p_cur;
END;

我已经搜索了论坛,我希望我的代码是防邪恶的黑客。

Oracle SQL注入

评论

2赞 Boneist 5/30/2023
Paul W 的答案是你所需要的,但是如果将来你遇到绝对不能使用绑定变量而必须使用串联的情况(例如,因为你的查询引用了动态表名/列/其他标识符),内置的 DBMS_ASSERT 包中有一系列函数,可以检查输入是否满足标识符的要求。

答:

3赞 Paul W 5/30/2023 #1
  1. 如果您使用绑定变量而不是将参数拼接到 SQL 中,您将受到保护,不会注入脚本(以调用注入到字符串中的未知函数的形式,带有转义以退出引号),因为如果您将调用者将其作为绑定传入,则 Oracle 不会评估/执行调用者尝试嵌入到这些字符串中的任何函数调用。绑定发生在时间之前,但在 之后,因此无法在此时引入结构更改(如调用函数)。||execparse

    因此,更改代码以使用启用或禁用的 :p laceholder 和绑定(因此您不必更改绑定列表),而不是有条件地组装它们:

    OPEN p_cur FOR '
    SELECT account_id, user_name, email
      FROM my_accounts
     WHERE (email = :email OR :email IS NULL OR :exact_search = 0)
       AND (user_name= :user_name OR :user_name IS NULL OR :exact_search = 0)
       AND (UPPER(email) LIKE UPPER(''%''||:email||''%'') OR :email IS NULL OR :exact_search = 1)
       AND (UPPER(user_name) LIKE UPPER(''%''||:user_name||''%'') OR :user_name IS NULL OR :exact_search = 1)
       ' 
    USING IN p_search_email, p_search_email, p_exact_search, 
             p_search_name, p_search_name, p_exact_search,
             p_search_email, p_search_email, p_exact_search, 
             p_search_name, p_search_name, p_exact_search;
    

    这不仅比 更安全,而且更简洁、更简洁的代码,它还会表现得更好(通过避免重复的硬解析),并使您的 DBA 满意,因为您不会创建无数不可共享的游标并粉碎共享池。||

    注意:您会注意到我仍然有一些表达式可以处理,但这些表达式是字符串本身的内部表达式,并在绑定之后连接绑定变量,而不是解析时的字符串,因此不会像其他运算符那样容易受到攻击。||LIKE||

  2. 编译过程,以便在出现错误并且存在一些漏洞时,调用方将在自己的权限下操作,并且不会从使用过程的所有者权限中获得任何好处。当然,您需要向所有可能的调用方授予该过程执行其操作所需的任何单个对象权限 (AUTHID CURRENT_USERGRANT SELECT ON my_accounts TO .... )