什么是sql注入?
服务端没有对用户提交的参数进行严格的过滤,导致可以将SQL语句插入到可控参数中,改变原有的SQL语义结构,从而执行攻击者所预期的结果。
sql注入的探测
判断数据库类型
端口
报错信息
一些中间件常用的数据库
PHP MySQL
ASP SQL Server
ASPX SQL Server
JSP MySQL Oracle
寻找SQL注入点
寻找与数据库交互的可控参数
GET
POST
COOKIE
HTTP头
确定注入点
确定注入点的核心思想就是判断插入的数据是否被当做SQL语句执行。可以使用简单的算术运算来测试。
SQL语句预编译和绑定变量
使用足够严格的过滤和安全防御
Web应用向数据库传递语句模板
数据库对模板进行编译,编译以后语义将不会改变
变量绑定,Web应用向数据库传递变量,变量只会被当做数据识别,不会被作为语义结构识别
执行SQL语句
SQL注入的核心:数据和代码的混淆。
什么是PDO?
PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口。
PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。
PDO是php中最典型的预编译查询方式。
PDO场景下的SQL注入
PDO与安全相关的问题主要的设置有下面三项:
PDO::ATTR_EMULATE_PREPARES # 模拟预编译
PDO::ATTR_ERRMODE # 报错
PDO::MYSQL_ATTR_MULTI_STATEMENTS # 多语句执行
第一项为模拟预编译,如果为False,则不存在SQL注入;如果为True,则PDO并非真正的预编译,而是将输入统一转化为字符型,并转义特殊字符。这样如果是gbk编码则存在宽字节注入。
第二项而报错,如果设置为True,可能会泄露一些信息。
第三项为多语句执行,如果设置为True,且第一项也为True,则会存在宽字节+堆叠注入的双重漏洞。
对于此类问题的防范,主要有以下三个方面:
合理、安全的使用gbk编码。即使采用PDO预编译的方式,如果开启模拟预编译,依然可以造成宽字节注入。
使用PDO时,一定要将模拟预编译设置为false。
可采用Prepare Statement手动预编译,防御SQL注入。
代码示例
$dbh = new PDO('mysql:dbname=testdb;host=127.0.0.1', $user, $password);
$stmt = $dbh->prepare('INSERT INTO REGISTRY (name, value) VALUES (:name, :value)');
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
// insert one row
$name = 'one';
$value = 1;
$stmt->execute();
或者
$dbh = new PDO('mysql:dbname=testdb;host=127.0.0.1', $user, $password);
$stmt = $dbh->prepare('UPDATE people SET name = :new_name WHERE id = :id');
$stmt->execute( array('new_name' => $name, 'id' => $id) );
详细请参考:
ODBC 是一种应用程序编程接口(Application Programming Interface,API),使我们有能力连接到某个数据源(比如一个 MS Access 数据库)。
代码示例
$stmt = odbc_prepare( $conn, 'SELECT * FROM users WHERE email = ?' );
$success = odbc_execute( $stmt, array($email) );
或者
$dbh = odbc_exec($conn, 'SELECT * FROM users WHERE email = ?', array($email));
$sth = $dbh->prepare('SELECT * FROM users WHERE email = :email');
$sth->execute(array(':email' => $email));
MySQLi函数允许你访问MySQL数据库服务器。
$stmt = $db->prepare('update name set name = ? where id = ?');
$stmt->bind_param('si',$name,$id);
$stmt->execute();
对于框架的话只要遵循框架的API就好,例如wp查询
global $wpdb;
$wpdb->query(
$wpdb->prepare( 'SELECT name FROM people WHERE id = %d OR email = %s',
$person_id, $person_email
)
);
global $wpdb;
$wpdb->insert( 'people',
array(
'person_id' => '123',
'person_email' => '[email protected]'
),
array( '%d', '%s' )
);