一个注入漏洞的分析与检讨

今天积分系统被注射了,幸好没造成什么损失。问题在下面这一条语句上:

$type = ( in_array( $_GET['t'], array(1,2,3) ) ) ? $_GET['t'] : 0;
//对$type入库操作

先判断参数t是否在1,2,3中,如果不是$type则为0。看上去没什么问题,因为参数t被定死了,无法进行构造注入语句。

但是PHP弱类型语言,两个不同类型的变量比较,其中一个变量会进行转换,也就是说$_GET['t']在与整形1,2,3作比较前,会将字符串转成整形。

字符串转成数字型时,如果字符串开头不是数字形式,就转成0(例如intval('zyday')==>0 );如果该字符串开头是数字形式的,就会转成该数字,后面的非字符串被去掉(例如intval( '111zyday')==>111 )。只要开头为1,2,3中任意一个,后面随便接什么都行。

示例:

提交参数 t='and 1=2 union select '; 
in_array( $_GET['t'], array(1,2,3) )    //返回false

提交参数t='1 and 1=2 union select '; 
in_array( $_GET['t'], array(1,2,3) )    //返回true 漏洞产生

我很惭愧,作为一名PHP开发者,保证写出的代码没有安全问题是最起码的底线。今天出了这么大的纰漏,竟然还大言不惭地说热爱网络安全,无地自容。基本功差、执行力差、不主动、说到做不到是我最致命的弱点,长此以往即使再过10年水平也就和刚毕业一样。自身的问题很多很多,在此纪录一下,以此反思、勉励。

扫盲:Memery存储引擎

当我们需要快速访问数据时,特别是一些从不改变的或者重启服务器后不再需要的数据,那么Memery存储引擎是不二之选。Memery表又叫做HEAP表,它将数据存放在内存中,因此不需要进行磁盘I/O等待,这比MyISAM可快多了。当机子重启后,Memery表的结构还在,不过之前的数据将会全部消失。

以下情况较适合使用Memery存储引擎:

①用于检索或映射的表。如每个城市对应邮编。
②用于缓存周期性的聚合结果。
③用于分析数据时存放中间结果。

Memery表支持查询速度非常快的Hash索引,但是在进行写入替换操作时,它的效率却比不上存放在磁盘上的表。这是因为Memery表是表级锁,写入并发低。还有,它仅支持定长型字段,也就是说,varchar会自动转为char,很是浪费内存。

当一个查询使用到临时表时作中间介质,该临时表就会默认使用Memery存储引擎。Memery表不支持TEXT与BLOG字段类型,当这些中间数据含有BLOG或TEXT字段类型时,MYSQL会将临时表转化为MyISAM存储引擎,并存放在磁盘上。

PS:很多人会将临时表与Memery表搞混淆了,注意这是两个完全不同的概念。Memery表只是一种存储引擎,而临时表可以使用任意存储引擎,它是短时间存在的。临时表只存在于单个数据库连接中,一旦这个连接断开,临时表也将消失。

IE下兼容getElementsByName的方法

假如要获取<input type="text" name="zyday">的元素,可以使用document.getElementsByName,如:

zyday = document.getElementsByName("zyday");
仔细测试一下会发现,FF、chrome等浏览器没有问题,但是IE下就会出现异常了。
因为在IE中,document.getElementsByName("zyday") 不仅会获取 name 属性值为zyday的元素,同时也会获取 id 属性值为zyday的元素

所以要想解决此问题,可以先通过getElementsByTagName()提取所有input元素然后再帅选name属性值来获取zyday元素,代码如下:
 
inputs = document.getElementsByTagName("input");
zyday = new Array();
for (var i=0; i<inputs.length; i++) {
     if(inputs[i].name=='zyday') {
         zyday.push(inputs[i]);
     }
}
这时在IE下就能准确获取name="zyday"的元素了。

cookie的作用域

当我们给网站设置cookie时,大家有没有发现在网站的其他域名下也接收到了这些cookie。这些没用的cookie看似不占多少流量,但如果对一个日PV千万的站点来说,那浪费的资源就不是一点点了。因此在设置cookie时,对它的作用域一定要设置准确了。

我们都知道在PHP中用setcookie 来设置网站的cookie,该函数的用法如下:

bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
 

今天我们就来探讨一下它的第五个参数$domain,因为它决定了cookie的作用域。 继续阅读

INSERT ... ON DUPLICATE KEY UPDATE 的简单用法

假如我们要做一个统计页面访问量的功能,记录IP、点击量与时间。当某IP第一次访问该页面时,这个IP在数据库中肯定是不存在的,需要插入一条新数据,而以后的每次访问,就递增访问量。那么在入库的时候一般有以下几个步骤:
1.判断是否存在该IP
2.如果不存在,插入新数据
3.如果存在,递增点击量

其实还有一种更简便的方法,mysql在4.1版本以上就提供了一个INSERT ... ON DUPLICATE KEY UPDATE语法,用这句可以轻松实现以上的功能。 继续阅读

2012年学习计划

2011总算过了,过得浑浑噩噩。11年作出最重大的选择就是离开了我的第一家公司、离开了常州。父母在,不远游,我去了上海。由于家住上海附近的小城市,对于一个IT民工来说,上海是我最好的选择了。

2012年来了,管它世界末日会不会到来,反正我没有船票,但是日子还是要过的。在上海就职一个多月了,感受到公司伙伴之间浓厚的学习氛围,也发现了自身的许多不足。2012将从以下几方面来提高:

1、PHP。没什么好说,至少在3年内,PHP需要一直学习。方法:多看,多写,多交流。多看优秀的开源程序,如Uchome、discuz、Phpcms等,学习其思想、其手法,做到知其然,知其所以然。多看国内外优秀的PHP教程;勤动手,多写代码,不管多简单,一定要自己写一遍。还有要多写写博客,这也是提高自身水平的一种方式。最后是多交流,同事之间的交流、社区论坛上的交流,当然,写博客也是一种交流。

2、Linux。目标:夯实基础。方法:看书,练习。Linux是我的薄弱项,争取在上半年将鸟哥的私房菜看完,边看边练。下半年计划把鸟哥的私房菜服务器篇看完。

3、数据库优化。搞基开发攻城师的必经之路,也是本年度重点。方法:看书,实践,交流。

4、English。越来越发现英语的重要性,要想在技术上有更多提高,避免不了去看国外文章。这时英语水平就是一道坎,当然这也不是一朝一夕能提上去的,需要每天积累。方法:翻译国外文章,听VOA。

5、语文水平。哎,这是我的硬伤。小时候语文没学好,导致现在我的语言表达能力极差,看看我写的博客就知道了。对于这部分,我目前计划多去青番茄,借阅一些文学类书籍,不为陶冶情操,就当是补习我的语文课程。

2012了,又长了一岁,肩上的责任又大了、压力又加重了。我经常安慰朋友说:“做技术,切莫浮躁”,却不能用这句说服自己。社会太现实,现实又太残忍。在这浮躁的环境,我其实没有什么远大的追求,只希望在新的一年里技术与薪资节节升高!哈哈!只有享不完的福,没有吃不完的苦!2012,加油!

在Join中On与Using的区别

在用Join进行多表联合查询时,我们通常使用On来建立两个表的关系。其实还有一个更方便的关键字,那就是Using。那么这两个关键字在使用上有啥区别呢?往下看。

假设有如下两张表:

 
mysql> select * from pets;
+---------+---------+--------+-----------+
| pets_id | animal  | name   | owners_id |
+---------+---------+--------+-----------+
|       1 | fox     | Rusty  |         2 |
|       2 | cat     | Fluffy |         2 |
|       3 | cat     | Smudge |         3 |
|       4 | cat     | Toffee |         3 |
|       5 | dog     | Pig    |         3 |
|       6 | hamster | Henry  |         1 |
|       7 | dog     | Honey  |         1 |
+---------+---------+--------+-----------+
7 rows in set (0.00 sec)

mysql> select * from owners;
+-----------+-------+
| owners_id | name  |
+-----------+-------+
|         1 | Susie |
|         2 | Sally |
|         3 | Sarah |
+-----------+-------+
3 rows in set (0.00 sec)

继续阅读

详解$_SERVER['PHP_SELF']、$_SERVER['REQUEST_URI']与$_SERVER['SCRIPT_NAME']的区别

$_SERVER['REQUEST_URI']、$_SERVER['PHP_SELF']与$_SERVER['SCRIPT_NAME']这三个函数在用法上其实差不多,他们返回当前文件的一些信息。那它们有啥区别呢,看了下面的例子大家就很快能明白了。

$_SERVER['REQUEST_URI']

  • http://www.zyday.com/example/ -- -- -- /
  • http://www.zyday.com/example/index.php -- -- -- /example/index.php
  • http://www.zyday.com/example/index.php?a=test -- -- -- /example/index.php?a=test
  • http://www.zyday.com/example/index.php/dir/test -- -- -- /example/index.php/dir/test

以上4个例子,都有一个共同点,我们输入什么,就返回什么。所见即所得,很容易记住吧。

$_SERVER['PHP_SELF']

  • http://www.zyday.com/example/ -- -- -- /example/index.php
  • http://www.zyday.com/example/index.php -- -- -- /example/index.php
  • http://www.zyday.com/example/index.php?a=test -- -- -- /example/index.php
  • http://www.zyday.com/example/index.php/dir/test -- -- -- /dir/test

不管我们是否输入了index.php,都返回了/example/index.php;即使加上了参数,也只返回/example/index.php。但有一点不同,在后面加上目录时,返回的是该目录/dir/test。

$_SERVER['SCRIPT_NAME']

  • http://www.zyday.com/example/ -- -- -- /example/index.php
  • http://www.zyday.com/example/index.php -- -- -- /example/index.php
  • http://www.zyday.com/example/index.php?a=test -- -- -- /example/index.php
  • http://www.zyday.com/example/index.php/dir/test -- -- -- /example/index.php

这个变量只返回文件名,不管是否输入index.php,不管是否输入参数,也不管后面是否带上目录,都只返回当前文件名。

UCHome整站代码分析讲解

本次代码分析全部按照本人水平来讲解,有问题欢迎回复指正。谢谢!

common.php

@define('IN_UCHOME', TRUE);

/* 开头定义了一个常量IN_UCHOME。@的作用是屏蔽所有错误信息。那这个常量有啥作用的?安全。它相当于一张火车票,所有的PHP开头都有一个检票员,有了这车票才能进火车。没票?那就甭想上车,你太危险了。*/ 

define('D_BUG', '0');

/* 这里的常量为了控制调试的模式,默认为关闭调试。如果要开启调试,只要手动改为1,我们往下看。*/   

D_BUG?error_reporting(7):error_reporting(0);

/* 出现了,error_reporting显示的是错误级别。参数中0表示不显示任何错误,7会显示notice与warning,具体 参看手册。这里用了三目运算符,如果if判断较少时,这种写法非常爽。那么这句有什么作用呢? 如果D_BUG为true, 那么显示报错信息,反之,不显示任何报错信息。现在明白了上面定义D_BUG的意图了吧。*/   

set_magic_quotes_runtime(0);

/* 关闭入库前转义特殊字符,比如zyday'com,会转义成zyday\'.com。为了程序的性能,我们关闭吧。*/    继续阅读