PHP网站的安全性问题

针对PHP的网站主要存在下面几种攻击方式:

1、命令注入(Command Injection)

2eval注入(Eval Injection)

3、客户端脚本攻击(Script Insertion)

4、跨网站脚本攻击(Cross Site Scripting, XSS)

5SQL注入攻击(SQL injection)

6、跨网站请求伪造攻击(Cross Site Request Forgeries, CSRF)

7Session 会话劫持(Session Hijacking)

8Session 固定攻击(Session Fixation)

9HTTP响应拆分攻击(HTTP Response Splitting)

10、文件上传漏洞(File Upload Attack)

11、目录穿越漏洞(Directory Traversal)

12、远程文件包含攻击(Remote Inclusion)

13、动态函数注入攻击(Dynamic Variable Evaluation)

14URL攻击(URL attack)

15、表单提交欺骗攻击(Spoofed Form Submissions)

16HTTP请求欺骗攻击(Spoofed HTTP Requests)

以后的每期连载,会逐个介绍这些漏洞的原理和防御方法。

几个重要的php.ini选项。

RegisterGlobals

//php>=4.2.0b;选项的默认值预设为on

当register_globals的设定为On时,程序可以接收来自服务器的各种环境变量,包括表单提交的变量,而且由于PHP不必事先初始化变量的值,从而导致很大的安全隐患。

1:

//check_admin()用于检查当前用户权限,如果是admin设置$is_admin变量为然后下面判断此变量是否为
然后执行管理的一些操作。


<?php

//ex1.php
if(check_admin())
{
$is_admin=true;
}
if($is_admin)
{
do_something();
}
?>

这一段代码没有将$is_admin事先初始化为Flase,如果register_globalsOn,那么我们直接提交ex1.php?is_admin=true,就可以绕过check_admin()的验证:


<?php

//ex2.php

if(isset($_SESSION["username"]))
{
do_something();
}
else
{
echo"您尚未登录;
}
?>

当时,我们提交,就具有了此用户的权限所以不管register_globals为什么,我们都要记住,对于任何传输的数据要经过仔细验证,变量要初始化。

safe_mode

安全模式,用来限制文档的存取、限制环境变量的存取,控制外部程序的执行。启用

安全模式必须设置中的safe_mode=On

1、限制文件存取

<pre>safe_mode_include_dir="/path1:/path2:/path3";

不同的文件夹用冒号隔开

2、限制环境变量的存取

safe_mode_allowed_env_vars=string;

指定PHP程序可以改变的环境变量的前缀,如:safe_mode_allowed_env_vars=PHP_ ,当这个选项的值为空时,那么php可以改变任何环境变量

safe_mode_protected_env_vars=string用来指定php程序不可改变的环境变量的前缀。

3、限制外部程序的执行


safe_mode_exec_dir=string;

此选项指定的文件夹路径影响systemexecpopenpassthru,不影响shell_exec


disable_functions=string;

不同的函数名称用逗号隔开,此选项不受安全模式影响。

magicquotes

用来让php程序的输入信息自动转义,所有的单引号(),双引号(),反斜杠(\)和空字符(NULL),都自动被加上反斜杠进行 转义magic_quotes_gpc=On用来设置magicquotesOn,它会影响HTTP请求的数据(GETPOSTCookies)程 序员也可以使用addslashes来转义提交的HTTP 请求数据,或者用stripslashes 来删除转义。

推荐PHP新手入门需要的工具书

对于PHP新手入门或者老鸟,都必须要要的一本PHP入门手册,上面记载了PHP的全部包括他的历史

PHP手册截图

PHP手册截图

下载地址:点击下载

 

关于网站的编码问题

做中文网站,最的问题,就是编码问题,如果解决不了,那就根本没办法继续了

最常见的两种中文编码:UTF-8,GBK2312 (ps:这两者之间的关系和发现历史就不阐述了,因为不谈论的重点)

反正就要明确一点,从页面,到编辑器,再数据库统一编辑就行。

   怎么统一编码?

(1)   设计编辑器编码,你要先确定一种编码,就用UTF-8为例,我用的是dreamweaver 8 这个编辑器

操作流程:修改-> 页面信息->标题/编码 ->编码选择UTF-8 再点击应用

(2)  设计数据库编,用的是MYSQL数据库。可以在图形界面上面设计编码,设计编码为utf8_Unicode_ci

用代码实现:

1.单独设计某个数据库的编码

alter database testdb character set utf8;
     

testdb 是数据库名称,可以跟你需要设置编码的数据库进行替换,alter是一个SQL内置函数,以后在SQL语法操作中也讲得,一个功能很强大的函数

设计网站页面编码,可以用两种方式实现一种是是用HTML代码实现


<meta http-equiv="content-Type" content="text/html; charset=UTF-8" />

另一种是设计PHP代码实现


header("Content-type:text/html;charset='utf-8'");

上面分别用到了HTML代码中的Meta标签和PHP中的header()函数,他们的功能远远不止只有这些,以后再讲。

下面是一个最关键的问题,将讲数据从PHP页面写到数据库还是有乱码为什么?

因为在做数据库连接的时候,你忘记设计编码了,


mysql_connect(); //连接数据库,填写MYSQL的用户名和密码

mysql_query("set charset=''utf8"); //注意这个UTF8 中间没有连接符号‘-’; 为什么?

//因为这个数据库设计的编码方式,你看上面你就知道了

好了这样,就可以解决PHP和MYSQL之间中文数据交换的乱码问题了,简单明了就是统一编码。

PS:access数据库的默认编码是GBK2312。用IIS连接的时候也是这个编码。

PHP字符串操作函数


1.  echo,print,printf,sprintf, print_r

用于输出字符串.字符串中如果有变量名则被替换成其值.后两个函数类似于C的同名函数,print_r专门用来打印数字,跟函数dump/var_dump功能相同


2. strchr, strlen, strtok, strrchr, strrev, strstr,

strtolower, strtoupper, substr, ucfirst

用的字符串操作函数,有些和C中的同名函数意义完全一致. strrev是把一个字符串翻转. strtolower和strtoupper是将字符串转换为小写和大写.ucfirst是把字符串的第一个字符变成大写. substr 是返回字符串的一个子串,用法是:substr(字符串,头,长度).头位置是从0算起的. 如果是负数,则是从尾部向前数的意思.


3. Chr,Ord

同名函数. Ord是将字符串转换成ASCII码值,Chr函数功能相反


4. explode,implode,join

与数组有关的函数.explode(字符串,分割符)返回一个将字符串在分割符处分开所产生的数组.implode( 数组,分割符)返回一个将数组各元素之间插上分割符而成的字符串.join与implode意义相同.


5. Chop,trim,rtrim.ltrim

Chop处理串尾部的空白,trim除去字符串首部尾部的空白,rtrim除去字符串右边的空格,ltrim除去左边的空格


6. htmlspecialchars

将HTML特殊字符换成它们的名字,例如”$#@60;”变成”$#@60;”.


7. nl2br

在HTML中的每一个回车前面加上双引号(””).


8. AddSlashes,StripSlashes

给字符串中按照需要加上”\”和去掉”\”,对于某些数据库,必须在要查询的字符加上和去掉”\”之后才能够查询.


9. parse_str

将”name1=value1LRname2=value2LR…”类型的字符串分析成一些变量.
例如: parse_str(“a=1LRb=2″);生成$a与$b两个变量,值分别为1,2.如果有两对名字/ 值的名字部分相同,则后一个的值覆盖前一个的.如果这两对的名字尾部都有”[]”,例如”a[]=1LRa[]=2″, 则生成数组$a.

远程抓取(小偷程序)

纠结了N久,终于今天把小偷程序写出来了。但出现了还有很多问题,就要是结合了一个插件。我下面把代码特出来


<?php
set_time_limit(0);  //设置运行超时时间,0表示不设置
ini_set('memory_limit','256M'); //设置内存超限,这里设的256M
function init($num){
$i = $num/10;
for($k=1;$k<=$i; $k++)
{
$u = $k.".html";
require_once("simple_html_dom.php"); //加载PHP抓取插件
$result = file_get_contents("http://www.phpchina.com/category/list-357-60-".$u);
$url = array();
$pattern = '/<h3><a href="\/archives\/view\-\d{5}\-\d{1}.html">.+<\/a><\/h3>/Uis';
preg_match_all($pattern,$result,$arr);
foreach($arr[0] as $key=>$value)
{
$pattern_1 = '/\/archives\/view\-\d{5}\-\d{1}.html/Uis';
preg_match_all($pattern_1,$value,$arr);
if(!empty($arr[0]))
{
$arr_url = 'http://www.phpchina.com'.$arr[0][0];
array_push($url,$arr_url);
}
//gettitle($value);
}
foreach($url as $val)
{
$html = file_get_html($val);
$array['content'] = $html->find("table",0)->plaintext;
$array['title'] = $html->find('div.articleInfo',0)->find('h3',0)->plaintext;
save($array);
}
}
}

// 数据存储函数
function save($value)
{
require_once("mysql.class.php");
$content = htmlspecialchars(trim($value["content"]));
$title = $value["title"];
$author = 'innlab';
$time = time();
$sql = "INSERT INTO artcle(content,author,time,title) VALUES ('{$content}','{$author}','$time','{$title}')";
$query = mysql_query($sql) or die(mysql_error());
if($query)
{
echo "抓取成功<br/>";
}
else
{
echo "抓取失败<br/>";
}
}
//住函数
init(100);
?>

数据库里面就有数据了。有效问题,如果文章中有代码的话,抓取是不成功的。所以我的代码只适合新闻远程抓取。

蛋疼。

thinkPHP远程抓取数据批量存入数据库

今天做了一个抓取一组数据,批量存入数据库的实例。

开始的想法:

把数据抓过来,再循环存入数据库中,但发现一个问题,有时候远程页面数据更新了,这样可以要增加新的数据,也可以要删除不要数据,还有可以只更新。

   针对这三种情况分别做了下面的设计(下面只是几个核心的对象,不是完整的代码,如果需要完整代码请留言)


public function save_array($array)
{
//获取数组中数据的条数
$tmp_array = array();
$tmp_array_2 = array(); //数据存储数组
$oj = M("oj_question");
$sql_url = $oj->getField('id,url'); //数组的键名是ID,值是URL,这样下面的操作就方便了
$sql_url_num = count($sql_url);
$num = count($array);
if($sql_url_num > $num)
{
$this->delete_question($num,$sql_url_num);
foreach($sql_url as $key => $value )
{
array_push($tmp_array,$array[$key-1]);  //将改变了数据存储到过渡数组中
$this->update_question($tmp_array);
}

}
else if($sql_url_num == $num)
{
foreach($sql_url as $key => $value )
{

if( $value[$key-1] != $array[$key-1] )
{
// $value[$key] = $array[$key];
array_push($tmp_array,$array[$key-1]);
}
else
{
array_push($tmp_array,$value[$key]);
}
}
dump($tmp_array);
$this->update_question($tmp_array);
}
else{
//表示有数据要添加
$more = $num - $sql_url_num;
//更新已经有的数据
foreach($sql_url as $key => $value )
{
if( $value[$key-1] != $array[$key-1] )
{
// $value[$key] = $array[$key];
array_push($tmp_array,$array[$key-1]);
}
else
{
array_push($tmp_array,$value[$key-1]);
}
}
$this->update_question($tmp_array);
//添加新加的数据
for($i=$sql_url_num+1;$i<$num;$i++)
{
array_push($tmp_array_2,$array[$i]);
}
$this->add_question($tmp_array_2,$sql_url_num);
}
}

上面只是分类处理跟着情况,还没有跟数据库管理清理

下面是更新数据的代码


//更新数据库
public function update_question($tmp_array)
{
$url = 'http://openoj.awaysoft.com/JudgeOnline/';
$oj = M("oj_question");
foreach($tmp_array as $key => $value)
{
$data["url"] = $url.$value;
$data["time"] = time();
$oj->where("id=".$key)->data($data)->save();
}
}

删除数据里多余数据的代码


//删除多余数据并优化数据
public function delete_question($start,$end)
{
$oj = M("oj_question");
for($i=$start;$i<=$end;$i++)
{
$oj->where("id=".$i)->delete();
}
}

PS: 删除数据后,MYSQL里面会有残留的字节,但是数据库优化还不会,先留下这个问题,以后再解决。

新曾数据


//新增数据
public function add_question($array,$num=NULL)
{   //参数设计是因为项目需要,$NUM参数的默认值是空。
$oj = M("oj_question");
$url = 'http://openoj.awaysoft.com/JudgeOnline/';
foreach($array as $key => $value)
{
$num = $num + $key;
$data["url"] = $url.$value;
$data["name"] = "promble".$num;
$data["time"] = time();
$oj->add($data);
}
}

思想很简单,实现很蛋疼。做了一天,开始就这数据批量存储老存问题。后来发现是代码写的不够细致,照成了一些不必要的BUG.

PS: 把问题都思考到位,是写好代码的不二法门。

jquery+thinkphp的跨域抓取数据

         今天做一个远程抓取数据的功能,记得jquery可以用Ajax远程抓取,但不能跨域。再网上找了很多。但我觉得还是来个综合的,所以我现在觉得有点把简单问题复杂化了,但至少目前解决了:

      跨域抓取数据到本地数据库再异步更新的效果

         我实现的方式:jquery的$.post发送数据到服务器后台,在由后台的PHP代码执行远程抓取,存到数据库ajax返回数据到前台,前台用JS接受数据并显示。

 //远程抓取获取数据
  $("#update_ac").click(function() {
            $username = $("#username").text();
			$("#AC,#rank,#Submit,#solved,#solved2,#solved3").ajaxStart(function(){
						$(this).html("<span class='loading2'>&nbsp;&nbsp;&nbsp;</span>");
										});
			$.post("update_ac/username/"+$username,{},function($data)
									{
			   json = eval("(" + $data + ")");
			   $("#Submit").html(json.data.Submit);
			   $("#AC").html(json.data.AC);
			   $("#solved,#solved2,#solved3").html(json.data.solved);
	           $("#rank").html(json.data.rank);
								    }
					   ),"json";
                                 });

上面的jquery代码还算四楼比较清楚的,纠结我的就是那个json数据的接收

   json = eval("(" + $data + ")"); //eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。

其实这个都还是前台,跨域抓取是用php的扩展simple_html_dom完成的(不清楚的可以到网上去搜一搜,基于PHP5开发的)
把远程的页面抓取到本地。

import("@.ORG.simple_html_dom"); //thinkphp内导入扩展,你要把网上下载的代码改名为simple_html_dom.class.php放到APPNAME\Lib\ORG的目录下面
$html = file_get_html('http://openoj.awaysoft.com/JudgeOnline/userinfo.php?user='.$username); //远程抓取了
$ret = $html->find('center',0)->plaintext; //返回数据了。

上面的代码只是核心代码,simple_html_dom扩展的还有很多功能。自己去了解吧。
返回的数据是一个字符串,再用正则表达式去筛选需要的数据就了 下面是效果图

跨域抓取效果图

跨域抓取效果图

thinkphp 自动验证,自动添加,表单错误等问题

最近再做一个项目,想用thinkphp写验证,结果泪奔了几天。一开始就是令牌错误,后来有什么自动添加无效。

一直在测试,一直在查找,知道发现create()方法原来有两个参数,

第一个参数是大家都知道了数据参数,第二个是隐藏的$type参数,这个参数用来控制什么的呢??

 



//$type = $type?$type!empty($data[$this->getPk()])?self::MODEL_UPDATE:self::MODEL_INSERT);

仔细琢磨了这句话才发现,这个隐藏参数是用来指明本次数据库具体是什么操作的?

  $type取值为  1即为插入操作,0即为更新操作默认的情况下是不用给这个参数赋值的,原因是,系统能自动识别。

如果你的主键是数据库自动的添加的,那就没事。如果是手动添加,那就悲剧了。因为默认是更新数据操作。

下面是我写的自动完成的代码


protected $_auto = array(
array('password','sha1',1,'function'),
array('date', 'time', 1, 'function'),
);


看到那个1了没有,那个1表示是当插入数据是,才执行函数。PS:就是因为这个,纠结了我N久。

当要录入数据的时候您可以这么写:


create($_POST,1)【插入数据】

create($_POST,2)【更新数据】

直接告诉create方法此次操作是插入操作,这样可以解决自动完成时机不对问题,自动完成无效问题。

但你执意不要这么写,我也没办法,你只要记得,如果create()函数,默认为更新数据。写Model方法时。

 

thinkphp 自动完成图

thinkphp 自动完成图

看到没有,


protected $_auto = array(
array('password','sha1',2,'function'),
array('date', 'time', 2, 'function'), //把1换成2就好了。
);

问题就可以解决了。哈哈~~

THINKPHP框架留言版

奋斗了一天,终于THINKPHP小邓留言版的概念版出来了[/偷笑]

其实真的THINKPHP开发速度很快,作为一个互联网上“搬砖”的,从事这种 纯码农的事也是无可厚非的。

代码就实现了如下公告

1.留言功能。

2.验证功能。

3.分页显示功能。

就是写了几行代码(PS:页面设计代码不算,就算控制器和模型的代码)

下面我公布一下控制的器的代码,关于THINKPHP的代码规则我就不阐述了,看thinkphp手册就可以了。

class IndexAction extends Action
{
    public function index() {
       $Form = M("word");
        // 按照id排序显示前6条记录
	    import("@.ORG.Page");       //导入分页类
            $count = $Form->count();    //计算总数
            $p = new Page ( $count, 1 );
            $list=$Form->limit($p->firstRow.','.$p->listRows)->order('id desc')->findAll();
            $page = $p->show ();
            $this->assign ( "page", $page );
            $this->assign ( "list", $list );
	    $this->display(); //模板调用,这个是关键。
	}
	//数据插入
	public function insert() {
        $word = D("word");
      	 if($vo = $word->create())
		     {
		         if(false !== $word->add())
		        {
		             $this->success("数据添加成功");
		         }
		         else
		         {
		            $this->error('数据写入错误!');
		         }
		     }
		else
		    {
		      $this->error($word->getError());
		    }
                         }
   //验证重复
   public function checkTitle()
   {
       if (!empty($_POST['username'])) {
            $Form = M("word");
            //getByTitle是model的获取数据根据某字段获取记录的魔术方法
            //比如getById etc getByXXX XXX大写
            if ($Form->getByUsername($_POST['username'])) {
                $this->error('<font color=red>标题已经存在</font>');
            } else {
                $this->success('标题可以使用!');
            }
        } else {
            $this->error('标题必须');
        }
   }
}

下面是验证模型的代码

     class wordModel extends Model{
      protected $_validate = array(
	   array('username', 'require', '称呼必须!', 1),//1为必须验证
       array('email', 'email', '邮箱格式错误!', 2),//2为不为空时验证
	   array('qq','number','QQ号错误',2),
       array('content', 'require', '内容必须',1),
	   array('username','','称呼已经存在',0,'unique',1)
	  );
	  protected $_auto = array(
	    array('datetime', 'get_date',1, 'callback'),
		array('ip','getip',1,'callback')
	  );
	 protected function get_date()
	 {
	    return date("Y-m-d H:i:s");
	 }
	 protected function getip()
	 {
	    return $_SERVER['REMOTE_ADDR'];
	 }
  }

下面是留言班体验链接 点击阅览留言板
thinkphp有一个要注意的,在CURD操作中,都规定要用表名。

thinkphp第一天

今天总算有时间学thinkphp框架了,其实老早以前就有这个打算了,问题就在于人太蠢了。从面向过程直接到框架,一下转变不过来。所以花了一点时间去学PHP的面向对象编程。

写了一些CLASS之类的东东,以后再陆续公布面向对象写的东西和一些经验。

今天一开始就遇到一个问题,一直系统报错误。修改了配置文件没有效果。弄个了好长一段时间

system_error

system_error

后来才发现。是thinkphp的缓存机制。第一次加载框架的时候,就在runtime文件夹下面生成了一个~runtime的php文件。所以你更新了convention.php文件没有效果。所以把那个~runtime.php文件删了就可以了。

打算用thinkphp框架写一个留言板。发现无限的方便。

下面我把我今天看的入门视频贴出来。十分有用,我就不多少了,看完就能入门了。