我有一些与使用函数时的“潜在安全漏洞”相关的问题/疑虑,如 SQLite 库的 Tcl 接口文档中所述:https://www.sqlite.org/tclsqlite.html#function.
-仅直接
此选项限制函数只能由直接的顶级 SQL 语句使用。触发器、视图、CHECK 约束、生成列或索引表达式无法访问该函数。建议所有应用程序定义的 SQL 函数使用此选项,并且强烈建议任何具有副作用或揭示应用程序内部状态的 SQL 函数。
安全警告:如果没有此开关,攻击者可能能够更改数据库文件的架构,以将新函数包含在触发器或视图或 CHECK 约束中,从而欺骗应用程序使用攻击者选择的参数运行该函数。因此,如果新函数有副作用或揭示了应用程序的内部状态并且未使用 -directonly 选项,则这是一个潜在的安全漏洞。
我无法理解此漏洞是如何被利用的;这可能是由于我的担忧/期望受到我想要完成的任务的影响(问题稍后会详细说明)。
对其工作原理的全面描述或如何利用它的代码示例将对我非常有帮助。
解释所呈现的风险也很有用:
rm -rf
的函数,则此漏洞可能会执行此操作。但是,如果该函数不执行此操作并且不操作数据库,则第 1 点和第 2 点是否仍然存在此漏洞的风险?
关于第3点,我不明白它如何允许获取不应该可用的数据?在我看来,该漏洞依赖于使用恶意行为者提供的数据库文件的应用程序,该应用程序使用触发器来运行通常不应由不受信任的行为者运行的函数;这是正确的吗?
那么,这个漏洞是否可以通过以下方法缓解?
关于我的具体用例: 我正在尝试为游戏创建一个灵活的模组系统;数据库将遵循实体-属性-值模型。
我认为如果模组制作者能够定义属性的约束将会很有帮助。为此,我想出了以下方法:
这是我想做的一个例子:
#!/bin/sh
# This line continues for Tcl, but is a single line for 'sh' \
exec tclsh "$0" ${1+"$@"}
package require sqlite3
proc delegate {id type value} {
set proc_name "is_valid_$type"
# If no proc has been defined to delegate to, automatically succeed.
if {[safe_interp eval "info proc $proc_name"] eq ""} {
return
}
set failed [catch {
safe_interp eval "$proc_name $id $type $value"
} error_msg]
if {$failed} {
puts "delegate to: $proc_name\
\nwith args:\
\n id: $id\
\n type: $type\
\n value: $value\
\nfailed with error msg: $error_msg"
}
# Used by the trigger to determine whether to rollback.
return [expr {!$failed}]
}
proc insert_attribute {type value} {
db eval {
insert into attribute (type, value) values(:type, :value);
}
}
interp create -safe safe_interp
interp alias safe_interp insert_attribute {} insert_attribute
safe_interp eval {
proc is_valid_t1 {id type value} {
error "error: is invalid for some reason I guess?"
}
proc is_valid_t2 {id type value} {}
}
sqlite3 db -create true ""
db function delegate delegate
db eval {
create table attribute(
id integer primary key,
type varchar not null,
value varchar not null
);
}
db eval {
create trigger delegate_on_attribute_insert
after insert on attribute
begin
select case (select delegate(new.id, new.type, new.value))
when false then
raise(rollback, 'delegate failed, rolling back')
end;
end;
}
# Fails because attribute type t1 has a proc defined
# that checks for validity, and that proc is defined to always
# give an error.
if {[catch {safe_interp eval insert_attribute t1 v1} msg]} {
puts "msg: $msg"
}
# Succeeds because attribute type t2's validity-checking proc
# always succeeds.
if {[catch {safe_interp eval insert_attribute t2 v1} msg]} {
puts "msg: $msg"
}
# Succeeds because attribute type t3's has
# no validity-checking proc so nothing makes it fail.
if {[catch {safe_interp eval insert_attribute t3 v1} msg]} {
puts "msg: $msg"
}
puts "attributes: [db eval {
select * from attribute
}]"
这给出了输出:
delegate to: is_valid_t1
with args:
id: 1
type: t1
value: v1
failed with error msg: error: is invalid for some reason I guess?
msg: delegate failed, rolling back
attributes: 1 t2 v1 2 t3 v1
在此用例中,使用 trust_schema = on 或将委托函数标记为无害运行有什么危险?
该漏洞来自于您可能定义了允许数据库操纵其自身外部世界的函数(例如,通过发送电子邮件)的可能性。当与特定的触发器结合使用时,这是非常强大和有用的,但是如果攻击者可以在程序之外操纵数据库,它们可能会导致这些函数以您意想不到的方式被调用。 这并不意味着这肯定会发生! 部署方案可以轻松阻止这种情况,也许可以通过确保其他人无法在计算机上运行其代码,或者只有授权用户可以写入数据库,但 SQLite 引擎不会这样做。不知道这一点,所以谨慎是正确的。
需要注意的是,默认情况下,架构中的位置(数据库中存储 SQL 的位置)无法运行危险函数。这就是触发程序、检查约束、视图定义等。将函数标记为直接函数只会取消它在这些情况下运行的资格;它必须直接位于程序中的 SQL 代码中。 (如果您确实想在触发器程序中使用改变世界的函数,请不要将它们标记为仅直接函数,因为无论如何它们都必须在每个连接中重新注册。)