12

Ok, here is an issue: in the project i'm working on, we can't rely on server-side sessions for any functionality.

The problem is that common captcha solutions from preventing robotic submits require session to store the string to match captcha against.

The question is - is there any way to solve the problem without using sessions? What comes to my mind - is serving hidden form field, containing some hash, along with captcha input field, so that server then can match these two values together. But how can we make this method secure, so that it couldn't be used to break captcha easily.

4
  • 1
    Why can’t you rely on sessions?
    – Gumbo
    Commented Sep 23, 2009 at 12:35
  • I want to ask the same session. Also no sessions means no user identification at all. Your PHP script becomes stateless. What is captcha for in this system? Commented Sep 24, 2009 at 7:03
  • they put login in GET or POST variables ;)
    – naugtur
    Commented Apr 22, 2010 at 8:26
  • 2
    Maybe is just to send a "contact us" form.
    – Josejulio
    Commented May 28, 2015 at 22:39

13 Answers 13

4

The need for session or database comes from the need to coordinate the GET for the image with the html page containing it, so how about use the same code to embed a captcha image: [img src='data:image/jpeg;base64,...'], use a random salt to hash its text, then sending the random salt and hash together with the image to the client in a single GET?

On postback you append the user text to the salt then compare the hashes. Just wondering how safe this would be...

3

Use the honeypot technique: place a text field with a greedy name, as 'email', into an field hidden by CSS (display: none; visibility: hidden;).

When you have to sanitize the form, simply check if that field is empty, is being send by an human (that cant see the field and so cant fill it up), else, from a spammer.

That's why usually spammer use to fill up all the fields in the page with predefinited values before sending the form... and doesnt bother the user for reading the captcha.

Else, rely on the human reading, something like "Write the first $x letter of the word "$word" in the field:"

Then, you only have to send the $x and $word to the next page and check it (and of course, you can randomize the fields name to be more accurated)

I remember that a plugin for phpBB forum rely on the fact that, usually, the spam bots selects the first option avaiable (with a value) in the <select> fields; Just put as first option <option value="kickmeplease">Yes, im a bot.</option>

There are many ways to protect against spambots, playing on one factor that bots will never have: imagination

1
  • 2
    Honeypots aren't a particularly useful solution, except for obscure, low value targets. It's a form of security-through-obscurity. It doesn't solve for the custom-designed attack. Even for non-custom-designed attacks, it assumes bots won't develop the ability to identify invisible fields, which is fairly trivial. If honeypot was used by valuable targets, bots will be adjusted to interpret CSS and ignore invisible fields. True captcha resolves all situations, except the "human sweatshop captcha solver method" for which no protection exists (except maybe IP blacklisting).
    – Dan
    Commented Mar 6, 2015 at 17:46
2

You could try storing a bunch of captcha codes in a database. Alternatively, theres a nice discussion on alternate captcha methods here: Practical non-image based CAPTCHA approaches?

some pretty interesting techniques really, have a read through.

2

Without persistent state server-side, I don't see a CAPTCHA working.

What you suggested is not secure since an attacker could easily always POST his own 'hidden field' with matching CAPTCHA text.

Why not do the CAPTCHA from another webserver where you can have persistent state?

2
  • 1
    Yes, i'm aware of the fact that without some form of persistence on server, attacker can use the same correct pair of hash-captcha like 100 billion times. But what if we salt hash with the server's timestamp and lucky pairs will expire every 10-20 minutes, what do you think of the security of this method?
    – Anton N
    Commented Sep 23, 2009 at 12:58
  • It indeed offers an added level of security, but I fear the salt will not be random enough. One thing to consider is the level security you need. If you need 'some' security, but the likelyhood of becoming the target of an attack is low, you could try such a low security method. Commented Sep 23, 2009 at 13:31
2

Have the CAPTCHA generator return an image, and use a salted hash or custom hash for the answer (emphasis on salted/custom). Have the generator push that hash into a cookie. The server can then validate based on the value in the cookie. This wouldn't require JavaScript, but if cookies are disabled, you'd have to fallback to another technique.

7
  • 1
    The hash can be pushed into a hidden form field. That way you wouldn't need a cookie.
    – Matthew
    Commented Jun 9, 2013 at 22:24
  • @Matt this is a good idea but I think it's not safe. The attacker could use a known hash for a known result (e.g d9a263b9de1023a8cc... = "928137"), modify the hidden field and submit with the known result. Thus bypassing the captcha.
    – cherouvim
    Commented Mar 4, 2015 at 11:47
  • 2
    @cherouvim that's why you need to salt your hash ;)
    – Matthew
    Commented Mar 4, 2015 at 16:31
  • 1
    @matt if you give me a salted hash on the HTML source as a hidden input, and you also give me the respective captcha as an image, what stops me from "replaying" that exact hash together with the captcha value whenever I need to bypass the captcha?
    – cherouvim
    Commented Mar 4, 2015 at 19:50
  • 1
    @cherouvim that's a valid point. One solution for this problem is to use a nonce - a unique single-use token - that can only be validated one time, tied directly to the captcha answer. I would probably also use the nonce in my salting algorithm, which would mean that in addition to the nonce being single-use, the nonce token and captcha answer hash are fundamentally linked. Similar to XSRF prevention.
    – Dan
    Commented Mar 5, 2015 at 17:22
2

Auto-populate a UUID of the CAPTCHA along with the user answer in the POST. Easy peasy.

2

just make a math captcha ;) 2+90 = ? equation should be shown in an image and voila ;)

2
  • But you still have to store the equation server-side and identify the sender to verify it. It's not about the challenge, it's about the delivery. Commented May 24, 2020 at 22:12
  • I'm not sure what do you mean by 'store the equation server-side', the equation is returned by PHP, it's server-side anyway.
    – Rizerzero
    Commented Oct 27, 2020 at 23:04
1

Make your hidden input field just a random sequence. Store this random data in the database along with the captcha information, so you can look up the correct captcha with it.

You will also need to set a short-ish time to live for each captcha generated. Finally, you can store and track in the database the number of attempts on each captcha and impose a hard limit on it (3 guesses and it is a permanent fail).

1
  • 1
    That’s the basics of how sessions work. Store the data on the server side (your captcha information in the database) and associate it with an ID (your random sequence).
    – Gumbo
    Commented Sep 23, 2009 at 12:47
1

My own idea, don't know is it good:

1) If user is logged, just use some hash function on his login and generate CAPTCHA with it,

2)if it is register form, etc just hash some value from form field (for example login, when user finished type it) and by ajax show CAPTCHA with hash from login.

Hope, that it is understandable. :)

EDIT: Without AJAX: 2 steps registration:

At 1, we collect login etc. after submit, we direct to ?login=new_login

At 2, we have hidden input with GET["login"] and hash from it in CAPTCHA image - after submit we have all to check answer.

7
  • 2) Nice idea :) but on doing this we either block people with javascript off or allow all bots with no js engine to pass the captcha every time. Correct me, if i'm wrong.
    – Anton N
    Commented Sep 23, 2009 at 12:54
  • Bots will not pass captcha unless they know how you hash login :) Commented Sep 23, 2009 at 13:01
  • Yes, but real clients, who can't make ajax calls won't have their chance to pass either?
    – Anton N
    Commented Sep 23, 2009 at 13:07
  • Well, I like it, especially combined - make captcha on the fly for ajax-enabled clients and use plan b with two-steps form for all the others. Will take this method into consideration :)
    – Anton N
    Commented Sep 23, 2009 at 13:21
  • Will be nice if you don't forget vote +1 my answer or just mark it as solution, THX (I noticed you are new here) : - ) Commented Sep 23, 2009 at 13:33
0

Can you grant them a client certificate in response to a CAPTCHA call? Then once they select that certificate in the browser it's sent with each call from the client, and can be used for authentication without sessions and without further CAPTCHA calls.

1
  • It's a similar type of certificate, but it's held by the client instead of the server. With SSL, the server presents the certificate and the client accepts it. With client certificates, it's the reverse - the client presents (through the web browser) and the web server accepts. Once you pick a client certificate on the web browser side, that certificate is sent with every call (Request["ClientCertificate"] or some such), so you don't have to keep login state in session. It does add some hoops for the client to jump through, however, and may not work for you. Commented Sep 23, 2009 at 14:52
0

Here's my take at it (sry if it seems complicated):

  1. on page request:

    • you generate a random string code 'abcdef';
    • you encrypt the code using some predefined password: $crypt = encrypt($captcha_code, 'password')
  2. in the form:

    • an image link is sent to the browser 'captcha.php?$crypt'
    • a hidden input is set with the value of $crypt
  3. the captcha.php page decrypts the encrypted text, and generates the image.

  4. the user submits a form with code 'abcdaa' (and hidden input $crypt)

  5. the server verifies if encrypt('abcdaa') == $crypt

edit: the encrypt function needs to be reversible (decrypt), since the captcha image generator will need the original code.

5
  • the problem with this method is that the attacker can use one pair of values a lot of times.
    – Anton N
    Commented Sep 23, 2009 at 13:35
  • @Anton N: not exactly, if the password changes (just add a salt based on available information like user IP address, current day, browser info, or some other stuff) Commented Sep 23, 2009 at 14:29
  • I think this approach is worse than what Anton N suggested in the first place. Use a hash, obviously! hidden_field=hash(captcha + salt) and verify by hidden_field == hash(user_inputted_captcha + salt). Commented Sep 23, 2009 at 17:05
  • @Yannick M. The issue lies on the fact that the image generation script needs to know what text code to generate in the first place. without server-side use of session/db, the text must come from somewhere... Commented Sep 23, 2009 at 17:47
  • The image generation is server-side, and a hash of the text + salt is only to verify the result afterwards. The thing about encryption is, if an attacker can break it, they have the text + the password. Whereas they cannot 'break' the hash, they can only find possible collisions. Commented Sep 23, 2009 at 23:23
0

How about this solution? I found this "Sessionless PHP Captcha" article on google and I used on one of my projects, it's simple, no session and it's free. Any security concerns on RC4?

http://www.mythos-rini.com/blog/archives/732

0

Form with validation:

$errorsucc = '';

if (isset($_POST["captcha_check"])) {

    $code = str_decrypt($_POST["captcha_check"]);   

    if (empty($_POST['captcha_code'])) { 
        $errorsucc = '<p style="color:red">Please Enter the security code.</p>';

    } elseif(!( $code == $_POST['captcha_code'] && !empty($code) )) {
        $errorsucc = '<p style="color:red">Incorrect Code Entered.</p>';

    } else {
        $errorsucc = '<p style = "green">Nice, you entered the correct code.</p>';  
    }
}

$captcha = new CaptchaCode();
$code = str_encrypt($captcha->generateCode(6));
?>

<html>
    <title>Sessionless Captcha</title>
    <div style = "background: #e2e2e2; padding: 20px; width: 20%; box-shadow: 5px 5px #ccc;">
        <?php echo $errorsucc; ?>
        <form name="captchaform" method="post">
            <table border="0" cellpadding="4" cellspacing="0">
                <tr><td valign="middle" align="left">Security Code:</td>
                    <td valign="middle" align="left"><img src="captcha_images.php?width=150&height=50&code=<?php echo $code?>" /></td>
                </tr>
                <tr><td valign="middle" align="left">Enter Code:</td>
                    <td valign="middle" align="left"><input id="captcha_code" name="captcha_code" style="width:150px" type="text" /></td>
                </tr>

                <tr><td valign="top" align="left">
                    </td>
                    <td valign="top" align="left">
                        <input border="0" type="submit" value="Submit" />   
                    </td>
                </tr>
            </table>
            <input type="hidden" name="captcha_check" value="<?php echo $code?>" />
        </form>
    </div>
</html>

Generate images just like any other captcha:

/* font size will be 75% of the image height */
    $font_size = $height * 0.75;
    $image = @imagecreate($width, $height) or die('Cannot initialize new GD image stream');
    /* set the colours */
    $background_color = imagecolorallocate($image, 255, 255, 255);
    $text_color = imagecolorallocate($image, 0, 26, 26);
    $noise_color = imagecolorallocate($image, 25, 89, 89);
    /* generate random dots in background */
    for( $i=0; $i<($width*$height)/3; $i++ ) {
        imagefilledellipse($image, mt_rand(0,$width), mt_rand(0,$height), 1, 1, $noise_color);
    }
    /* generate random lines in background */
    for( $i=0; $i<($width*$height)/150; $i++ ) {
        imageline($image, mt_rand(0,$width), mt_rand(0,$height), mt_rand(0,$width), mt_rand(0,$height), $noise_color);
    }
    /* create textbox and add text */
    $textbox = imagettfbbox($font_size, 0, $this->font, $code) or die('Error in imagettfbbox function');
    $x = ($width - $textbox[4])/2;
    $y = ($height - $textbox[5])/2;
    imagettftext($image, $font_size, 0, $x, $y, $text_color, $this->font , $code) or die('Error in imagettftext function');
    /* output captcha image to browser */
    header('Content-Type: image/jpeg');
    imagejpeg($image);
    imagedestroy($image);

Download the demo files from this link: Create a Sessionless Captcha in PHP

Not the answer you're looking for? Browse other questions tagged or ask your own question.