当前位置: 首页 > 知识库问答 >
问题:

这个PHP验证码脚本有什么问题?

狄阳秋
2023-03-14
session_start();

$randomnr = rand(1000, 9999);
$_SESSION['randomnr2'] = md5($randomnr);

$im = imagecreatetruecolor(100, 28);
$white = imagecolorallocate($im, 255, 255, 255);
$grey = imagecolorallocate($im, 128, 128, 128);
$black = imagecolorallocate($im, 0,0,0);

imagefilledrectangle($im, 0, 0, 200, 35, $black);

$font = '/img/captcha/font.ttf';

imagettftext($im, 30, 0, 10, 40, $grey, $font, $randomnr);
imagettftext($im, 20, 3, 18, 25, $white, $font, $randomnr);

// Prevent caching
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past3
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

header ("Content-type: image/gif");

imagegif($im);
imagedestroy($im);
if(md5($_POST['norobot']) != $_SESSION['randomnr2']) {
    echo 'Wrong captcha!';
}

请注意,在表单页和表单结果页上调用session_start();

如果有人能在这个脚本中找出潜在的错误原因,我将不胜感激!

附注:我知道验证码脚本的缺点。我知道某些机器人仍然可以读出它们。我不想使用Recaptcha,因为它对我的用户来说太难了(不同的语言+很多次老用户)。我也知道md5很容易解密。

// Insert a random number of four digits into database, along with current time
$query   = 'INSERT INTO captcha (number, created_date, posted) VALUES ("'.rand(1000, 9999).'", NOW(),0)';
$result  = mysql_query($query);

// Retrieve the id of the inserted number
$captcha_uid = mysql_insert_id();

$output .= '<label for="norobot"> Enter spam protection code';
// Send id to captcha script
$output .= '<img src="/img/captcha/captcha.php?number='.$captcha_uid.'" />'; 
// Hidden field with id 
$output .= '<input type="hidden" name="captcha_uid" value="'.$captcha_uid.'" />'; 
$output .= '<input type="text" name="norobot" class="norobot" id="norobot" maxlength="4" required  />';
$output .= '</label>';

echo $output;
$font = '/img/captcha/font.ttf';

connect();
// Find the number associated to the captcha id
$query = 'SELECT number FROM captcha WHERE uid = "'.mysql_real_escape_string($_GET['number']).'" LIMIT 1';
$result = mysql_query($query) or trigger_error(__FUNCTION__.'<hr />'.mysql_error().'<hr />'.$query);
if (mysql_num_rows($result) != 0){          
    while($row = mysql_fetch_assoc($result)){
        $number = $row['number'];
    }
} 
disconnect();

$im     = imagecreatetruecolor(100, 28);
$white  = imagecolorallocate($im, 255, 255, 255);
$grey   = imagecolorallocate($im, 128, 128, 128);
$black  = imagecolorallocate($im, 0,0,0);

imagefilledrectangle($im, 0, 0, 200, 35, $black);
imagettftext($im, 30, 0, 10, 40, $grey, $font, $number);
imagettftext($im, 20, 3, 18, 25, $white, $font, $number);

// Generate the image from the number retrieved out of database
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past3
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header ("Content-type: image/gif");

imagegif($im);
imagedestroy($im);

表单的结果

function get_captcha_number($captcha_uid) {
    $query = 'SELECT number FROM captcha WHERE uid = "'.mysql_real_escape_string($captcha_uid).'" LIMIT 1';
    $result = mysql_query($query);
    if (mysql_num_rows($result) != 0){          
        while($row = mysql_fetch_assoc($result)){
            return $row['number'];
        }
    } 
    // Here I would later also enter the DELETE QUERY mentioned above...
}
if($_POST['norobot'] != get_captcha_number($_POST['captcha_uid'])) {
    echo 'Captcha error'
    exit;
}

这工作得很好,所以非常感谢这个解决方案。

然而,我在这里看到了一些潜在的缺点。我注意到至少4个查询,感觉有点资源密集,我们正在做的事情。另外,当一个用户多次重新加载同一个页面时(只是为了成为一个混蛋),数据库会很快填满。当然,在下一次提交表格时,这些都将被删除,但尽管如此,你能和我一起检查一下这个可能的替代方案吗?

$key = 'encryption-password-only-present-within-the-application';
$string = rand(1000,9999);
$encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $string, MCRYPT_MODE_CBC, md5(md5($key))));
<img src="/img/captcha.php?number="'.$encrypted.'" />
<input type="hidden" name="encrypted_number" value="'.$encrypted.'" />
$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($encrypted), MCRYPT_MODE_CBC, md5(md5($key))), "\0"); 

4)再次解密表单提交上的数字,以与用户输入$decrypted=rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256,md5($key),base64_decode($encrypted),MCRYPT_MODE_CBC,md5(md5($key))),“\0”)进行比较;
if($_post['norobot']!=$decrypted){echo'captcha error!‘;exit;}

同意,这有点“通过模糊实现安全”,但它似乎提供了一些基本的安全,并且仍然相当简单。或者这种加密/解密操作本身是否会占用太多资源?

有人对此有什么意见吗?

共有1个答案

孙正业
2023-03-14

不要只依赖会话值,原因有二:

  • 您的会话可能过期,因此在某些情况下无法工作
  • 如果用户打开具有相同页面的另一个选项卡,则会出现奇怪的行为

使用某种令牌:

    null
 类似资料:
  • 我试图做一个代码,将打印所有元素的内容与从一些链接,但它不起作用,我不知道为什么,这是代码: 当我粘贴“someurl”而不是

  • 问题内容: 我已经为Employee类的父类是抽象的并且父类中的clone()方法是抽象的编写了此克隆方法。我想用此代码复制Employee对象的原始数据类型,而不是复制每个原始数据单独键入,但是此代码在我调用clone()方法的行中有问题。(此代码在Employee类中) 错误是:来自对象类型的方法clone()不可见。 但是我的Employee类在类层次结构中,可以访问Object类中受保护的

  • 我有一个关于书库的问题...我想写一个有3个堆栈的程序,我想在每个堆栈上添加这些操作(我应该使用数组): 1.创建堆栈2。按3号。流行音乐4号。显示每个堆栈的顶部 我写的程序,但我遇到了这些错误: 错误4错误LNK2019:未解析的外部符号“public:int\u thiscall stack::IsFull2(void)”(?IsFull2@stack@@QAEHXZ)在函数“public:v

  • 当我尝试执行代码时,我收到一个无效的密码错误。我想提及的是,我也在使用相同的密码功能进行注册。但是,当我在登录代码中实现它时,我并没有获得成功。请告诉我我做错了什么,并帮助我修复错误。 另一件事是,我输入的密码与注册演示用户时使用的密码相同/正确。这是登录的代码。

  • 我是GraphQL的新手,我正试图做一个突变来从我的数据库中删除一篇文章,但我不知道如何删除。我使用Node.js,猫鼬和GraphQL。 这是我的模式的变异。 这是我在调用API删除文章时使用的查询。 我做错了什么? 我收到一个错误的400请求。消息:“无法查询类型为“变异”的字段“删除”