提问

问题1:应该如何正确使用图形验证码?

问题2:应该如何正确使用短信验证码?

问题3:应该如何正确使用邮件验证码?

课程单元

  1. 一个图片验证码的例子

  2. 错误一:验证码为空

  3. 错误二:验证码太简单

  4. 错误三:在客户端校验验证码

  5. 错误四:验证码保存在cookie中

  6. 错误五:没有及时销毁验证码

  7. 错误六:验证码图片文件名漏洞

  8. 错误七:验证码没有跟账户绑定

1. 一个图片验证码的例子

下面是一个简单的图片验证码:

验证码:
<input type="text" placeholder="请输入验证码" name="code" />
<img src="code.php" />

下面是图片验证码的简单php代码:

<?php
$code = rand(1000, 9999);
$_SESSION["code"] = $code;
//下面是根据code生成图片的代码(略)

下面是检查验证码是否正确的简单php代码:

<?php
if($_GET["code"] == $_SESSION["code"]){
    echo "验证码正确";
}
else{
    echo "验证码不正确";
}

2. 错误一:验证码为空

验证码是在图片中生成并且存放进session中,因此,只要我们不请求图片,则session为空,如果我们直接使用浏览器访问提交页面,例如:xxx.php?code=,传入code为空,则满足了$_GET[“code”] == $_SESSION[“code”]的条件,成功跳过了验证码判断。

解决办法:必须判断$_SESSION[“code”]是否为空。

<?php
if($_GET["code"] == $_SESSION["code"] && $_SESSION["code"] != ""){
    echo "验证码正确";
}
else{
    echo "验证码不正确";
}

3. 错误二:验证码太简单

<?php
$code = rand(1000, 9999);
$_SESSION["code"] = $code;

4位数随机码,对于找回密码等重要场合,太过于简单,用户很容易通过反复提交、暴力破解等方式进行破解。

解决办法:对于找回密码等重要场合,验证码必须足够长、足够复杂,如果采用md5方式生成验证码,则原始字符串应该是字母加数字加特殊符号,10位以上的长度,才能确保相对安全。

4. 错误三:客户端校验验证码

类似如下在客户端判断验证码的方式,非常容易被表单提交欺骗攻击的方式跳过验证码校验步骤,而直接提交服务器,因此,验证码校验步骤必须在服务器端进行。

<script type="text/javascript">
    function emp(){
        if($("#code").val() != $("#hidCode").val()){
            alert("验证码错误");
            return false;
        }
        return true;
    }
</script>

6. 错误五:未及时销毁验证码

对于下面的判断程序,当验证码不正确时,没有及时销毁验证码,$_SESSION[“code”]中的值会一直保持不变,导致用户可以通过反复提交的方式,试出来验证码是多少。比如对于4位数验证码,用户可以使用循环,从0000到9999逐个测试,很快就能测试出来验证码是多少。

解决方法:验证码校验错误时,必须及时销毁验证码,用户必须重新获取验证码,增加用户循环测试的难度。

<?php
if($_GET["code"] == $_SESSION["code"] && $_SESSION["code"] != ""){
    echo "验证码正确";
}
else{
    unset($_SESSION["code"]); //需要增加销毁验证码
    echo "验证码不正确";
}

7. 错误六:验证码文件名泄漏

例如,对于下面这种图形验证码,很容易通过查看源代码的方式,发现验证码是多少。

解决方法:验证码图片文件名不能是有实际含义的文件名,必须是用户无法理解含义的文件名,比如code.php这种没有特别含义的文件名。

<img src="images/code/4.gif" border="0" >&nbsp;
<img src="images/code/4.gif" border="0" >&nbsp;
<img src="images/code/3.gif" border="0" >&nbsp;
<img src="images/code/9.gif" border="0" >&nbsp;

8. 错误七:验证码未绑定账户

例如,找回密码验证码,验证码校验成功后,用户可以修改密码。类似下面的代码,用户可以传递任意的userId,导致可以修改任意用户密码。

<?php
if($_GET["code"] == $_SESSION["code"] && $_SESSION["code"] != ""){
    echo "验证码正确";

    //执行修改密码操作
    $sql = "update user set password = '" . $_GET["password"] . "' where userId = '" . $_GET["userId"] . "'";
    mysql_query($sql);
}
else{
    unset($_SESSION["code"]); //需要增加销毁验证码
    echo "验证码不正确";
}

解决办法:对于修改密码等与账户相关的操作,userId等关键参数,必须与验证码相关联,而不能是用户随意输入的参数。关联方式可以用数据库、session等关联,但不能是cookie,原因同错误四,用户很容易获得和修改cookie的值,构建虚假的关联关系,从而导致关联无效。

<?php
<?php
if($_GET["code"] == $_SESSION["code"] && $_SESSION["code"] != ""){
    echo "验证码正确";

    //执行修改密码操作
    $sql = "update user set password = '" . $_GET["password"] . "' where userId = '" . $_SESSION["userId"] . "'";
    mysql_query($sql);
}
else{
    unset($_SESSION["code"]); //需要增加销毁验证码
    echo "验证码不正确";
}