问题
我想为一些DBI
函数编写一个包装程序,以允许安全执行参数化查询。我发现this resource可以解释如何使用glue
包将参数插入SQL查询中。但是,似乎有两种不同的方法可以使用glue
包来插入参数:
?
,然后随后使用dbBind
来填充它们。示例来自上面的链接:library(glue)
library(DBI)
airport_sql <- glue_sql("SELECT * FROM airports WHERE faa = ?")
airport <- dbSendQuery(con, airport_sql)
dbBind(airport, list("GPT"))
dbFetch(airport)
glue_sql
或glue_data_sql
自行填写参数(不使用dbBind
)。还是来自上面链接的示例:airport_sql <-
glue_sql(
"SELECT * FROM airports WHERE faa IN ({airports*})",
airports = c("GPT", "MSY"),
.con = con
)
airport <- dbSendQuery(con, airport_sql)
dbFetch(airport)
我更喜欢使用第二种方法,因为它具有很多额外的功能,例如折叠sql语句的in
子句中的where
语句的多个值。有关其工作原理,请参见上面的第二个示例(请注意表明该参数必须折叠的参数后的*
)。问题是:这样可以防止SQL注入吗? (我还有其他需要担心的事情吗?)
我的代码
这是我目前用于包装程序的代码。
paramQueryWrapper <- function(
sql,
params = NULL,
dsn = standard_dsn,
login = user_login,
pw = user_pw
){
if(missing(sql) || length(sql) != 1 || !is.character(sql)){
stop("Please provide sql as a character vector of length 1.")
}
if(!is.null(params)){
if(!is.list(params)) stop("params must be a (named) list (or NULL).")
if(length(params) < 1) stop("params must be either NULL, or contain at least one element.")
if(is.null(names(params)) || any(names(params) == "")) stop("All elements in params must be named.")
}
con <- DBI::dbConnect(
odbc::odbc(),
dsn = dsn,
UID = login,
PWD = pw
)
on.exit(DBI::dbDisconnect(con), add = TRUE)
# Replace params with corresponding values and execute query
sql <- glue::glue_data_sql(.x = params, sql, .con = con)
query <- DBI::dbSendQuery(conn = con, sql)
on.exit(DBI::dbClearResult(query), add = TRUE, after = FALSE)
return(tibble::as_tibble(DBI::dbFetch(query)))
}
我的问题
这样可以防止SQL注入吗?尤其是因为我没有使用dbBind
。
结语
我知道已经存在一个名为dbGetQuery
的包装程序,该包装程序允许使用参数(有关更多信息,请参见this question-有关参数化查询的示例,请@krlmlr查找答案)。但这再次依赖于第一种使用?
的方法,该方法在功能上更为基本。