在 Laravel 4 中,我想保护一些复杂的数据库查询免遭 SQL 注入。这些查询结合使用查询生成器和 DB::raw()。这是一个简化的示例:
$field = 'email';
$user = DB::table('users')->select(DB::raw("$field as foo"))->whereId(1)->get();
我读过 Chris Fidao 的教程,可以通过使用准备好的语句将绑定数组传递给 select() 方法,从而正确防止 SQL 注入。例如:
$results = DB::select(DB::raw("SELECT :field FROM users WHERE id=1"),
['field' => $field]
));
这可行,但该示例将整个查询放入原始语句中。它没有将查询生成器与 DB::raw() 结合起来。当我使用第一个示例尝试类似的操作时:
$field = 'email';
$user = DB::table('users')->select(DB::raw("$field as foo"), ['field' => $field])
->whereId(1)->get();
...然后我得到一个错误: strtolower() 期望参数 1 为字符串,给定数组
对于将查询生成器与 DB::raw() 相结合的查询,防止 SQL 注入的正确方法是什么?
我发现查询生成器有一个名为 setBindings() 的方法,在这种情况下很有用:
$field = 'email';
$id = 1;
$user = DB::table('users')->select(DB::raw(":field as foo"))
->addSelect('email')
->whereId(DB::raw(":id"))
->setBindings(['field' => $field, 'id' => $id])
->get();
Eloquent 在底层使用 PDO 来消毒物品。它不会清理添加到 SELECT 语句中的项目。
但是,mysqli_real_escape_string方法对于清理 SQL 字符串仍然有用。
还考虑(或改为)从用户表中保留有效字段名称的数组,并对其进行检查以确保没有使用无效值。
$allowedFields = ['username', 'created_at'];
if( ! in_array($field, $allowedFields) )
{
throw new \Exception('Given field not allowed or invalid');
}
$user = DB::table('users')
->select(DB::raw("$field as foo"))
->whereId(1)->get();
除了能够将
setBindings()
作为独立函数与 DB::raw
一起使用之外,查询生成器的所有新“原始方法” - selectRaw
、whereRaw
、havingRaw
等 - 接受一组绑定作为他们的第二个参数。尽管在相关文档中没有明确提及,但我设法弄清楚这基本上意味着您需要用问号替换有价值的 SQL 变量,然后将这些变量的值放入数组中:
$user = DB::table('users')->selectWhere("? as permissions", [admin]))->get();
// This will replace ? with admin
这样做可以防止暴露变量,从而防止它们被 SQL 注入和操纵。