此检查是否避免 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)
)
然后它将:
- 通过检查;
- 确认有一个名为
security_table
的table;
- 确认 table 包含
userid
和 password_hash
列;和
- 确认存在 ID 为
1234
且密码哈希为 ABCD
的用户
这可用于映射整个数据库结构并检查是否存在任何数据。绝对是一个漏洞。
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();
(上面的伪代码是为了表明从固定片段在中间层动态构建查询的概念,并没有考虑实现细节,例如设置绑定参数的数据类型和可能还有许多其他事情 - 谨慎使用这个想法并在将其投入生产之前验证您的实施的安全性。)
我需要将 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)
)
然后它将:
- 通过检查;
- 确认有一个名为
security_table
的table; - 确认 table 包含
userid
和password_hash
列;和 - 确认存在 ID 为
1234
且密码哈希为ABCD
的用户
这可用于映射整个数据库结构并检查是否存在任何数据。绝对是一个漏洞。
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();
(上面的伪代码是为了表明从固定片段在中间层动态构建查询的概念,并没有考虑实现细节,例如设置绑定参数的数据类型和可能还有许多其他事情 - 谨慎使用这个想法并在将其投入生产之前验证您的实施的安全性。)