当使用 PDO 连接操作数据库的时候,有时会出现:保存在数据库中的汉字为乱码。以文件为 UTF-8 格式,其解决方法如下:
(1)实例化的对象直接执行 query() 方法或者 exec() 方法:
<?php
class DB {
static public function getDB() {
try {
$_opts_values = array(PDO::ATTR_PERSISTENT=>true,PDO::ATTR_ERRMODE=>2);
$_pdo = new PDO(DB_DSN, DB_NAME, DB_PASS, $_opts_values);
} catch (PDOException $e) {
exit('数据库连接错误!错误信息:'.$e->getMessage());
}
$_pdo->query("SET NAMES utf8"); // $_pdo->exec('SET NAMES utf8'); //设置数据库编码,两种方法都可以
return $_pdo;
}
}
?>
(2)在实例化 PDO 的第四个参数添加 MYSQL_ATTR_INIT_COMMAND 属性:
<?php
class DB {
static public function getDB() {
try {
$_opts_values = array(PDO::ATTR_PERSISTENT=>true,PDO::ATTR_ERRMODE=>2,PDO::MYSQL_ATTR_INIT_COMMAND=>'SET NAMES utf8');
$_pdo = new PDO(DB_DSN, DB_NAME, DB_PASS, $_opts_values);
} catch (PDOException $e) {
exit('数据库连接错误!错误信息:'.$e->getMessage());
}
return $_pdo;
}
}
?>
PHP PDO 参考手册
PHP PDO 预处理语句与存储过程
PHP防SQL注入不要再用addslashes和mysql_real_escape_string了
PDO中使用Like进行参数模糊查找
预处理参数集我觉得最大的功能是为了减少注入攻击的,也就是绑定外来参数值。
如果字段名需要动态参与sql执行,动态字段名由自己控制不存在安全问题,直接写在sql串里吧,拼接起来,字段名没必要绑定
easydb
PHP-PDO-MySQL-Class
如果不执行$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);PDO 只是会将插入的参数使用本地转义之后和SQL模板拼装起来,然后一起发送给Mysql Server。这实际上与使用mysql_real_escape_string()过滤,然后拼装这种做法并没有什么不同。
要对数据库的安全做出更加全面的考量,以下两种方式任选其一:
A. 通过添加(php 5.3.6以前版本):$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
B. 升级到php 5.3.6 (不用设置PDO::ATTR_EMULATE_PREPARES也可以)
为了程序移植性和统一安全性,建议使用$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false)方法
使用PDO访问MySQL数据库时,真正的real prepared statements 默认情况下是不使用的。为了解决这个问题,你必须禁用 prepared statements的仿真效果。
当调用 prepare() 时,查询语句已经发送给了数据库服务器,此时只有占位符 ? 发送过去,没有用户提交的数据;当调用到 execute()时,用户提交过来的值才会传送给数据库,他们是分开传送的,两者独立的,SQL攻击者没有一点机会。
但是我们需要注意的是以下几种情况,PDO并不能帮助你防范SQL注入
1、你不能让占位符 ? 代替一组值,如:
2、你不能让占位符代替数据表名或列名,如:
3、你不能让占位符 ? 代替任何其他SQL语法,如:
使用PDO的注意事项
知道以上几点之后,我们就可以总结使用PDO杜绝SQL注入的几个注意事项:
1. php升级到5.3.6+,生产环境强烈建议升级到php 5.3.9+ php 5.4+,php 5.3.8存在致命的hash碰撞漏洞。
2. 若使用php 5.3.6+, 请在在PDO的DSN中指定charset属性
3. 如果使用了PHP 5.3.6及以前版本,设置PDO::ATTR_EMULATE_PREPARES参数为false(即由MySQL进行变量处理),php 5.3.6以上版本已经处理了这个问题,无论是使用本地模拟prepare还是调用mysql server的prepare均可。在DSN中指定charset是无效的,同时set names <charset>的执行是必不可少的。
4. 如果使用了PHP 5.3.6及以前版本, 因Yii框架默认并未设置ATTR_EMULATE_PREPARES的值,请在数据库配置文件中指定emulatePrepare的值为false。
那么,有个问题,如果在DSN中指定了charset, 是否还需要执行set names <charset>呢?
是的,不能省。set names <charset>其实有两个作用:
A. 告诉mysql server, 客户端(PHP程序)提交给它的编码是什么
B. 告诉mysql server, 客户端需要的结果的编码是什么
也就是说,如果数据表使用gbk字符集,而PHP程序使用UTF-8编码,我们在执行查询前运行set names utf8, 告诉mysql server正确编码即可,无须在程序中编码转换。这样我们以utf-8编码提交查询到mysql server, 得到的结果也会是utf-8编码。省却了程序中的转换编码问题,不要有疑问,这样做不会产生乱码。
那么在DSN中指定charset的作用是什么? 只是告诉PDO, 本地驱动转义时使用指定的字符集(并不是设定mysql server通信字符集),设置mysql server通信字符集,还得使用set names <charset>指令。
execute( )执行预准备语句,fetchAll( )返回包含所有结果集行的数组。
PDO如何解决sql注入
在php5.3.6之后,pdo不会在本地对sql进行拼接然后将拼接后的sql传递给mysql server处理(也就是不会在本地做转义处理)。pdo的处理方法是在prepare函数调用时,将预处理好的sql模板(包含占位符)通过mysql协议传递给mysql server,告诉mysql server模板的结构以及语义。当调用execute时,将两个参数传递给mysql server。由mysql server完成变量的转移处理。将sql模板和变量分两次传递,即解决了sql注入问题。
PDO的变量绑定bindParam和bindValue会自动添加转义。
但无法对表或字段的名字进行变量绑定。
因此当表名或字段名不确定(即可能从用户输入中获取),而需要动态组装SQL语句时,即使是用了PDO变量绑定来提供字段的值,应当注意表名和字段名部分的过滤。
PDO,只是一个手段,并非最合适的方法。
方法 bindParam() 和 bindValue() 非常相似。
唯一的区别就是前者使用一个PHP变量绑定参数,而后者使用一个值。
所以使用bindParam是第二个参数只能用变量名,而不能用变量值,而bindValue至可以使用具体值。
另外在存储过程中,bindParam可以绑定为input/output变量,如下面
存储过程执行过后的结果可以直接反应到变量上。
对于那些内存中的大数据块参数,处于性能的考虑,应优先使用前者bindParam()
#7错的。大家去看手册http://www.php.net/manual/zh/pdostatement.bindparam.php
http://zhangxugg-163-com.iteye.com/blog/1835721
注意二级注入。比如用户把带危险的字符串先存进数据库,如果你想在另外一个 SQL 语句里 concat 或者直接拼接那个带危险的字符串,是可以注入的。
PHP PDOStatement对象bindpram()、bindvalue()和bindcolumn之间的区别
PDOStatement::bindParam — 绑定一个参数到指定的变量名。
绑定一个PHP变量到用作预处理的SQL语句中的对应命名占位符或问号占位符。 不同于 PDOStatement::bindValue() ,此变量作为引用被绑定,并只在 PDOStatement::execute() 被调用的时候才取其值。
PDOStatement::bindValue — 把一个值绑定到一个参数。
绑定一个值到用作预处理的 SQL 语句中的对应命名占位符或问号占位符。
PDOStatement::bindColumn — 绑定一列到一个 PHP 变量。
安排一个特定的变量绑定到一个查询结果集中给定的列。每次调用 PDOStatement::fetch() 或 PDOStatement::fetchAll() 都将更新所有绑定到列的变量。
#10🎉黑鬼问号脸