Secure Your Forms With Form Keys

Secure Your Forms With Form Keys

Security is a hot topic. Ensuring that your websites are secure is extremely important for any web application. In fact, I spend 70% of my time securing my applications. One of the most important things we must secure are forms. Today, we are going to review a method to prevent XSS (Cross-site scripting) and Cross-site request forgery on forms.

Why?

POST data can be sent from one website to another. Why is this bad? A simple scenario…

A user, logged into your website, visits another website during his session. This website will be able to send POST data to your website — for example, with AJAX. Because the user is logged in on your site, the other website will also be able to send post data to secured forms that are only accessible after a login.

We also must protect our pages against attacks using cURL

How Do We Fix This?

With form keys! We’ll add a special hash (a form key) to every form to make sure that the data will only be processed when it has been sent from your website. After a form submit, our PHP script will validate the submitted form key against the form key we’ve set in a session.

What We Must Do:

  1. Add a form key to every form.
  2. Store the form key in a session.
  3. Validate the form key after a form submit.

Step 1: A Simple Form

Login Form

First we need a simple form for demonstration purposes. One of the most important forms we have to secure is the login form. The login form is vulnerable tobrute force attacks. Create a new file, and save it as index.php in your web-root. Add the following code within the body:

	<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
	<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
	<head>
		<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
		<title>Securing forms with form keys</title>
	</head>
	<body>
		<form action="" method="post">
		<dl>
			<dt><label for="username">Username:</label></dt>
			<dd><input type="text" name="username" id="username" /></dd>
			<dt><label for="username">Password:</label></dt>
			<dd><input type="password" name="password" id="password" /></dd>
			<dt></dt>
			<dd><input type="submit" value="Login" /></dd>
		</dl>
		</form>
	</body>
	</html>

Now we have a simple XHTML page with a login form. If you want to use form keys on your website, you can replace the script above with your own login page. Now, let’s continue to the real action.

Step 2: Creating a Class

We are going to create a PHP class for our form keys. Because every page can contain only one form key, we could make a singleton of our class to make sure that our class is used correctly. Because creating singletons is a more advanced OOP topic, we will skip that part. Create a new file called formkey.class.php and place it in your web-root. Now we have to think about the functions we need. First, we need a function to generate a form key so we can place it in our form. In your PHP file place the following code:

<?php

//You can of course choose any name for your class or integrate it in something like a functions or base class
class formKey
{
	//Here we store the generated form key
	private $formKey;
	
	//Here we store the old form key (more info at step 4)
	private $old_formKey;
	
	//Function to generate the form key
	private function generateKey()
	{
		
	}
}
?>

Above, you see a class with three parts: two variables and a function. We make the function private because this function will only be used by our outputfunctions, which we will create later. In the two variables, we will store the form keys. These are also private because they may only be used by functions inside our class.

Now, we have to think of a way to generate our form key. Because our form key must be unique (otherwise we don’t have any security), we use a combination of the users IP-address to bind the key to a user, mt_rand() to make it unique, and the uniqid() function to make it even more unique. We also encrypt this information with md5() to create a unique hash which we can then insert into our pages. Because we used md5(), a user cannot see what we used to generate the key. The whole function:

//Function to generate the form key
private function generateKey()
{
	//Get the IP-address of the user
	$ip = $_SERVER['REMOTE_ADDR'];
	
	//We use mt_rand() instead of rand() because it is better for generating random numbers.
	//We use 'true' to get a longer string.
	//See http://www.php.net/mt_rand for a precise description of the function and more examples.
	$uniqid = uniqid(mt_rand(), true);
	
	//Return the hash
	return md5($ip . $uniqid);
}

Insert the code above into your formkey.class.php file. Replace the function with the new function.

Step 3: Inserting a Form Key into Our Form

For this step, we create a new function that outputs a hidden HTML field with our form key. The function consists of three steps:

Generate, Save, Output

  1. Generate a form key with our generateKey() function.
  2. Store the form key in our $formKey variable and in a session.
  3. Output the HTML field.

We name our function outputKey() and make it public, because we have to use it outside of our class. Our function will call the private function generateKey() to generate a new form key and save it locally in a session. Lastly, we create the XHTML code. Now add the following code inside our PHP class:

//Function to output the form key
public function outputKey()
{
	//Generate the key and store it inside the class
	$this->formKey = $this->generateKey();
	//Store the form key in the session
	$_SESSION['form_key'] = $this->formKey;
	
	//Output the form key
	echo "<input type='hidden' name='form_key' id='form_key' value='".$this->formKey."' />";
}

Now, we are going to add the form key to our login form to secure it. We have to include the class in our index.php file. We also have to start the session because our class uses sessions to store the generated key. For this, we add the following code above the doctype and head tag:

<?php
//Start the session
session_start();
//Require the class
require('formkey.class.php');
//Start the class
$formKey = new formKey();
?>

The code above is pretty self-explanatory. We start the session (because we store the form key) and load the PHP class file. After that, we start the class with new formKey(), this will create our class and store it in $formKey. Now we only have to edit our form so that it contains the form key:

<form action="" method="post">
<dl>
	<?php $formKey->outputKey(); ?>
	<dt><label for="username">Username:</label></dt>
	<dd><input type="text" name="username" id="username" /></dd>
	<dt><label for="username">Password:</label></dt>
	<dd>input type="password" name="password" id="password" /></dd>
<dl>
</form>

And that’s all! Because we created the function outputKey(), we only have to include it in our form. We can use form keys in every form by just adding <?php $formKey->outputKey(); ?> Now just review the source of your webpage and you can see that there is a form key attached to the form. The only remaining step is to validate requests.

Step 4: Validating

We won’t validate the whole form; only the form key. Validating the form is basic PHP and tutorials can be found all over the web. Let’s validate the form key. Because our “generateKey” function overwrites the session value, we add a constructor to our PHP class. A constructor will be called when our class is created (or constructed). The constructor will store the previous key inside the class before we create a new one; so we’ll always have the previous form key for validating our form. If we didn’t do this, we wouldn’t be able to validate the form key. Add the following PHP function to your class:

//The constructor stores the form key (if one exists) in our class variable.
function __construct()
{
	//We need the previous key so we store it
	if(isset($_SESSION['form_key']))
	{
		$this->old_formKey = $_SESSION['form_key'];
	}
}

A constructor should always be named __construct(). When the constructor is called we check if a session is set, and if so, we store it locally in our old_formKey variable.

Now we are able to validate our form key. We create a basic function inside our class which validates the form key. This function should also be public because we are going to use it outside our class. The function will validate the POST value of the form key against the stored value of the form key. Add this function to the PHP class:

//Function that validated the form key POST data
public function validate()
{
	//We use the old formKey and not the new generated version
	if($_POST['form_key'] == $this->old_formKey)
	{
		//The key is valid, return true.
		return true;
	}
	else
	{
		//The key is invalid, return false.
		return false;
	}
}

Within index.php, we validate the form key by using the function we just created in our class. Of course, we only validate after a POST request. Add the following code after $formKey = new formKey();

$error = 'No error';

//Is request?
if($_SERVER['REQUEST_METHOD'] == 'post')
{
	//Validate the form key
	if(!isset($_POST['form_key']) || !$formKey->validate())
	{
		//Form key is invalid, show an error
		$error = 'Form key error!';
	}
	else
	{
		//Do the rest of your validation here
		$error = 'No form key error!';
	}
}

We created a variable $error which stores our error message. If a POST request has been sent we validate our formkey with $formKey->validate(). If this returns false, the form key is invalid and we display an error. Note that we only validate the form key — you’re expected to validate the rest of the form yourself.

In your HTML, you can place the following code to show the error message:

	<div><?php if($error) { echo($error); } ?></div>

This will echo the $error variable if it is set.

Form

If you start your server and go to index.php, you will see our form and the message ‘No error’. When you submit a form you will see the message ‘No form key error’ because it is an valid POST request. Now try to reload the page and accept when your browser requests that the POST data be sent again. You will see that our script triggers an error message: ‘Form key error!’ Your form is now protected against input from other websites and errors with page reloads! The error is also shown after a refresh because a new form key was generated after we submitted the form. This is good because, now, user can’t accidentally post a form twice.

Full Code

Here is the whole PHP and HTML code:

index.php

<?php
//Start the session
session_start();
//Require the class
require('formkey.class.php');
//Start the class
$formKey = new formKey();

$error = 'No error';

//Is request?
if($_SERVER['REQUEST_METHOD'] == 'post')
{
	//Validate the form key
	if(!isset($_POST['form_key']) || !$formKey->validate())
	{
		//Form key is invalid, show an error
		$error = 'Form key error!';
	}
	else
	{
		//Do the rest of your validation here
		$error = 'No form key error!';
	}
}
?>
	
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
	<title>Securing forms with form keys</title>
</head>
<body>
	<div><?php if($error) { echo($error); } ?>
	<form action="" method="post">
	<dl>
		<?php $formKey->outputKey(); ?>

		<dt><label for="username">Username:</label></dt>
		<dd><input type="text" name="username" id="username" /></dd>
		<dt><label for="username">Password:</label></dt>
		<dd><input type="password" name="password" id="password" /></dd>
		<dt></dt>
		<dd><input type="submit" value="Submit" /></dd>
	<dl>
	</form>
</body>
</html>

fomrkey.class.php

<?php

//You can of course choose any name for your class or integrate it in something like a functions or base class
class formKey
{
	//Here we store the generated form key
	private $formKey;
	
	//Here we store the old form key (more info at step 4)
	private $old_formKey;
	
	//The constructor stores the form key (if one excists) in our class variable
	function __construct()
	{
		//We need the previous key so we store it
		if(isset($_SESSION['form_key']))
		{
			$this->old_formKey = $_SESSION['form_key'];
		}
	}

	//Function to generate the form key
	private function generateKey()
	{
		//Get the IP-address of the user
		$ip = $_SERVER['REMOTE_ADDR'];
		
		//We use mt_rand() instead of rand() because it is better for generating random numbers.
		//We use 'true' to get a longer string.
		//See http://www.php.net/mt_rand for a precise description of the function and more examples.
		$uniqid = uniqid(mt_rand(), true);
		
		//Return the hash
		return md5($ip . $uniqid);
	}

	
	//Function to output the form key
	public function outputKey()
	{
		//Generate the key and store it inside the class
		$this->formKey = $this->generateKey();
		//Store the form key in the session
		$_SESSION['form_key'] = $this->formKey;
		
		//Output the form key
		echo "<input type='hidden' name='form_key' id='form_key' value='".$this->formKey."' />";
	}

	
	//Function that validated the form key POST data
	public function validate()
	{
		//We use the old formKey and not the new generated version
		if($_POST['form_key'] == $this->old_formKey)
		{
			//The key is valid, return true.
			return true;
		}
		else
		{
			//The key is invalid, return false.
			return false;
		}
	}
}
?>

Conclusion

Adding this code to every important form on your website will increase your form’s security dramatically. It even stops refreshing issues, as we saw in step 4. Because the form key is only valid for one request, a double post is not possible.

This was my first tutorial, I hope you like it and use it to improve your security! Please let me know your thoughts, via the comments. Have a better method? Let us know.

Further Reading


Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://myfacefriends.com Myfacefriends

    Thanks for another wonderful tuts!

    • http://www.jeff-way.com Jeffrey Way

      Thanks for reading — after thirty seconds of being posted. :)

      • Merxhan

        hahahah I wanna read that fast, and digest it :)

  • http://kiran13xtreme.wordpress.com Kiran

    Another great article from nettuts! :)

  • http://myfacefriends.com Myfacefriends

    I’m so much addicted to your site Jeff, your site is great. hope someday theres a smarty tutorial here. thanks

  • http://www.amayamedia.com Rene

    great tutorial, really awesome and helpful :D

  • http://james.padolsey.com James

    “This website will be able to send POST data to your website — for example, with AJAX” – how, exactly? I’m pretty sure the same-domain-policy would forbid this.

    • Mikael

      True.
      But all it takes is a regular form on the foreign website to POST to yours.
      As long as the user is logged into your website, bam, vulnerable !

      • Tom

        doesn’t authentication via ajax always somewhere include a hash of some sort? How else would you authenticate the user when he/she submits data via ajax?

      • http://61dh.com Adam

        Seems to me this is a good example about OO PHP. But for security, what does ‘POST data can be sent from one website to another’ really mean? Can anyone have a better explanation?

      • http://61dh.com Adam

        Never mind. Maybe the author was just talking about the a regular form (non-ajax).

      • http://instantsolve.net Thomas Milburn

        I think the author was talking about how you can send POST data cross domain using an iframe and javascript. Of course this is not Ajax but probably the classic case of mixing up Ajax and Javascript. You can create a hidden iframe which has a form on it with action=”www.otherdomain.com/post.php” and use javascript to submit that POST form.

        The user will be unaware that data has been submitted to another site. Of course in keeping with the cross domain policy, javascript won’t be able to read the result of this form submission.

  • http://www.prop-14.com Randy

    This is so great to see so much about php security here! People talk about how “easy” php is to learn, but many fail to focus on securing their site properly. There can not be too many articles on this IMO. Nice job Wouter.

  • http://resnodesigns.com Bryan P

    A great post to increase my form security. I think its interesting because I have recently been examining my security processes.

  • http://twitter.com/keithnorm keithnorm

    Thank you for an explanation of a nice clean way to handle this. Very helpful.

  • Daniel

    I can’t understand where is the security point, sorry.
    Why attackers couldn’t read generated HTML code for form, copy the key value, and send it from another server with all the other variables on a POST?

    • Gerry

      Exactly what I was thinking

      • Gerry

        Actually this won’t work as the attacker can only send data to the logged in session, it can’t receive it. Or I think so anyway.

    • http://elycruz.com Ely

      Because the ip address would be different, in addition this could be hardened with an appened timestamp. Remeber this is all stored in the session variable,
      which if secured correctly would not allow some to copy paste the values and run from another server (the receiving script would either redirect or throw an error upon receipt of invalid key/token/capthca etc..

    • Don

      I agree, the attacker could use a script to read the key value in the form and then do a form submission using the key value. This would have to be done from the same computer or server so session is not interrupted.

  • Seed

    I always have “No errors” message, even if I refresh the site :/

    • ananda rudra

      //Is request?
      if($_SERVER['REQUEST_METHOD'] == ‘post’)

      CHANGE it to

      //Is request?
      if($_SERVER['REQUEST_METHOD'] == ‘POST’)

      • http://www.softwareforenterprise.us Bob

        Thanks! Changing to POST instead of post works for me finally.

        Maybe the author should consider changing the script?

        Thanks again.

  • http://www.shaunbent.co.uk Shaun

    Awesome Tutorial! Would be great to get more tutorials on Secuirty!

  • Greg

    This is great stuff for a php newbie like me. Very much appreciated.
    cheers

  • http://www.benniemosher.com Bennie Mosher

    Awesome post Jeff. This will come in handy one day.

    One question I have though, does anyone know if WordPress is using some kind of secure form publishing technique for their log-in forms? I would think that they are, but can’t seem to find where it would be doing it at. Thanks for the heads up in advance!

    • http://www.mai-ai.org Wouter Bulten

      See the end of my tut:

      Further Reading
      - WordPress also uses form keys (naming it Nonces): WordPress Nonces
      - Seven habits for writing secure PHP applications

  • http://johnnemec.com John

    great tutorial…

    one small typo – in the Full Code section you have fomrkey.class.php – it should be formkey.class.php

    Again… GREAT STUFF… look forward to your future posts

    • http://twitter.com/socialcoop B. Ackles

      I saw that to. I also noticed that the input tag on Step 3, snipit 3 is missing an opening bracket. Simple typo.

      Great tutorial. Extremely valuable to any beginners to intermediates php developers.

  • Ben

    This is a great write up. Thanks. It’s going to be a great help with some of the projects I’m currently working on.

  • http://blog.ntierdesign.com Nathan

    Great post, but one warning. You’ll run into problems if you use a user’s ip address to uniquely identify them. When a user’s request originates behind a corporate proxy or a proxy farm, their ip address is sure to change between requests (thus their token).

    This technique is fine for simple forms, but if you were to roll this out into a large registration system, you’d be hitting your head against the wall trying to find out why 5% of your user’s cannot use your application.

    • http://instantsolve.net Thomas Milburn

      Actually, this script doesn’t check the IP address again when validating so this wouldn’t be an issue. Which of course raises the point of why put the IP address into the hash?

      Quote: “we use a combination of the users IP-address to bind the key to a user” is blatantly not true.

      • http://blog.ntierdesign.com Nathan

        Gotcha, I didn’t verify that it was used on form validation, only the line you quoted.

  • http://www.philohermans.nl Philo

    Great tutorial Wouter! ( Goede tutorial! ;) )

  • http://www.quizzpot.com Crysfel

    Thank you! i enjoy this one :D

  • http://www.mai-ai.org Wouter Bulten

    Thanks for your great responses!

    @James
    “This website will be able to send POST data to your website — for example, with AJAX”

    It is possible if you use iframes and send POST data with AJAX to the iframe. (I read an article about it, not tested it myself)

  • Santana

    Nice!!! Thank u!! Great Stuff

  • http://www.johngirvin.com John Girvin

    Good tutorial on defending against some cross site request forgery (CSRF) attacks. Chris Shifflet wrote about this back in 2004 here: http://shiflett.org/articles/cross-site-request-forgeries

    For the “form key” – aka “nonce” or “number used once” – a random string works just as well as anything else you can hash (hash, not encrypt!) together with md5(). The important points are that the value is unique and non-predictable.

    You should also consider checking IP address and user agent strings to see if they have changed from previous requests. This isn’t foolproof as these values can change anyway, but taken together they can mark a request as “suspicious” and prompt a graduated response from your application if the form is sensitive. For example, you can re-ask for the user’s password or CAPTCHA.

  • http://www.angechierchia.com Ange Chierchia

    Great tuts, your post is very interesting I think I should use your method in my on going project security in e-commerce websites is the most important thing.

    Thanks!

  • Matthijn

    Well, you say it is ‘not wanted’ being able to post from other sites with functions like curl, but if you just have good validations and filtering on all the values, what can go wrong then?

  • Matthijn

    Can’t seem to edit my last post.

    But even with this method you can’t be secure from ‘posting from another script’. Because you could get all the values needed via Zend_Http_Client, read that out, and repost those (and own) values via Zend_Http_Client back, and your script wouldn’t know the difference.

  • mefisto

    The result is NOT a valid XHTML !
    Its just wrong.

  • OSX Boy

    Nice tutorial now all we need is to extend the class to eiminate non-human form fillers! We get lots of bots and other than adding a capatcha type test it’s tough to avoid automated form submissions.

  • http://www.howsthe.com Vitaly Babiy

    Great tip, alot of frameworks do this for your like, Rails or Django. Not sure in the PHP world.

  • Merxhan

    Great tutorial, easy to understand and not so much code.
    I don’t have so much knowledge on security issues but these seems inspiring
    Thanks

  • http://www.webdev3000.com Csaba

    Well written tutorial Wouter. Easy to follow.

  • Jobe

    Nice work, this is very helpful.

  • OSX Boy

    One thing I noticed is that:

    if($_SERVER['REQUEST_METHOD'] == ‘post’) {

    needs to be:

    if($_SERVER['REQUEST_METHOD'] == ‘POST’) {

    This may be our server setup or a genic thing i’m not sure as I user
    ‘ method=”post” ‘ in my form tag

    • Nuclear Gorilla

      Thanks, this was my issue

  • joe

    Already do it. Thanks, keeping going!

  • http://soundenergyflux.blogspot.com Alejandro

    Great tutorial! A short question.. would this solution work with two (protected) forms on the same page?

    • http://www.hpwebsolutions.com/ jonathan

      It would only work with two forms on the same page if they both use the same form key. It also wouldn’t work if the user has two forms loaded up in two different tabs or windows and the user tries to submit the older form. Since $_SESSION['form_key'] is overwritten every time a new form key is generated, it is good for one form or page only. A way around this would be to insert the form_key into a database and look it up based on the user’s session id. Or you could set $_SESSION['form_key'] = array(form_key1,form_key2….), adding each form_key to the array as it is generated and search the array when a form is posted for the submitted form_key.

  • http://www.tripleoption.com/ Ryan S

    For years I have used ASP. Over the past few months of reading Nettuts, I think I’m finding PHP to be much easier and more available code and information. Is there more information and users coding with PHP, over ASP and others?

    Thanks

    • http://nettutsblog@gmail.com Jeffrey Way

      Are you referring to ASP or ASP.NET 3 — big difference between the two. ASP.NET is very, very powerful.

  • Andy H

    I thought the tutorial was well written and easy to follow. Good use of OO concepts and a very great security layer to implement.

  • http://antihack.de Arne

    This article is nice and really shows what popular CMS do nowadys to secure their forms.
    But in fact this measure of security is just to protect your form against real cheapstakes.
    You can’t kill a real cURL form spoofer with this one, cause cURL is capable of sending and parsing cookies.
    Infact you have two do 2 requests with cURL, but if you really want to do this it is not much of a problem.

    A better way would be using captures or generate the form-key via Ajax and put it in the value field.
    cURL is not capable of rendering JS, so if mystify your JS code so much that is is to hard to read no one will be willing to hijack the form.

  • K. N.

    This method actually helps you against CSRF, but I dont get the point how it helps against XSS flaws. By the way: If your site has XSS holes you dont need to secure you against CSRF, because then these protections are pretty pointless because you could grab the generated hash via javascript ;)

    • http://instantsolve.net Thomas Milburn

      You are absolutely right. I’m not sure the author really understands the security issue or at least hasn’t explained it very well.

    • Some Macedonian

      you will need to do input filtering in order to protect against XSS attacks, while you are right about this tutorial, it only helps against CSRF attacks. CodeIgniter has a good class for input filtering, the xss_clean method does lots of checking before it returns the valid input.

  • http://www.l4u.dk/ Lau

    Useful info.

  • http://www.webmaster-source.com redwall_hp

    Very nice tutorial.

    I think it would be nice to have a series of tutorials showing the basics of how certain exploits work, and how to counter them. Just a thought. :)

    • Thomas

      What would you like to see covered? I might write one…

  • Les

    I have found that with a single key preserved for verification of the key held in the form is not enough in the event of a redirect, where you submit a form and redirect to a new page and you still require to validate a key.

    In such a situ your key won’t validate, so what I do is to keep the last three generated keys in an array instead, and compare a key coming in from a form submission, against those three held in the array.

    This works better and it doesn’t impede any security measures.

  • Les

    > because you could grab the generated hash via javascript

    Doesn’t matter, and that is the point really…

    No matter what you do with it on the client side, if it ain’t on the server side your goose is well cooked; this approach is basically to prevent a) automated machines taking advantage, and b) to prevent a previously saved page on someone’s desktop being used instead of the live page, for two examples.

    If you are going to make a wide, sweeping statement such as this, at least know a bit more about what you are talking about aye?

    • K. N.

      Automated machines could not open the page, generate a hash and submit them?

  • Ruben

    I can see problems with tabbed browsing: imagine a user needs to use a form (not a login form obviously) multiple times and opens it in multiple tabs.
    Only the last form will be submitted successfully, and only if the user submits it first.

    So you are assuming here that the user will use your site the way you intended, which is not that realistic.

    Solutions:
    * Store last N keys instead of just 1
    * Encrypt the request time + a session specific value and use that value as a key. On submit, decrypt the key and check the difference between request time and current time.

  • Arun

    Hi

    May I know how different is this approach from the captcha?

    with captcha the generate code / key is checked with the user input. Here instead of the user input, we have it in the hidden element.

    Please correct if I’m wrong.

  • david

    I don’t see why you should make a class for this? The workhorses of the code are the formfield and the session key/value pair. So all you have to do is create the formkey, add it to the session and a hidden input field.

    Most of the class code just restricts developers.

  • http://www.zensomedia.com Steven

    Usually I don’t comment, but this is a really wonderful high quality tut. Wonderful job! More php tuts about security would be wonderful.

  • Brad

    This is an excellent turtorial, perhaps one of the most pertinent I have read on form security. Thank you!

  • Polo

    Hey Thomas Milburn and K.N, why do you write a better tutorial on security since you guys seems to be very knowledgeable.

    This tutorial covers the basic security concerns for newbies but you keep bashing on the author, i mean everybody has the right to raise their opinions but at least be cool enough to provide better information if you feel that this tutorial is missing something, just my 2 cents.

    Cool tutorial by the way.

  • http://pachume.com James Hogan

    synchronized token pattern. i love this site. thank you

  • http://twitter.com/iPad iPad

    Look at this form http://www.romancortes.com/contact/ I just love it, this is Roman Cortes the ‘Homer CSS’ Author

  • Hash

    Good