提问人:Ike348 提问时间:3/23/2023 最后编辑:Ike348 更新时间:3/23/2023 访问量:177
如何在不添加不必要的引号的情况下清理闪亮应用程序中的用户 SQL 输入?
How do I sanitize user SQL input in a shiny app without adding unnecessary quotation marks?
问:
我正在用 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'"
文档指出,“当再次将返回的对象传递给 as 参数时,它将原封不动地返回”。然而,如果结果被强制为字符(类似于再次将表读入 R 内存时发生的情况),则它会发生变化:dbQuoteString
dbQuoteString()
x
> dbQuoteString(con, "'a'")
<SQL> '''a'''
> dbQuoteString(con, dbQuoteString(con, "'a'"))
<SQL> '''a'''
> dbQuoteString(con, as.character(dbQuoteString(con, "'a'")))
<SQL> '''''''a'''''''
如何避免这些额外的引号,同时实现一些针对 SQL 注入的保护?
编辑:我应该在原始问题中提到这一点,在收到评论和答案后,我应该注意到参数化查询也会导致相同的行为。
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)
答:
您可能希望使用 (https://www.rdocumentation.org/packages/DBI/versions/0.5-1/topics/dbBind),而不是使用 。dbQuoteString
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)
大多数(所有?)现代编程语言都会有一种像这样参数化查询的方法。
与简单地手动转义值相比,这有一些优点。如果您从未在查询中放置任何用户输入,那么您不太可能因错误地执行错误而犯错误。除了引号之外,它可能会做其他聪明的转义(你不必担心这一点)。它允许您将字符串输入和非字符串输入视为相同(您根本不需要在参数周围写引号)。您的数据库可能能够更好地优化这些查询(因为至少对于某些驱动程序,它将分别传递查询和参数,因此数据库可以避免重新计算执行计划)。
评论
dbExecute
dbGetQuery
dbClearResult
DBI
dbSendStatement
dbClearResult
dbExecute
dbGetQuery
?
paste0("UPDATE ", select_var$table_name, " SET ", select_var$var_name, " = ? WHERE ", select_id, " = ?")
dbExecute(con, update_query, params=list(select_var$var_name, select_value))
评论
dbSendStatement
?
?
params
paste
insert
where
dbReadTable(con, table_name) %>% as.data.table()
dbQuoteString