[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Creating a token to verify a form's origin

Posted on 2011-10-11
15
Medium Priority
?
436 Views
Last Modified: 2013-12-12
I'd like to create a token, as a hidden input, to keep a form 'pure'. I'd like to prevent someone from altering the form. Here's an example:

- user loads page with form
- - before the page is sent to the user
- - a token is created // token = hash(time + salt) perhaps?
- -  token is stored in database (assuming there are no duplicates) with timestamp
- -  token and timestamp are inserted into the form as hidden inputs
- page is loaded

- user submits form
- - token and timestamp are submitted with form data
- - check database for token and timestamp
- - if exists, continue and remove row from database
- - if not exists, fail and start over with new token

What do you think? I'm looking for a best practice, is there a better way?
0
Comment
Question by:giandem
  • 5
  • 3
  • 2
  • +3
15 Comments
 
LVL 13

Expert Comment

by:themrrobert
ID: 36953915
That still leaves the ability to modify the form and pass the token you are freely handing out.

What is your SPECIFIC scenario?
0
 
LVL 1

Author Comment

by:giandem
ID: 36953936
Sorry, I left out a critical step:

- - The timestamp will be compared to the current time and will reject the token after x amount of time.
0
 
LVL 1

Author Comment

by:giandem
ID: 36953950
themrrobert, I don't have a specific scenario but I'd probably check the referer as well, even though I know it's not foolproof.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 84

Accepted Solution

by:
Dave Baldwin earned 2000 total points
ID: 36953998
While you can make that work, I don't see what it has to do with "altering the form".  Your PHP page that receives the form shouldn't be accepting anything not on the form anyway.  And 'hidden inputs' are visible in the "View Source" of the browser so the only people you will be hiding anything from are the simple users.  The timestamp and the token can be used to eliminate stupid postings from the spammers that post directly to your 'action page' and that has some value.

If there is money involved, the first thing is to use SSL/TLS to create a secure connection between the browser and the server.
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 36954045
Place the form behind HTTPS and use CAPTCHA to identify human input.  And accept only known good values.  That's really the right approach.  Trickery like creating form tokens increases complexity for you, but does nothing to dissuade determined attackers.  I would not waste my time on that.  I used to do those things, but found over time that they had no value at all compared to a very simple CAPTCHA.
0
 
LVL 1

Author Comment

by:giandem
ID: 36955875
Both of you are making some sense but I'm not yet convinced that you're right. By the way, I want to mention that I'm an amateur at PHP development, not quite a beginner and not yet an expert. Most of my posts here are for my own education and I often can't cite a specific scenario.

DaveBaldwin, the only reason that it's hidden is so it doesn't bother the user. The token would be invalid after the original user submits the form or would expire after a short period of time.

Ray Paseur, isn't a CAPTCHA just a token that bothers the user? Isn't it a similar concept? And, I suppose, complexity is a reasonable price for security.

Assume that the form data is valid and the connection is secure. Also assume that the user is logged-in.
0
 
LVL 1

Author Comment

by:giandem
ID: 36955898
I just checked the source here and I wanted to point out that EE uses a token on this form, perhaps for the same reason.
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 36956030
Many developers mistakenly believe that a form token somehow secures the form.  But if the token is stored in a hidden variable, it is no problem at all to use CURL, read the hidden inputs and send them back in the raw POST string.  In addition it's quite easy to make your CURL request look as if it coming from a client browser.  I can show you how to make your request look like it is a Firefox browser referred by Google, but that is beside the point.

Captcha is not really a similar concept at all.  In a captcha test, you store a value on your server and send a puzzle or a picture of the value to the client.  The client must look at the puzzle or picture and type a response.  The goal is to be sure that the client is really a human being.  While it's very easy to regurgitate the contents of a hidden form input, it's programatically much harder to tease out the necessary response to a random inquiry.

Here is a pair of scripts that I have used successfully.  A smart programmer could use a rainbow attack to crack this code in a matter of an hour or two, but that said, it has been adequate for my needs for quite a long time.  The first script generates an image.
<?php // RAY_captcha_image.php
error_reporting(E_ALL ^ E_NOTICE);


// GENERATES A PICTURE OF A NUMBER INTO THE BROWSER OUTPUT


// DECODE THE INCOMING STRING
$data = base64_decode($_GET['dt']);

// CREATE AN IMAGE RESOURCE - CHOOSE THE SIZE THAT BEST MATCHES YOUR PAGE STYLE
$im = imagecreate(46,13);

// WHITE BACKGROUND
$bg = imagecolorallocate($im, 255,255,255);

// GRAY STRIPES
$gray = imagecolorallocate($im, 188,188,188);

// FIREBRICK TEXT
$text = imagecolorallocate($im, 178,34,34);

// ADD THE NUMBER TO THE IMAGE
imagestring($im,5,4,0,$data,$text);

// WRITE A GRAY STRIPE (OR MORE IF YOU CHOOSE)
imageline($im,4,12,38,0,$gray);

// SEND THE IMAGE INTO THE BROWSER OUTPUT STREAM
header('Content-type: image/png');
imagepng($im);
imagedestroy($im);

Open in new window

And the second script demonstrates how this works when inserted into a form.
<?php // RAY_captcha_in_action.php
error_reporting(E_ALL);

// IF ANYTHING WAS POSTED
if (!empty($_POST))
{
    // TEST THE STRINGS
    if ($_POST["_newMd5"] != md5($_POST["_newCode"]))
    {
        // MIGHT WANT TO MAKE THIS USER-FRIENDLY
        echo 'SECURITY CODE NUMBER DID NOT MATCH';
    }
    else
    {
        echo "SUCCESS!";
    }
}
// END OF PHP - PUT UP THE FORM
?>
<form method="post">
<!-- STYLE THIS TO SUIT YOUR PAGE STYLE -->
Type <img style="display:inline;" src="RAY_captcha_image.php?dt=<?php $x = mt_rand(1000,10000); echo base64_encode($x); ?>" /> here:
<input name="_newCode" type="text"   maxlength="64" size="6" autocomplete="off" />
<input name="_newMd5"  type="hidden" value="<?php echo md5($x); ?>" />
<input type="submit" />
</form>

Open in new window

You can test it here.
http://www.laprbass.com/RAY_captcha_in_action.php

You can get much more complicated and annoying captcha images from the Gold Standard, reCaptcha.
http://en.wikipedia.org/wiki/ReCAPTCHA

HTH, ~Ray
0
 
LVL 34

Expert Comment

by:Beverley Portlock
ID: 36956063
Tokens can stop automated resubmission of the same form over and over again. One way is to embed the EXPIRY time and the ip address in the token. For example let us say that you want a form to 'live' for 20 minutes then create an token using MD5, the IP, the expiry and a secret salt string. Then you do this

$expires = time() + 20*60;
$token = md5( $expires . $_SERVER['REMOTE_ADDR'] . "my secret string");

You then bury two hidden fields in the form

<input type='hidden' name='exp' value='<?php echo $expires; ?>' />
<input type='hidden' name='tok' value='<?php echo $token; ?>' />

When the form is submitted you read the expiry time from the form, pick up the IP and regenerate the MD5

$test = md5( $_POST['exp'] . $_SEVER['REMOTE_ADDR'] . "my secret string");

and compare it to the form value. If it passes, then check the expiry time

$submissionOk = false;

if ( $test == $_POST['tok'] )
    if ( is_numeric( $_POST['exp'] )
          if ( intval( $_POST['exp'] ) >= time() )
               $submissionOk = true;

if ( $submissionOk ) {
     ... do stuff - form was valid
}



As the other posters have said, this mechanism has its limits but it does stop links being stored in bookmarks or passed around in email links.


0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 36956158
@bportlock: If the link is just to the web page that contains the form, then when I visit that page, I would expect to find a fresh new token and a new expiration time that I could use to send back to the server.  I might be visiting the page as a human being or as a carefully crafted CURL request, but the server-side script would not necessarily be able to tell the difference.  I think you are onto a good design idea if the form creates a GET method request, where the URL exposes the response.  But in the case of a POST response the arguments do not appear in the URL, so the likelihood of passing a POST response in an email link seems small.

What I have used form tokens for, and still do occasionally, is to prevent duplicate submissions of identical data.  But for keeping 'bots out of your forms, I think that nothing beats captcha.

Best to all, over and out, ~Ray
<?php // RAY_multi_submit.php
error_reporting(E_ALL);


// PREVENT MULTIPLE SUBMISSIONS DUE TO REPEATED REFRESH, CLICKS ON SUBMIT BUTTON OR FIRING THE BACK BUTTON
// EXAMPLE:
//    if ( multi_submit() )
//    {
//       handle error
//    }
//    else
//    {
//       normal processing
//    }


// ALWAYS START THE PHP SESSION ON EVERY PAGE
session_start();



// A FUNCTION TO RETURN TRUE OR FALSE ABOUT MULTI-SUBMIT CONDITIONS
function multi_submit($type="POST")
{
    // MAKE THE FUNCTION WORK FOR EITHER GET OR POST SUBMITS
    $input_array = (strtoupper($type) == "GET") ? $_GET : $_POST;

    // GATHER THE CONTENTS OF ALL THE SUBMITTED FIELDS AND MAKE A MESSAGE DIGEST
    $string = NULL;
    foreach ($input_array as $val)
    {
        // CONCATENATE ALL SUBMITTED VALUES
        $string .= $val;
    }
    $string = md5($string);

    // IF THE SESSION VARIABLE IS NOT SET THIS IS NOT A MULTI-SUBMIT
    if (!isset($_SESSION["_multi_submit"]))
    {
        // SAVE THE SUBMITTED DATA MESSAGE DIGEST
        $_SESSION['_multi_submit'] = $string;
        return FALSE;
    }

    // IF THE SESSION DATA MATCHES THE MESSAGE DIGEST THIS IS A MULTI-SUBMIT
    if ($_SESSION['_multi_submit'] === $string)
    {
        return TRUE;
    }
    else
    {
        // SAVE THE MESSAGE DIGEST TO DETECT FUTURE MULTI-SUBMIT
        $_SESSION['_multi_submit'] = $string;
        return FALSE;
    }
}



// SHOW HOW THIS IS DONE
if (!empty($_POST))
{
    if (multi_submit())
    {
        die("ALREADY GOT THAT");
    }
}



// CREATE THE FORM FOR THE DEMONSTRATION
$form = <<<FORM
<form method="post">
ENTER SOMETHING, THEN REENTER IT
<input name="foo" />
<input type="submit">
</form>
FORM;

echo $form;

Open in new window

0
 
LVL 35

Expert Comment

by:gr8gonzo
ID: 36956588
I mostly agree with Ray. I work for the company that handles customer support web portals for a lot of big names. If you're a normal consumer, I can all but guarantee that you've used the forms from our product, so form security is a topic that I deal with ALL the time.

Now, our most important forms most often require a person to be logged in before they are even shown, which helps negate a lot of attacks, but every form still has a token attached to help discourage attackers from trying bulk attacks.

A determined attacker COULD write a script that goes out and pulls the page, parses out the variables, and submits the form with the correct token, but that tends to be too time-consuming for most attackers because the page-pulling-and-parsing process slows down the entire attack. It turns the 1000-fire-and-forget-requests-per-second into a fraction of that. Each attack requires a full page load, an HTML parse, and THEN the next submission can occur.

We take it a step further by presenting the token data using Javascript, cookies, and sessions, which is harder for a script to process, since most scripts cannot parse Javascript (but again, a very determined hacker can still write scripts to get around it).

CAPTCHA eliminates all but the most determined of hackers, because CAPTCHA is so difficult for attack scripts to read. I don't think we've ever had any problems with sites that use both CAPTCHA and tokens, and in my experience, CAPTCHA alone is often enough. The key to successful CAPTCHA is -not- do what Ticketmaster does and present you with text so horribly disfigured, it looks like it watched The Ring video last week. I don't like trying to figure out what those letters are - I just want to proceed to the next step already!

If you make it easy enough for users to read (especially if you use actual words), chances are that they will do it without complaining. I see random math problems all the time ("What is 5 plus 2 plus 2?"), which is usually even easier for users and still hard for most scripts to process as long as your problem generation is random enough with its phrasing.

So I'd recommend using CAPTCHA but make it easy on your users. OCR is CPU-intensive enough to discourage bulk attacks, even with easy-to-read images.

Tokens are a nice additional layer, but unless you're protecting credit card numbers, social security numbers, or other PII, I'd just go with CAPTCHA.
0
 
LVL 34

Expert Comment

by:Beverley Portlock
ID: 36958976
We use captchas as well and we wrote a PHP class to generate them, but the form tokens which combine an expiry time combined with an IP address prevent scrapers from analysing the form and then passing it to bot networks.

Every little helps....
0
 
LVL 1

Author Comment

by:giandem
ID: 36960075
Ray Paseur, you've almost convinced me. I'm beginning to think that a token is a good idea but not for security.  I do believe that CAPTCHAs are (probably) mandatory for sign-up and log-in forms but probably not a good idea to use when posting in a forum or editing a social network profile.

What would you, and everyone else, consider a good 'blueprint' (minus the CAPTCHA) for securing forms? I'm not asking for code but rather some bullet points or pseudo-code.
0
 
LVL 84

Expert Comment

by:Dave Baldwin
ID: 36960310
The number one thing is to define and accept only good input.  You can use javascript form checking to let the user correct mistakes but you must also provide checking on the page that the form posts to.  Number two is use HTTPS secure connections which requires an SSL/TLS certificate for your website.  If you can require a login before the form, that helps also.

I believe Ray has a page that shows how to escape data that is going to be INSERTed into a database to prevent SQL Injection using mysql_real_escape_string http://us3.php.net/manual/en/function.mysql-real-escape-string.php .  His example pages are very organized, better than mine...

I like a lot of the ideas here and they can all help with different aspects.  For a posting on a forum, you need to make the database doesn't get corrupted so protection from SQL injection is important.  If there is money involved, the security requirements rise.  More security often means more complexity because you put up more obstacles in the way of someone who wants to get in where they don't belong.
0
 
LVL 35

Expert Comment

by:gr8gonzo
ID: 36963294
1. Your first step should be to make the form as easy-to-use as possible. This means minimizing the number of fields that have to be filled out in order to submit the form, making the form page load as quickly as possible (e.g. don't load a heavy rich HTML editing toolbar component if your users don't make a lot of use of it)

2. Your next step is to secure the form while minimizing impact on the easy experience you've created. A lot of security won't make a difference if nobody wants to fill out the form.

3. Make sure your form-processing script knows what fields to expect. While your form may only have 5 fields to POST, hackers might try to post 10 fields. In some cases, those extra fields may not make any difference, but should there be any future code that tries to process or access the entire POST, you run risks if you're not validating what fields should be processed.

4. Once you know the fields you're expecting, ensure that you're running some kind of data cleansing / sanitizing routine on them. Don't assume that just because the field "someID" should always be an integer, it actually will be one. Force IDs to be integers ($_POST["someID"] = intval($_POST["someID"]) and strip out or html-entitize unwanted characters in POSTed strings to help avoid SQL injection or XSS attacks (especially for forum-type code).

5. Limit your session lengths. Most people don't have a problem logging back into a resource that they want to access, and it limits the opportunity for someone to go steal a cookie somehow and gain access to someone's account.

6. Ask yourself a lot of questions. Most developers just assume things and leave security holes behind as a result. For example, treat any piece of data as if it's a wrapped present. Ask yourself who it's from and ask yourself if you would trust that person to always give you something that is good. Think up questions to ask while you code and then ask yourself those questions. It's a surprisingly good practice, even if it sounds foolish.

7. Use SSL when you can.

8. Keep your web server and PHP engine up-to-date, and make sure your server has a good amount of security (firewall, daily logs, automated IP denial upon attacks, etc...) . All the secure coding in the world won't help you if your system's rear end is exposed.
0

Featured Post

[Webinar] Cloud and Mobile-First Strategy

Maybe you’ve fully adopted the cloud since the beginning. Or maybe you started with on-prem resources but are pursuing a “cloud and mobile first” strategy. Getting to that end state has its challenges. Discover how to build out a 100% cloud and mobile IT strategy in this webinar.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article discusses four methods for overlaying images in a container on a web page
This article discusses how to create an extensible mechanism for linked drop downs.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …
Suggested Courses
Course of the Month19 days, 11 hours left to enroll

872 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question