如何在不添加不必要的引号的情况下清理闪亮应用程序中的用户 SQL 输入?

问题描述 投票:0回答:1

我正在用 R 构建一个闪亮的应用程序,用户可以通过它编辑 SQL 数据库的内容。也就是说,我们的应用程序包含自由文本字段,每当修改用户对这些字段的输入时,底层数据库也会修改。当然,我们担心 SQL 注入,并愿意采取措施帮助防范它。

目前,服务器代码如下所示。我们正在使用

DBI
包来连接数据库。

update_query <- paste0("UPDATE ", select_var$table_name, " SET ", select_var$var_name, " = ", insert,  " WHERE ", select_id, " = '", select_value, "'")
query <- dbSendStatement(con, update_query)
dbClearResult(query)

查询的五个动态组件中有四个是从代码本身生成的,不需要清理,但

insert
是用户提供的自由格式文本。

为了帮助防止 SQL 注入,我们将

insert
包裹在
dbQuoteString
中。我知道简单地引用输入并不能完全防止注入,但这不是重点。所以
dbQuoteString
有效,除非用户提供的输入以引号开始和/或结束。例如:

insert <- "'a'"
update_query <- paste0("UPDATE ", select_var$table_name, " SET ", select_var$var_name, " = ", dbQuoteString(insert),  " WHERE ", select_id, " = '", select_value, "'")
query <- dbSendStatement(con, update_query)
dbClearResult(query)

这成功插入了转义字符串,但是当表格再次加载到 R 的内存中时,相关单元格的值现在是

"'''a'''"
(注意添加的引号),而不是
"'a'"
。因此,当用户重新启动应用程序并需要编辑该输入时,他必须在进行所需的编辑之前删除多余的字符。有什么方法可以确保转义用户输入实际上不会更改内容本身?

dbQuoteString
的文档指出“当将返回的对象作为
dbQuoteString()
参数再次传递给
x
时,它原封不动地返回”。然而,如果结果被强制转换为字符(类似于再次将表读入 R 的内存时发生的情况),它会改变:

> dbQuoteString(con, "'a'")
<SQL> '''a'''

> dbQuoteString(con, dbQuoteString(con, "'a'"))
<SQL> '''a'''

> dbQuoteString(con, as.character(dbQuoteString(con, "'a'")))
<SQL> '''''''a'''''''

如何避免这些额外的引号同时实施some SQL 注入保护?

sql r shiny sql-injection r-dbi
1个回答
1
投票

而不是使用

dbQuoteString
,你可能想使用
dbBind
https://www.rdocumentation.org/packages/DBI/versions/0.5-1/topics/dbBind)。

类似的东西:

insert <- "'a'"
update_query <- paste0("UPDATE ", select_var$table_name, " SET ", select_var$var_name, " = ?",  " WHERE ", select_id, " = '", select_value, "'")
query <- dbSendStatement(con, update_query)
dbBind(query, list(insert))
dbClearResult(query)

dbBind(query, list(insert))
表示将只是问号的参数替换为
insert
中的值并对其进行适当的消毒。

或者,正如@markalex 所指出的,您可以将参数放在

dbSendStatement
中。像这样的东西:

insert <- "'a'"
update_query <- paste0("UPDATE ", select_var$table_name, " SET ", select_var$var_name, " = ?",  " WHERE ", select_id, " = '", select_value, "'")
query <- dbSendStatement(con, update_query, params = list(insert))
dbClearResult(query)

大多数(所有?)现代编程语言都有一种像这样参数化查询的方法。

与简单地手动转义值相比,这有一些优势。如果您根本不在查询中输入任何用户输入,那么您不太可能因为不正确地执行某个操作而犯错。除了引号之外,它可能还会进行其他巧妙的转义(您不必担心这一点)。它允许您将字符串输入和非字符串输入视为相同(您根本不需要在参数周围写引号)。您的数据库可能能够更好地优化这些查询(因为至少对于某些驱动程序,它将分别传递查询和参数,因此数据库可以避免重新计算执行计划)。

© www.soinside.com 2019 - 2024. All rights reserved.