从ThinkPHP漏洞里学到的

2012年4月20日 策马江湖 没有评论

前几天thinkphp被爆出执行任意代码漏洞。正好我平时常用的也是thinkphp,乘此时机研究一下。

引起漏洞的代码(Dispatcher.class.php):

$res = preg_replace('@(\w+)'.$depr.'([^'.$depr.'\/]+)@e', '$var[\'\\1\']="\\2";', implode($depr,$paths));

这个是tp用来把url后的参数赋值的操作。类似于domain.com/index.php/Index/index/a/111/b/222这样的URL,它的使命就是使$var['a'] = 111;$var['b'] = 222;然后合并到$_GET和$_REQUEST中。

为了使preg_replace的第二个参数’$var[\'\\1\']=”\\2″;’(这是一个字符串)作为PHP语句执行以便完成赋值操作,tp在这里用了preg_replace的e参数(e参数的作用)。

问题就出在这了。如我们所看到的,\\2的外面包围的是双引号——有些php常识的就知道双引号和单引号的区别——双引号里的变量可以直接执行。

但是双引号里的函数却是不被执行的。不是没有办法的——在PHP里可以通过{${}}构造一个特殊的变量,把函数放在中间就可以执行了:

domain.com/index.php/Index/index/a/${@phpinfo()}

加一个@来忽略错误。这样,就直接执行并显示了:

update:

刚开始以为用@是为了忽略错误,后来搜索发现仅仅是${phpinfo()}是没法执行的,会报unexpected ‘(‘错误。
需要在函数前加空格、@、回车、tab、注释才行(php程序里,url上只能用空格和@)。

参考:

1.《preg_replace危险的/e修饰符,慎用!!!

2.《浅谈PHP可变变量安全续

分类: 框架 标签: ,

[win]php连接mssql

2012年4月5日 策马江湖 没有评论

一、php5.2.*

连接mssql 2005以下的版本在php.ini里把extension=php_mssql.dll前的分号去掉重启web服务器就行了。

如果mssql版本在2005及以上的话,在完成上述的基础上还要下载新版的ntwdblib.dll( 2000.80.194.0)放到apache的bin目录下。(我安装的apache2.2.8,bin下有个默认的ntwdblib.dll,版本是2000.2.8.0,直接覆盖之)。

二、php5.3.*

php从5.3开始默认不支持sql server。庆幸的是微软反过来对php支持。所要要想连接sql server的话就要费一番周张:

1.去Microsoft官网下载sqlsrv扩展。解压后有针对各个版本的扩展,还有文档。(也可单独下载)

2.查看自己的php版本:一般要了解的就是VC6、VC9、TS和NTS,打开phpinfo,Compiler选项是MSVC6的话就是是VC6,Thread Safety选项是enabled就是TS。我的是php5.3.5-vc6-ts。就把php_sqlsrv_53_ts_vc6.dll拷到php的extension目录下。

3.在php.ini里开启对sqlsrv的支持:extension=php_sqlsrv_53_ts_vc6.dll

4.重启web服务器,打开sqlsrv手册,与测试

$serverName = "server_ip,port";
$connectionInfo = array( "Database"=>"dbname","UID"=>"username","PWD"=>"password");
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if( $conn )
{
     echo "Connection established.\n";
}
else
{
     echo "Connection could not be established.\n";
     die( print_r( sqlsrv_errors(), true));
}

错误整理:
1.This extension requires either the Microsoft SQL Server 2008 Native Client (SP1 or later) or the Microsoft SQL Server 2008 R2 Native Client ODBC Driver to communicate with SQL Server. Neither of those ODBC Drivers are currently installed.

这是因为要连接的mssql的版本比较高,需要安装SQL Server Native Client

分类: 未分类 标签: , ,

eAccelerator的安装

2012年4月2日 策马江湖 没有评论

1.下载

wget http://downloads.sourceforge.net/project/eaccelerator/eaccelerator/eAccelerator%200.9.6.1/eaccelerator-0.9.6.1.tar.bz2

2.

解压:tar jxvf eaccelerator-0.9.6.1.tar.bz2

进入目录: cd eaccelerator-0.9.6.1

3.安装

phpize

./configure

make

make install

这时会把扩展eaccelerator.so安装到/usr/lib/php5/(phpize所显示的php版本日期)/ 下(具体目录视情况而定,make install后会有提示)

4.把eaccelerator.ini拷贝到php的配置目录下:

cp eaccelerator.ini /etc/php5/conf.d

5.修改eaccelerator.ini里eaccelerator.so的地址、cache_dir等。

6.建立cache_dir并给足权限。

7.验证:

重启web服务器service nginx restart

php5-fpm -v

Copyright (c) 1997-2009 The PHP GroupZend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies    with eAccelerator v0.9.6.1, Copyright (c) 2004-2010 eAccelerator, by eAccelerator

出现这段字就是配置成功了。

如果提示:

[eAccelerator] This build of “eAccelerator” was compiled for PHP version 5.2.1ubuntu4.14. Rebuild it for your PHP version (5.3.2-1ubuntu4.7ppa5~lucid1) or download precompiled binaries.

网上有的教程说在这步之前修改/usr/include/php5/main/php_version.h,
但是我试了却没有起作用。

tutdepot上看到说要执行phpize –clean,执行过后地重新编译一下eAccelerator就成功了。

phpize是什么玩意?

手册地址:http://php.net/manual/en/install.pecl.phpize.php

phpize是用来扩展php扩展模块的,通过phpize可以建立php的外挂模块。也就是说对于已经编译好的php,要想再添加扩展模块,可以用这个东西,而不用重新编译php。

分类: PHP 标签:

nginx配置问题:root指定的目录无效

2012年4月1日 策马江湖 没有评论

配置nginx的vhost:

server {
listen 80;
server_name domain;

access_log /var/log/nginx/domain.access.log;

location / {
root /var/www/domain;
index index.php index.html index.htm;
}

}

nginx重启后,域名打开的内容却是var/www/nginx-default下面的。找了半天无果,而在今天无意中发现问题的原因:fastcgi_param配置的问题:

location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/nginx-default$fastcgi_script_name;
include fastcgi_params;
}

修改后重启nginx就OK了。

分类: 服务器, 问题字典 标签:

昙花一现:CSS中的expression

2012年3月31日 策马江湖 没有评论

为了解决ie6对first-child的兼容性搜索到了expression:

IE5及其以后版本支持在CSS中使用expression,用来把CSS属性和Javascript表达式关联起来。

能在css里写javascript,听起来挺酷的,立即尝试了一下:

.theme-small {padding-top:expression(this.previousSibling==null?’0′:’20px’);}

在IE6下一试:还真行!但是在Firefox和IE8下却没有效果了。怎么回事呢?

原来expression在非常消耗CPU,即使在IE5-7中也不建议使用,而IE8+及Firefox等直接就把它给封杀了。

参考:

1.《让IE6支持first-child和last-child伪类选择符》

2.《IE8将不再支持CSS中的expression》

分类: CSS 标签: ,

Git: “failed to push some refs to”问题

2012年3月31日 策马江湖 没有评论

当用git push origin master把本地的修改推到服务器上时,出现了下面的错误:

Counting objects: 8, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (6/6), 515 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require ‘git reset –hard’ to match
remote: error: the work tree to HEAD.
remote: error:
remote: error: You can set ‘receive.denyCurrentBranch’ configuration variable to
remote: error: ‘ignore’ or ‘warn’ in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error:
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: ‘receive.denyCurrentBranch’ configuration variable to ‘refuse’.
To /data/test/
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to ‘/data/test/’

对于问题和答案就在这段文字里。

红色的是原因,蓝色的是解答。

如果使用了git init初始化,则远程仓库的目录下,也包含work tree,当本地仓库向远程仓库push时,   如果远程仓库正在push的分支上(如果当时不在push的分支,就没有问题), 那么push后的结果不会反应在work tree上,  也即在远程仓库的目录下对应的文件还是之前的内容,必须得使用git reset –hard才能看到push后的内容.

解决办法:
一,在服务器上初始化仓库的时候用git –bare init;

用”git init”初始化的版本库(暂且称之为working repository)将会生成2类文件:“.git“版本库目录(记录版本历史)和实际项目文件的拷贝。你可以把这类版本库叫做“工作目录”。工作目录是一个包含有版本历史目录“.git”和源文件的目录。你可以在工作目录修改你的源文件并使用”git add”和”git commit”命令进行版本管理。

用“git init –bare”初始化的版本库(暂且称之为bare repository)仅包含”.git”目录(记录版本历史),不含项目源文件拷贝。如果你进入版本目录,你会发现仅有”.git”目录,没有其他文件。版本库仅包含记录着版本历史的文件。

 

二,如果用的是git init的话,就要修改下服务器上的配置(.git/config),添加:
[receive]
denyCurrentBranch = ignore

参考:1.《push出错解决》

2.《git init & git init –bare》

分类: 服务器, 问题字典 标签: ,

utf8字符串截取

2012年3月23日 策马江湖 2 条评论

对于字符串的截取,php里提供了不少的函数,普通的有substr,多字节的有mb_substr。我们常接触到的就是utf8字符的截取,一般的用mb_substr就可以搞定的。但是如果有特殊的需求,需要自己来写一个呢?

这里多少就要了解些utf8的原理了。
utf8是unix之父Ken Thompson提出的,具有以下性质:

  • 编码为U+0000~U+007F的字符只占一个字节,就是0×00~0x7F,和ASCII码兼容。
  • 编码大于U+007F的字符用2~6个字节表示,每个字节的最高位都是1,而ASCII码的最高位都是0,因此非ASCII码字符的表示中不会出现ASCII码字节(也就不会出现0字节)。
  • 用于表示非ASCII码字符的多字节序列中,第一个字节的取值范围是0xC0~0xFD,根据它可以判断后面有多少个字节也属于当前字符的编码。后面每个字节的取值范围都是0×80~0xBF,见下面的详细说明。
  • UCS定义的所有231个字符都可以用UTF-8编码表示出来。
  • UTF-8编码最长6个字节,BMP字符的UTF-8编码最长三个字节。
  • 0xFE和0xFF这两个字节在UTF-8编码中不会出现。

UTF-8编码最长6个字节,按字节来区分的话可以分为6类,表示如下:
U-00000000 – U-0000007F: 0xxxxxxx
U-00000080 – U-000007FF: 110xxxxx 10xxxxxx
U-00000800 – U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 – U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 – U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 – U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

根据这些我们可以得出结论:第一个字节在0-127之间是一个字节;在128-192的占两个字节;在193-224之间的占三个字节……

知道这个原理了,写起来就简单的多:

1.用strlen求出字符串的长度;

2.遍历字符串,用ord求出当前字节的值;

3.根据需求进行处理。

自己随便写了一个:

function word_width_cut($string, $start = 0, $len = 0, $separator = '')
{
    $str = "";
    $begin = 0;
    $count = 1;
    $str_len = strlen($string);

    for($i = 0; $i < $str_len; $i++) {
    	$ord = ord($string[$i]);
    	if (($count - ($start + 1)) >= $len) {
    		$str .= $separator;
    		return $str;
    	}else if ( $count >= $start) {
	    	if ($ord >= 224 ){
	    		$str .= substr($string, $i , 3);
	    		$i += 2;
	    	} elseif ($ord >= 192) {
	    		$str .= substr($string, $i , 2);
	    		$i += 1;
	    	} else {
	    		$str .= substr($string, $i , 1);
	    	}
	    	$count ++;
    	} else {
    		if ($ord >= 224 ){
    			$i += 2;
	    	} elseif ($ord >= 192) {
	    		$i += 1;
	    	}
	    	$count ++;
    	}
    }
    $str .= $separator;
    return $str;
}

参考:《UTF-8 编码原理篇

分类: PHP 标签: ,

VirtualBox下修改ubuntu的分辨率

2012年3月21日 策马江湖 没有评论

用VirtualBox装了个ubuntu,但是每次打开都是800*600的分辨率,那么大一点儿,真是恼人。网上搜了下,终于把问题解决了。整理笔记如下:

一、安装VBOXADDITIONS

(我的VirtualBox的版本是4.1.2)

设备->安装增强功能

然后桌面上就会出现VBOXADDITIONS的图标(以光驱的形式挂载在/media下)

从命令行下进入:

有 window下的(x86、amd64平台)和linux下的。

执行VBoxLinuxAdditions.run:

二、重启

重启后就可以根据VirtualBox窗口的大小而自适应了!

参考:

1.《virtualbox 虚拟ubuntu设置分辨率
2.《How to install VirtualBox Guest Additions

分类: 软件使用 标签:

字节与存储(扫盲篇)

2012年3月20日 策马江湖 没有评论

前一阵子和同事讨论mysql里的字段所能存储的最大最小值的时候,发现一个很奇怪的逻辑:

在phpmyadmin里设置字段的时候,int默认长度是11位,他就认为所能存储的最大值是99999999999(11个9)。

这是典型的计算机基础没有打好。(本文是扫盲篇,如果上述的问题您知道答案,就没有必要看下去了。)

我们都知道:计算机的最小存储单位是位(bit),一个位能表示两个值(0和1)。一个字节(Bytes)呢,就是8个位。8位可以用来表示2^8共256个数字(0~255)。

看一下mysql里的数值型字段的所占的字节数:

TinyInt    1Bytes

SamllInt   2Bytes

MediumInt   3Bytes

Int     4Bytes

BigInt  8Bytes

接下来拿TinyInt来算一下它的最大值和最小值:

TinyInt占1个字节,即8个位,编号0~7

无符号:最小值就是8个位全是0,即:0

最大值是8个位全是1,即2^0+2^1+2^2+2^3+2^4+2^5+2^6+2^ = 2^8 – 1 = 255

有符号:符号占一位,剩余的就是7个位了。
正数的最小值和最大值就是七个0和七个1,即0和127;(原码)
那么负数呢?负数在计算机里用补码来表示(反码加1),即除符号位外,七个1是表示-1,七个零表示的则是-128。
(正数0-127的表示是原码,即除符号位外0000000-1111111,而负数的-1到-127的表示是1111111-0000001,0000000表示-128)
那么默认长度是怎么回事呢?在《高性能Mysql》第二版(P64)里有这样的解释:

MySQL还可以对整形类型定义宽度,比如INT(11)。这对于大多数应用程序是没有意义的:它不会限制值的范围,只规定了MySQL交互工具(例如命令行客户端)用来显示的字符的个数对于存储和计算,INT(1)和INT(20)是一样的

分类: Mysql 标签:

计数排序

2012年3月20日 策马江湖 没有评论

计数排序有很大的局限性:适用于输入的数字都不大于k。

package algorithms;
public class CountSort {
	public static void main(String[] args) {
		int[] arr = new int[] {5, 2, 0, 3, 4, 0, 2, 5, 4, 1};
		int k = 6;
		int[] newArr = countSort(arr, k);
		for (int i : newArr) {
			System.out.println(i);
		}
	}
	public static int[] countSort(int[] arrA, int k){
		int aLen = arrA.length;
		int[] arrC = new int[k];
		for (int i = 0; i < k; i++ ) {
			arrC[i] = 0;
		}
		for (int i = 0; i < aLen; i++) {
			arrC[arrA[i]] ++;
		}
		for (int i = 1; i < k; i++) {
			arrC[i] += arrC[i-1];
		}
		int[] arrB = new int[aLen];
		for (int i = aLen - 1; i >= 0; i--){
			arrB[arrC[arrA[i]] - 1] = arrA[i];
			arrC[arrA[i]] --;
		}
		return arrB;
	}
}
分类: 算法 标签: ,