PHP 安全设置 %5c magic_quotes_gpc GBK

Tagged Under : , ,

安全问题。

今天看到一个朋友的blog上,写到PHP的安全设置,应该开启magic_quotes_gpc,避免SQL注入等。

可是,我在用Symfony开发时,文档上说应该关闭php.ini的magic_quotes_gpc,以前我不知道为什么,今天突然有些明白了。

这个设置,只能起到非常有限的安全作用,却有可能带来更严重的问题。

PHP的放置SQL注入,很多网站或教程上,都写着用addslashes或mysql_escape_string来过滤,或者用自动的magic_quote。这些函数的作用,就是查询字符串中的单引号、双引号和斜线”\”,在这些字符前面插入一个斜线”\”。

如果遇到了”頫運鳿黒靄錦鳿黒靄錦”这样的字符,而你的数据库、网页又是GBK编码的话,上面的3种过滤函数,都会给你带来麻烦,因为这些字符的第二个字节的编码,是%5c,恰好是”\”对应的编码。

原本是2个字节的汉字,被这些函数变成了3个字节,最后的2个字节是连续的”\”,而这第3个”\”会把后续的引号转义,因此SQL语句就会出错,导致更新无法执行。

安全建议:

  1. 设置php.ini,默认关闭magic_quotes_gpc
  2. 使用mysql_real_escape_string对字符串变量进行转义,注意,转义前判断magic_quotes_gpc是否已开启
  3. 使用utf-8编码,可以有效避免此类问题

MySQL bin-log PURGE 清除日志

Tagged Under : , ,

我有一个每天进行大量数据更新的程序,写数据库,用的是innoDB,而且开了bin-log,这段时间,写了几百G的日志,太大了,前一段时间设置过 expire-logs-days =3,但没有生效,不知为什么。

后来实在受不了,上网找了找,原来有另一个方法清除bin-log,且看中文版:

PURGE {MASTER | BINARY} LOGS TO ‘log_name’

PURGE {MASTER | BINARY} LOGS BEFORE ‘date’

用于删除列于在指定的日志或日期之前的日志索引中的所有二进制日志。这些日志也会从记录在日志索引文件中的清单中被删除,这样被给定的日志成为第一个。

例如:

PURGE MASTER LOGS TO ‘mysql-bin.010′;

PURGE MASTER LOGS BEFORE ‘2008-06-22 13:00:00′;

清除3天前的 binlog

PURGE MASTER LOGS BEFORE DATE_SUB( NOW( ), INTERVAL 3 DAY);

BEFORE变量的date自变量可以为’YYYY-MM-DD hh:mm:ss’格式。MASTER和BINARY是同义词。

如果您有一个活性的从属服务器,该服务器当前正在读取您正在试图删除的日志之一,则本语句不会起作用,而是会失败,并伴随一个错误。不过,如果从属服务器是休止的,并且您碰巧清理了其想要读取的日志之一,则从属服务器启动后不能复制。当从属服务器正在复制时,本语句可以安全运行。您不需要停止它们。

要清理日志,需按照以下步骤:

1. 在每个从属服务器上,使用SHOW SLAVE STATUS来检查它正在读取哪个日志。

2. 使用SHOW MASTER LOGS获得主服务器上的一系列日志。

3. 在所有的从属服务器中判定最早的日志。这个是目标日志。如果所有的从属服务器是更新的,这是清单上的最后一个日志。

4. 制作您将要删除的所有日志的备份。(这个步骤是自选的,但是建议采用。)

5. 清理所有的日志,但是不包括目标日志

本文来自: (www.91linux.com) 详细出处参考:http://www.91linux.com/html/article/database/mysql/20080622/12727.html

以下是MySQL手册的英文原版:

13.6.1.1. PURGE MASTER LOGS Syntax

PURGE {MASTER | BINARY} LOGS TO 'log_name'
PURGE {MASTER | BINARY} LOGS BEFORE 'date'

Deletes all the binary logs listed in the log index prior to the specified log or date. The logs also are removed from the list recorded in the log index file, so that the given log becomes the first.

Example:

PURGE MASTER LOGS TO 'mysql-bin.010';
PURGE MASTER LOGS BEFORE '2003-04-02 22:46:26';

The BEFORE variant’s date argument can be in 'YYYY-MM-DD hh:mm:ss' format. MASTER and BINARY are synonyms.

This statement is safe to run while slaves are replicating. You do not need to stop them. If you have an active slave that currently is reading one of the logs you are trying to delete, this statement does nothing and fails with an error. However, if a slave is dormant and you happen to purge one of the logs it has yet to read, the slave will be unable to replicate after it comes up.

To safely purge logs, follow this procedure:

  1. On each slave server, use SHOW SLAVE STATUS to check which log it is reading.
  2. Obtain a listing of the binary logs on the master server with SHOW BINARY LOGS.
  3. Determine the earliest log among all the slaves. This is the target log. If all the slaves are up to date, this is the last log on the list.
  4. Make a backup of all the logs you are about to delete. (This step is optional, but always advisable.)
  5. Purge all logs up to but not including the target log.

You can also set the expire_logs_days system variable to expire binary log files automatically after a given number of days (see Section 5.2.3, “System Variables”). If you are using replication, you should set the variable no lower than the maximum number of days your slaves might lag behind the master.

MySQL 字符集

两个不同的字符串,分别插入一个表,保存字符串的字段叫 word,在 word 上建了唯一索引(unique key)。

连续插入这 2 个字符串,第二个居然报错:Duplicate entry ‘%s’ for key %d - Error: 1062 SQLSTATE: 23000 (ER_DUP_ENTRY)

是索引存在,不能插入 2 条相同 word 的记录。

这就奇怪了,明明是 2 个不同的字符串啊,urlencode() 和 md5() 计算出来的结果都不一样,怎么就不能插入呢?

后来仔细对比,并查了 ASCII 码表,发现有些字符经 urlencoe 以后,有一个字节是 %93,或者其他的,反正都大于 0×80 了。

如果这个字节加上后面的一个字节,不是一个有效的 GBK 编码,那么,MySQL 会把字符串截断到这个字节之前。

我做了一个测试,两个字符串经 urlencode 编码后是这样的:

‘%3F%3F%3F%3F%3F%3F%3F%3F%3F%3F’

‘%3F%3F%3F%3F%3F%3F%3F%3F%3F%3F%93%3F%3F%94%3F%3F%3F%3F%3F%3F’

在一个表建立唯一索引,然后按照先插入第一个字符串,再插入第二个,肯定会报错(GBK编码的数据库、表和字段)。

分析原因:

这段时间在钻研 mysql_real_escape_string() 与 addslashes() 等函数的内在区别。

mysql_real_escape_string() 需要数据库连接句柄作为第二个参数,也就是说,这个函数需要参考当前连接的字符集进行转义。

GBK 编码,汉字是双字节的,第一个字节的 ASCII 码必须要大于 0×80,而第二个字节必须满足另一个规律才可以。这个规律就是 GBK 的字符集。第一个字节大于 0×80 的双字节字符有 128 * 256 个,但并不是说 GBK 的字符集就有 32768 个字符,我们的汉字好像没有这么多。

mysql_real_escape_string() 函数会参考当前连接的字符集,检查需要转义的字符串的每一个字节。

因此,如果一个汉字是 %bf%5c,并不会被转义成 %bf%5c%5c (addslashes() 的结果就是这样,\ 的ASCII 码就是 %5c),因为 %bf%5c 是一个有效的汉字。

我写了一个Wiki,里面是测试代码,用于验证上面的唯一索引的例子:

http://wiki.leakon.com/MySQLUniqueKeyCharset

安装php5出现 mysql configure failed

安装PHP5时出现了错误。
configure: error: mysql configure failed. Please check config.log for more information.
./configure –prefix=/usr/local/php –with-apxs2=/usr/local/apache2/bin/apxs –with-gd –with-jpeg-dir –with-zlib-dir –with-png-dir –with-ttf –with-mysql enable-track-vars 出现的问题。

如果是自己选择GZ包安装的MYSQL。这里要加路径。
加的方法为–with-mysql-dir=/user/local/mysql
切记不可写为:–with-mysql=/user/local/mysql

数据库导入 乱码 数据被截断 数据不全

今天碰到一个Case,是数据库导入的,很顺利地解决了,跟大家分享一下过程。

环境:

数据源服务器

mysql>show variables like ‘char%’;
character_set_client gbk
character_set_connection gbk
character_set_database gbk
character_set_results gbk
character_set_server gbk
character_set_system utf8

所有环境都是gbk,但备份脚本是这样的:
mysqldump my_db_name /home/leakon/db_bak/dump.sql

打开dump.sql,里面的中文字符都变成乱码了,在这个文件的头部,可以看到这么一行:
/*!40101 SET NAMES utf8 */;

也就是说,mysql在导出的时候,按照utf8字符集,进行了编码转换。

你可以这么理解,你和我都是中国人,说汉语,但我们之间沟通的时候用了一个翻译,这个翻译是英国人。

我就是那个数据源,英国的翻译就是dump.sql,我要跟你说中国话, 但翻译先把汉语翻译成英语,存在他的脑子里了。

这个时候你看他的内容,是没法理解的,这就是你看到的乱码。

下面,要把导出的数据,导入到新数据库中。

注意了,应该用下面的脚本:
mysql –default-character-set=latin1 -uroot my_new_db < dump.sql

我指定了latin1,作为连接mysql服务器的字符集。

实际上,导入的过程,就是让那个英国翻译,把英语再转换成汉语告诉你。这时,指定了latin1字符集, 就是告诉翻译,你让他把脑子里存的话,用英语翻译成汉语告诉你。

这里必须指定latin1,我也很奇怪为什么不指定utf8,我试过,不成功,服务器提示不支持这种编码。

具体原因我还会再研究,目前解决办法就是这样了。

指定字符集,就是让翻译按英语->汉语的转换程序进行翻译,否则,翻译可能是按英语->法语的顺序转换的,也就证明了,为什么导入数据库时,不指定编码,会是奇怪的乱码字符。

这里还有一个重要因素,就是数据库的字符集。

这个 my_new_db 创建的时候指定的是 gbk 字符集,建表语句是:
CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;

如果数据库的字符集和源数据库不一样,那么,在导入时,很肯能出现导入到一半,MySQL报语法错,或者本来10万行数据只导入了2万行就停了。

原因就是编码转换时解析引擎会把其中的某些字符理解为引号等字符,导致语法错乱。

其实,有彻底解决这些编码问题的办法,那就是保证所有过程都指定正确的编码。

下面就以gbk为例:

  1. 源数据库,数据库、表和字符串字段的编码统一设定为gbk
  2. PHP调用程序时,在创建MySQL连接后立即发送一条 set names ‘gbk’ 语句
  3. 导出时指定gbk字符集:mysqldump -uroot –opt –default-character-set=gbk my_db_name > dump.sql
  4. 检查文件内容,应该是正确编码的字符,用普通文本编辑器可以阅读
  5. 目标数据库建库脚本:CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci
  6. 导入时:mysql –default-character-set=gbk -uroot my_new_db < dump.sql

就这几个简单的步骤,就完全避免编码问题!

如果你要用utf8编码,把上述脚本的gbk都替换成utf8,把 gbk_chinese_ci 替换成 utf8_general_ci。

再重新导入,编码没问题,数据也完全导入了吧。

Google

Google
LAMP-Linux-redhat LAMP-Apache LAMP-MySQL LAMP-Php Leakon-Wiki Leakon-BBS XueBaoBao Xyoyou