Rust语言:基于变量生命周期进行编译时SQL注入防止
Posted 虫虫搜奇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust语言:基于变量生命周期进行编译时SQL注入防止相关的知识,希望对你有一定的参考价值。
自打Web流行以后,面向用户终端的应用程序和数据库开始结合,成了动态网站的开发标准架构。但是,这就带来了一个可怕的漏洞—SQL注入。
在世界最具危险的漏洞排行榜中,SQL常年占据榜首,是最具危险的漏洞。
为了应对SQL注入的威胁,业界提供了占位符和SQL连接类库的prepared语句。这种方式,将可变数据从实际查询中分离出来,确保两者被混合。几乎所有现代SQL客户端都支持这个功能,当然,还是可以通过sql语句字混合可变的数据。
为了展示编译时程序验证的做法,虫虫在此通过Rust语言实例来说明下如何通过Rust特有的生命周期功能来检查SQL注入漏洞问题。
开始
我们首先创建一个执行SQL查询的函数。它需要一个SQL查询和参数值列表以用于占位符:
fn sql_query(query: &str, params: &[&SQLParam]) -> SQLRows {
// ...
}
为了引入SQL注入漏洞,我们可以动态构建一个format!宏SQL的查询,Rust的sprintf版本,如下所示:
fn get_user_by_name(username: &str) {
let query = format!("SELECT * FROM users WHERE username={}", username);
let _rows = sql_query(&query, &[]);
// ...
}
这个函数有问题。攻击者可以提交利用函数中SQL查询交互的用户名,在其中注入额外的代码。代码可以正常的编译,只有通过代码审计才能发现漏洞。
那么,如何让我们在编译就能检测到这个问题呢?
生命周期
可能你还不熟悉Rust语言,这个语言有一个生命周期的概念。这些是编译时必须满足的特殊条件。用它们来防止内存在被引用时被释放掉。生命周期通常局限于所引用的变量的词法作用域。
我们的sql_query的查询参数是对实际字符串的引用,所以为了确保引用的内存还在被引用的时候不会被释放掉,所以有了生命周期。生命周期默认由编译器自动推断。但是我们可以自己设置生命周期,比如sql_query函数:
fn sql_query<'a>(query: &'a str) {
// ...
}
函数的生命周期必须在可以其使用之前声明。这就是为什么"'a"出现两次的原因。一次在查询类型中,还有函数名称之后的<'a>的声明中。
'static生命周期
我要去哪里?有一个特殊的"'"静态生命周期值,它可以超过程序的生命周期。
我们可以利用这个特性。如果我们将查询的生命周期更改为'static,那么它将只接受与程序同样生命周期长的字符串:
fn sql_query(query: &'static str) {
// ...
}
这会导致动态字符串不再是有效的参数,因为它们是动态构建的,因此永远不满足'static条件。
如果我们试图编译之前错误的示例代码,我们会发现它会报错:
error[E0597]: `query` does not live long enough
--> src/main.rs:6:28
|
6 | let _rows = sql_query(&query, &[]);
| ^^^^^ borrowed value does not live long enough
7 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
这样,我们现在无需运行程序就可以成功预防SQL注入漏洞!
要修复这个问题,我们必须提供一个静态字符串并使用占位符?为用户名。
let _rows = sql_query("SELECT * FROM users WHERE username=?", &[username]);
因为查询字符串现在是一个字符串字面值,所以它满足'static生命周期,允许程序编译。
结论
Rust是一种具有许多有趣功能的语言,它们可以帮助程序员编写可以静态正确性的检查代码。这也给了它学习起来有点烧脑,但是一旦你掌握了这们神奇的语言,你也就不太可能再注入犯SQL注入这样的错误了。
我们在这里通过一个实例证明了静态程序分析的强大和用途,以及它如何帮助码农们写出更好更安全的软件。
以上是关于Rust语言:基于变量生命周期进行编译时SQL注入防止的主要内容,如果未能解决你的问题,请参考以下文章