1

私はこのスクリプトを長い間使用してきましたが、99%で完全に機能します。使い勝手が良く、使い続けたいと思います。

ただし、まばらなユーザーから、番号が正しい間はシステムがキャプチャ(間違ったコード)を受け入れないと言われることがあります。私は彼らのクッキー設定を調べたり、キャッシュをクリアしたりするたびに、これらの場合は何も機能していないようです。

したがって、私の質問は、このスクリプトのコードに、例外的な場合の誤動作を説明する理由がありますか?

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();フォームページとフォーム結果ページで呼び出されることに注意してください。

誰かがこのスクリプトで潜在的なエラーの原因を特定できれば、私はそれをいただければ幸いです!

PS:キャプチャスクリプトの欠点を認識しています。特定のボットがまだそれらを読み取ることができることを私は知っています。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つのクエリに注目しており、私たちが行っていることに対していくらかリソースを消費していると感じています。また、ユーザーが同じページを数回リロードすると(ただの嫌いな人になるため)、データベースはすぐにいっぱいになります。もちろん、これは次のフォーム送信時にすべて削除されますが、それでも、この可能な代替案を私と一緒に検討してもらえますか?

私は、一般的に暗号化/復号化すべきではないことを知っています。ただし、キャプチャには本質的に欠陥があるため(ボットの画像の読み取りが原因)、captcha.phpスクリプトに送信されるパラメーターを暗号化および復号化することでプロセスを簡素化できませんでしたか?

これを行った場合はどうなりますか(Alix Axelの暗号化/復号化の指示に従って):

1)次のようにランダムな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))));

2)パラメータ付きの暗号化された番号を画像スクリプトに送信し、非表示フィールドに保存します

<img src="/img/captcha.php?number="'.$encrypted.'" />
<input type="hidden" name="encrypted_number" value="'.$encrypted.'" />

3)キャプチャスクリプト内の番号($ _GETを介して送信された)を復号化し、そこから画像を生成します

$decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, md5($key), base64_decode($encrypted), MCRYPT_MODE_CBC, md5(md5($key))), "\0"); 

4)フォーム送信時に番号を再度復号化して、ユーザー入力と比較します$ decoded = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256、md5($ key)、base64_decode($ encode)、MCRYPT_MODE_CBC、md5(md5($ key)))、 "\ 0 ");
if($ _ POST ['norobot']!= $ decoded){echo'キャプチャエラー!'; 出口; }

同意しました。これは少し「隠すことによるセキュリティ」ですが、基本的なセキュリティを提供しているようで、かなり単純なままです。それとも、この暗号化/復号化アクションは、それ自体でリソースを大量に消費しますか?

誰かがこれについて何か意見がありますか?

4

2 に答える 2

3

2つの理由から、SESSION値のみに依存しないでください。

  • セッションが期限切れになる可能性があるため、場合によっては機能しません
  • ユーザーが同じページで別のタブを開くと、奇妙な動作が発生します

ある種のトークンを使用します:

  • フォームを出力するときにランダムなIDを生成し、予想される数(および現在の日付/時刻)とともにデータベースに配置します
  • このIDを使用して画像を生成します
  • IDを使用してフォームに非表示の入力を追加します
  • POSTを受け取ったら、データベースから期待値を取得して比較します
  • このトークンとすべての古いトークンを削除します(WHERE token == %token AND datetime < DATE_SUB(NOW(), INTERVAL 1 HOUR)たとえば)
于 2012-07-05T10:06:20.953 に答える
1

一部の訪問者がプロキシの背後にいる場合や、一部のファイルの二重要求を実行できるプラグイン/ソフトウェアがコンピューター上にある場合があります。私のプロジェクトの開発中にこれを発見し、完全に忘れてしまったChromeプラグインをいくつか持っていました。

ごく少数の訪問者に起こっているので、これが事実である可能性があります。問題をデバッグするために実行した手順は次のとおりです(これは開発環境であり、サイトで直接コードを変更できたことを忘れないでください)。

訪問者が問題を報告したら、「デバッグ」を有効にします。これは、キャプチャジェネレータの構成でデバッグアレイにIPを追加することを意味します。これは次のようになります。

  1. マイクロタイム形式で画像の生成時間を取得します。
  2. ファイルシステムのどこかに、キャプチャページへのすべてのリクエストを次のような形式でログファイルに書き込みます。ip| microtime | random_numbers
  3. ユーザーのIPアドレスからのリクエストのログをチェックし、互いに約10秒の範囲でクローズリクエストがあるかどうかを確認します。ある場合は、キャプチャページに2番目のリクエストを行っているものがあり、訪問者には表示されない新しいコードを生成しています。

また、ユーザーのキャッシュをクリアした後、ページを更新するたびにユーザーに異なる番号が表示されることを確認する必要があります。ブラウザ側で奇妙な動作が発生する可能性がありますが、それでも古いキャッシュコピーが表示される可能性があります(Firefoxで見られる場合は、キャッシュをクリアしてブラウザを再起動し、キャッシュを再度クリアすると正常に動作します)。

この場合、スクリプトに次のような単純な時間ベースの追加を行うことができます。

新しいキャプチャ画像を生成するときは、セッションでキャプチャ番号がすでに設定されているかどうかを確認してください。設定されている場合は、生成された時刻を確認し、たとえば10秒未満の場合は、同じ数値を表示します。10秒を超える場合は、新しい数字を表示します。このメソッドの唯一の注意点は、使用するたびにセッションでキャプチャ変数の設定を解除する必要があることです。

サンプルコードは次のようになります。

<?php

// begin generating captcha:

session_start();

if (
   empty($_SESSION['randomnr2']) // there is no captcha set
   || empty($_SESSION['randomnr2_time'])  // there is no time set
   || ( time() - $_SESSION['randomnr2_time']  > 10 ) // time is more than 10 secs
) {
   $randomnr = rand(1000, 9999);
   $_SESSION['randomnr2'] = md5($randomnr);
   $_SESSION['randomnr2_time'] = microtime(true); // this is the time it was 
                                                  // generated. You can use it 
                                                  // to write in the log file
}


// ...
?>
于 2012-07-05T10:13:34.940 に答える