* web security/form post secure way *
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:
- Add a form key to every form.
- Store the form key in a session.
- Validate the form key after a form submit.
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;
}
}
}
?>
secure form data
<!DOCTYPE html>
<?php error_reporting(0); ?><html lang="en">
<head>
<title>Secure contact form</title>
<meta charset="utf-8">
<style>
p {
margin: 0;
color: red;
}
</style>
</head>
<body>
<?php
if(isset($_POST['submit'])){
$name = htmlspecialchars(stripslashes(trim($_POST['name'])));
$subject = htmlspecialchars(stripslashes(trim($_POST['subject'])));
$email = htmlspecialchars(stripslashes(trim($_POST['email'])));
$message = htmlspecialchars(stripslashes(trim($_POST['message'])));
if(!preg_match("/^[A-Za-z .'-]+$/", $name)){
$name_error = 'Invalid name';
}
if(!preg_match("/^[A-Za-z .'-]+$/", $subject)){
$subject_error = 'Invalid subject';
}
if(!preg_match("/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/", $email)){
$email_error = 'Invalid email';
}
if(strlen($message) === 0){
$message_error = 'Your message should not be empty';
}
}
?>
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="POST">
<label for="name">Name:</label><br>
<input type="text" name="name">
<p><?php if(isset($name_error)) echo $name_error; ?></p>
<label for="subject">Subject:</label><br>
<input type="text" name="subject">
<p><?php if(isset($subject_error)) echo $subject_error; ?></p>
<label for="email">Email:</label><br>
<input type="text" name="email">
<p><?php if(isset($email_error)) echo $email_error; ?></p>
<label for="message">Message:</label><br>
<textarea name="message"></textarea>
<p><?php if(isset($message_error)) echo $message_error; ?></p>
<input type="submit" name="submit" value="Submit">
<?php
if(isset($_POST['submit']) && !isset($name_error) && !isset($subject_error) && !isset($email_error) && !isset($message_error)){
$to = 'youremail@addres.com'; // edit here
$body = " Name: $name\n E-mail: $email\n Message:\n $message";
if(mail($to, $subject, $body)){
echo '<p style="color: green">Message sent</p>';
}else{
echo '<p>Error occurred, please try again later</p>';
}
}
?>
</form>
</body>
</html>
No comments:
Post a Comment