此检查是否避免 SQL 注入?

Does this check avoid SQL injections?

我需要将 where 条件从我的客户端传递到 web 服务,它在 oracle 数据库上执行 SQL 查询。

 const res = await db.execute(`SELECT count(ID) FROM table WHERE ${where}`);

由于 where 条件可以是完全动态的,我不能使用准备语句、存储过程或参数。

通过以下检查仅 运行 where 条件就足够了吗?

static isDangerousSql(sql: string): boolean {
    const characters = [ '--', ';'];

    for (const char of characters) {
        if (sql.indexOf(char) > -1) {
            return true;
        }
    }

    return false;
}

不,这还不够。

如果${where}是:

EXISTS (
  SELECT 1
  FROM   security_table
  WHERE  userid        = 1234
  AND    password_hash = CHR(65) || CHR(66) || CHR(67) || CHR(68)
)

然后它将:

  1. 通过检查;
  2. 确认有一个名为security_table的table;
  3. 确认 table 包含 useridpassword_hash 列;和
  4. 确认存在 ID 为 1234 且密码哈希为 ABCD
  5. 的用户

这可用于映射整个数据库结构并检查是否存在任何数据。绝对是一个漏洞。


An example would be

WHERE status = 'Active' and ID in (SELECT cell_id FROM alerts WHERE alert_status = 1)

而不是使用动态 SQL,您可以在查询中列出有效的过滤条件并使用绑定参数来填充过滤条件:

所以稍微复杂一点的例子是:

SELECT count(ID)
FROM   table t
WHERE  ( :status IS NULL OR status = :status )
AND    (  ( :alert_status IS NULL AND :other_status IS NULL )
       OR EXISTS (
            SELECT 1
            FROM   alerts a
            WHERE  t.id = a.cell_id
            AND    ( :alert_status IS NULL OR alert_status = :alert_status )
            AND    ( :other_status IS NULL OR other_status = :other_status )
       ) )

然后你有一个静态查询,可以传入(命名的)绑定变量 :status:alert_status:other_status.

  • 您的用户不需要了解底层 table 结构。
  • 代码不易受到 SQL 注入的攻击。
  • 您可以控制他们可以访问的数据。
  • 数据库可以缓存静态查询。

如果您不想使用包含所有参数的单个静态查询,那么您可以从中间层的固定代码段组件构建过滤器。

一些伪代码:

sql             = "SELECT count(ID) FROM table";
filters         = [];
bind_parameters = [];
if ( [ 'Active', 'Inactive' ].indexOf( user_input.status ) > -1 )
{
  filters.push( "status = ?" )
  bind_parameters.push( user_input.status );
}
if ( user_input.active_status == 0 || user_input.active_status == 1 )
{
  filters.push( "id IN ( SELECT cell_id FROM alerts WHERE alert_status = ?)" );
  bind_parameters.push( user_input.active_status );
}
if ( filters.length > 0 )
{
  sql = sql + " WHERE " + filters.join( " AND " );
}
db.setSQL( sql );
for ( i = 0; i < bind_parameters.length; i++ )
{
  db.setBindParameter( i, bind_parameters[i] );
}
const res = await db.executeQuery();

(上面的伪代码是为了表明从固定片段在中间层动态构建查询的概念,并没有考虑实现细节,例如设置绑定参数的数据类型和可能还有许多其他事情 - 谨慎使用这个想法并在将其投入生产之前验证您的实施的安全性。)