I'm a beginner in PHP.
What I'm trying to do is stop Post Data coming from another webpage.
The problem I am having is let's say someone copies my form and pastes it in their website. I want to be able to stop that Post Data from running the script on my email form.
How can I do this? Let me know if I'm not being clear enough.
My PHP Contact form runs on one page with conditional statements. i.e. if data checks out, submit.
"accepted answer" has security holes. Instead, you should use more secure methods. A simple example:
Step 1: Disable framing of the page (.php), where the form is generated, in the top add:
header('X-Frame-Options: Deny');
Step 2: (important part ! ): In order to avoid XSS and 3rd party exploits, you should create a expirable validation.
For example:
ASP.NET builtin forms use dynamic input csrf (example value: gtlkjh29f9ewduh024cfvefb )
WordPress builtin forms use dynamic input nonce (example value: 340297658942346 )
So, if you are on a custom platform, which doesn't have built-in temporary token validation methods, then implement your approach. A simple concept:
<?php
$secret_key = 'fjd3vkuw#KURefg'; //change this
$encrypted_value = Cryptor::encrypt( time(), $_SERVER['REMOTE_ADDR'] . $secret_key);
?>
<form>
...
...
<input value="<?php echo $encrypted_value;?>" name="temp_random" type="hidden" />
</form>
(Cryptor code is here )
on submission, check:
if(!empty($_POST)){
// If REFERRER is empty, or it's NOT YOUR HOST, then STOP it
if( !isset($_SERVER['HTTP_REFERRER']) || parse_url($_SERVER['HTTP_REFERRER'])['host'] != $_SERVER['HTTP_HOST'] ){
exit("Not allowed - Unknown host request! ");
}
// Now, check if valid
if ( Cryptor::decrypt( $_POST['temp_random'], $_SERVER['REMOTE_ADDR'] . $secret_key) < time() - 60* 15 ) {
exit("Not allowed - invalid attempt! ");
}
...........................................
... Now, you can execute your code here ...
...........................................
}
You're trying to prevent CSRF - Cross-Site Request Forgery. Jeff himself has a blog article about this.
True XSRF Prevention requires three parts:
Hidden Input Fields, to prevent someone from just snatching the form and embedding it
Timechecking within an epsilon of the form being generated, otherwise someone can generate a valid form once and use the token (depending on impementation/how it's stored)
Cookies: this is to prevent a malicious server from pretending it's a client, and performing a man-in-the-middle attack
$_SERVER['HTTP_Referrer'] would be nice but it isn't reliable. You could use a hidden form field that MD5's something and then you check it on the other side.
In the form:
<?
$password = "mypass"; //change to something only you know
$hash = md5($password . $_SERVER['REMOTE_ADDR']);
echo "<input type=\"hidden\" name=\"iphash\" value=\"$hash\"/>";
?>
When you are checking:
$password = "mypass"; //same as above
if ($_POST['iphash'] == md5($password . $_SERVER['REMOTE_ADDR'])) {
//fine
}
else {
//error
}
If you're looking for a quick-and-dirty approach, you can check the REFERER header.
If you really want to make sure that the form was fetched from your site though, you should generate a token each time the form is loaded and attach it to a session. A simple way to do this would be something like:
$_SESSION['formToken'] = sha1(microtime());
Then your form can have a hidden input:
<input type="hidden" name="token" value='<?=$_SESSION['formToken'];?>' />
and you can check that when deciding whether to process your form data.
Every user do signup and then obtain a login id.
Following is algorithm to prevent CSRF: -
1) $login_id = user login id (converted to a numeric id using mysql)
2) $a_secret_key = $_SERVER['UNIQUE_ID'];
3) $remote_addr = $_SERVER['REMOTE_ADDR'];
4) Request Date and Time -> A unique reference key -> $refkey
5) $_SESSION['secretkey'] = $_SERVER['UNIQUE_ID'];
Combine aforesaid 1 to 4 to create a json file, when transferring data to another page.
Then
echo "<input type=\"hidden\" name=\"refkey\" value=\"$refkey\"/>";
At receiver's end:-
Receiver page should check if
1) any json file with $refkey exists at server?
2) If $refkey exists, then check $login_id, $a_secret_key and $remote_addr exists and are correct.
There's a typo in the highest score answer. It should be $_SERVER['HTTP_REFERER'] instead of $_SERVER['HTTP_REFERRER'].
Related
Suppose a website xyz.com is showing ads from my ad network example.com using JavaScript code:
<script type='text/javascript' src='http://example.com/click.php?id=12345678'></script>
Which shows the ad as:
click.php
<a href="http://example.com/process.php?var1=var1&var2=var2">
<img src="http://example.com/ads/banner.png"/></a>
When the link is clicked it is taken to process.php where I add and subtract balance using some MySQL queries and then redirect to ad's URL.
process.php
$ua = $_SERVER['HTTP_USER_AGENT'];
$ip = $_SERVER['REMOTE_ADDR'];
//invalid click
if($_SERVER['HTTP_REFERER']==null || $_SERVER['HTTP_USER_AGENT']==null) {
header("location: http://example.com");
exit;
}
I want to add to an Unique Session at click.php and retrieve it at process.php to prevent invalid clicks. How do I do that?
Update:
The answer below solves half of the issue but the users are still able to send fake clicks using iframe and img tag as below:
<img src="http://example.com/click.php?id=12345678" height="1px" width="1px"/>
These clicks are still being counted as the request are served by both the pages click.php and process.php
What's the solution for this?
I have got a solution to the problem and it works perfectly:
EDIT:
I have found a solution:
To set the variables using sessions at click.php and sent it to process.php using a random number
click.php
$_SESSION["ip"]=$ip;
$_SESSION["ua"]=$ua;
$rand="".rand(1,9)."".rand(0,9)."".rand(0,9)."".rand(0,9)."".rand(0,9)."".rand(0,9)."";
$_SESSION["hash"]=$rand;
<a href="http://example.com/process.php?hash=$rand">
<img src="http://example.com/ads/banner.png"/></a>
and getting the values from the session at process.php
process.php
$hash=$_GET["hash"];
$ua=$_SESSION["ua"];
$ip=$_SESSION["ip"];
$rand=$_SESSION["hash"];
// Invalid Redirection Protection
if(($hash!=$rand) || ($ip!=$_SERVER['REMOTE_ADDR']) || ($ua!=$_SERVER['HTTP_USER_AGENT'])) {
header("location: http://example.com");
session_destroy();
exit;
}
If I have understood your question, your goal is to ensure that any requests arriving at http://example.com/process.php were from links created by http://example.com/click.php
(note that this only means that anyone trying to subvert your system needs to fetch http://example.com/click.php and extract the relevant data before fetching http://example.com/process.php. It raises the bar a little but it is a long way from being foolproof).
PHP already has a very good sessions mechanism. It would be easy to adapt to propogation via a url embedded in the script output (since you can't rely on cookies being available). However as it depends on writing to storage, its not very scalable.
I would go with a token with a finite number of predictable good states (and a much larger number of bad states). That means using some sort of encryption. While a symmetric cipher would give the easiest model to understand it's more tricky to implement than a hash based model.
With the hash model you would hash the values you are already sending with a secret salt and include the hash in the request. Then at the receiving end, repeat the exercise and compared the generated hash with the sent hash.
To prevent duplicate submissions you'd need to use some other identifier in the request vars - a large random number, the client IP address, the time....
define('SALT','4387trog83754kla');
function mk_protected_url($url)
{
$parts=parse_url($url);
$args=parse_str($parts['query']);
$args['timenow']=time();
$args['rand']=rand(1000,30000);
sort($args);
$q=http_build_query($args);
$args['hash']=sha1(SALT . $q);
$q=http_build_query($args);
return $parts['scheme'] . '://'
.$parts['host'] . '/'
.$parts['path'] . '?' . $q;
}
function chk_protected_url($url)
{
$parts=parse_url($url);
$args=parse_str($parts['query']);
$hash=$args['hash'];
unset($args['hash'];
// you might also want to validate other values in the query such as the age
$q=http_build_query($args);
$check=sha1(SALT . $q);
return ($hash === $check)
}
I send the email and password unencrypted via the POST method over HTTP not HTTPS.
Is it possible for someone to see the raw post data?
I imagine raw post data looks like this:
email='example#aaa.com'&password='qazwsx'
I know that I should encrypt passwords but that's just an example I made up.
In reality I pass a lot more sensitive information.
EDIT:
I currently have a php script called register.php which does exactly what the name implies.
Anyone who is smart enough to guess that I'm using two variables named "email" and "password" could achieve the registration of a new user just by sending some POST data. How can I prevent this exploit? Obviously, it has nothing to do with SSL, it's a second concern I have.
I don't want a 17 years old "hacker" to generate 18,838,929 new accounts in 3 days.
POST data is sent unencrypted in the request body. It is as safe as passing it using GET, though less visual. If your users are connected for instance via a public/unsecure (WiFi) network, there is always a chance that someone behind the network intercepts the traffic and all HTTP data.
Considering you pass around sensitive data, the safest solution is to use HTTP over SSL (HTTPS). This will take care of encryption for you. You could also do some sort of inhouse client-side encryption and then send the data, but HTTPS is the way to go in order to be on the safe side.
EDIT:
As for your second question, a simple/basic method to prevent automatic requests is to check the Referer and Origin headers of each request. This way you can filter out any off-site requests. Another option is to limit the number of requests per time interval for the same combination of IP address and user agent details. Neither of these methods is completely safe, but can be combined with a solid captcha test.
Use sessions.
Create a new session identifier every time the user/client accesses your registration page and then use that to validate the request. You see that's the main idea behind captcha in the first place. Or better yet:
<?Php
session_start();
if(isset($_SESSION['input1'])
&& isset($_SESSION['input2'])
&&(strtoupper($_SERVER['REQUEST_METHOD']) === 'POST'
|| strtoupper($_SERVER['REQUEST_METHOD']) === 'GET'))
{ if(isset($_POST[$_SESSION['input1']])
&& !empty($_POST[$_SESSION['input1']])
&& isset($_POST[$_SESSION['input2']])
&& !empty($_POST[$_SESSION['input1']]))
{ //PROCESS YOUR POST DATA HERE
$myInput1 = sanitize($_POST[$_SESSION['input1']]);
$myInput2 = sanitize($_POST[$_SESSION['input2']]);
echo "My variables are {$myInput1} and {$myInput2}.";
//AND ET CETERA
}
else
{ session_unset();
session_destroy();
echo <<<REDIRECTION
<script type='text/javascript'>
alert("You've had a premature detonation. Call 911.");
window.location.replace('{$_SERVER['PHP_SELF']}');
</script>
REDIRECTION;
}
clearsess();
exit();
}
function sanitize($variable,$conn=NULL)
{ if ($conn != NULL)
return mysqli_real_escape_string($conn, filter_var($variable, FILTER_SANITIZE_STRING));
else
return filter_var($variable, FILTER_SANITIZE_STRING);
}
function createForm()
{ $_SESSION['input1'] = rand(1111,9999);
$_SESSION['input2'] = rand(11111,99999);
return <<<NEWFORM
<form action = '{$_SERVER['PHP_SELF']}' method = 'post'>
<input type='text' name='{$_SESSION['input1']}'>
<input type='text' name='{$_SESSION['input2']}'>
<input type='submit' value='submit'>
<input type='reset' value='reset'>
</form>
NEWFORM;
}
function clearsess()
{ if(session_id() != '' && empty($_SESSION))
{ session_unset();
session_destroy();
}
session_write_close();
}
?>
<html>
<head></head>
<body>
<?Php echo createForm(); ?>
</body>
</html>
<?Php clearsess(); ?>
Well you can use it hand in hand with captcha though. Captcha would definitely be using sessions too.
I have a script on my webserver that initiates a HTTPS connection with a secured payment solution. The payment solution allows the user to store its credit cards credentials, so the script's integrity is mandatory.
The script is called from web and from a web browser launch by an iPhone application.
It takes several POST values in entry:
user ID
value
currency
... etc
to generate the initial request with the payment solution.
My goal is to secure, as much as possible, the POST values sent to the script to avoid attacks, essentialy because anyone could see the POST variables it takes in entry with a simple Firebug.
The script is reached via HTTPS protocol, and this is what I came up with in order to secure the content data :
if (!empty($_POST)) {
$uid = $_POST['uid'];
$nonce = $_POST['nonce'];
$request_timestamp = $_POST['time'];
//other useful values ...
}
/*
* Test 1 : is the nonce correct ? (Example of possible hash)
*/
if (strcmp(md5($uid . $request_timestamp . 'mygrE4TpassPhraZ'), $nonce) !== 0) {
echo 'Bad nonce';
exit;
}
/*
* Test 2 : is the timestamp value acceptable ? 10 seconds maximum here.
*/
if (time() - $request_timestamp > 10) {
echo 'Request too old';
exit;
}
/*
* Test 3 : is this request is the first valid one that I receive with those credentials for this user ?
*/
if (strcmp(User::getOnGoingNonce($uid), $nonce) === 0) {
echo 'Request already registered';
exit;
}
//direct database access
User::setOnGoingNonce($uid,$nonce);
/*
* Finally, chat granted with the payment solution ...
*/
Do you think this is secure enough ? Do you have cleaner solutions ?
Inputs would be greatly appreciated, thank you in advance.
It is good developer practice to sanitize and filter every user input. There is a special PHP function for that purpose, available to make a programmer's life easier. It is called filter_var().
If you work with arrays you can use the filter_var_array().
See here for details.
So, the practical solution to your code should be something like this:
$uid = filter_var($_POST['uid'], FILTER_SANITIZE_NUMBER_INT);
$nonce = filter_var($_POST['nonce'], FILTER_SANITIZE_STRING);
$request_timestamp = filter_var($_POST['time'], FILTER_SANITIZE_STRING);
I assume, that 'uid' is a integer, other variables are strings. But you can choose whatever filter you need. See here for type of filters.
When sanitizing the user input you ensure, that your script will not allow SQL injection attacks, for example, or XSS attacks.
Whats the best was of doing this?
Sessions? How can I take all my variables defined on 1 page and send them over to another.
I read using serialize convert the HTTP_POST_VARS to a string, and then pass that (using a hidden form/input?) and use unserialize on the other PHP page to get the variables back.
Also I saw someone just use something like:
<?php
foreach($HTTP_POST_VARS as $key => $val) {
?>
<input type="hidden" name="<?php echo $key; ?>" value="<?php echo $val; ?>">
<?php
}
?>
Which seems ugly and asking for trouble.
Basically this is the run down of what I am trying to do:
The user fills out a form and then submits the form with all the information thats required. A second page intercepts all the HTTP_POST_VARS and then defines more variables. The user is asked if the information is correct and asked if they would like to submit all information. So far I have gotten this far. Now I want a button/link where the user clicks it and then it sends all the information page 2 has to another page where it finally runs the code to process all the information. (MYSQL, EMAILS, etc)
My ideal solution would be able to define something like onclick where I can just run a PHP function at whim, but that doesn't exist. One thing is I want to make sure information thats posted/pushed/whatever to page3 (processing) that its legit and actually comes from page2 (confirmation)...I don't want people just randomly making HTTP POSTs and having it validate. I was thinking of running some kind of MD5 stuff with a secret key to validate.
Does anyone have an elegant solution of a form where you have PART 1 ( filling out), PART 2 (confirmation to user) and PART 3 (processing all information from PART 2).
I would store the values in a session variable after the initial submission and then use them accordingly after confirmation/validation:
<?php
/////////////////////////////////
// STEP 1 - Initial Form Display
/////////////////////////////////
session_start();
echo '<form>';
echo '<input type="text" name="usr_name" />';
echo '<input type="text" name="usr_phone" />';
echo '<input type="text" name="invalid_field" />';
echo '<input type="submit" name="submit" value="Submit" />';
echo '</form>';
/////////////////////////////////
// STEP 2 - Confirmation Page
/////////////////////////////////
// change this by your global of choice ($_POST, $_GET, $_REQUEST)
$input_source &= $_GET;
// create an array of all input fields that start with 'usr_'
$input_fields = #preg_grep( '#^usr_[a-z]+#i', array_keys( $input_source ) );
if( !empty( $input_fields ) )
{
// store all valid input fields in the session
$_SESSION['input_values'] = array();
foreach( $input_fields as $key )
{
$_SESSION['input_values'][$key] = $input_source[$key];
}
// create a checksum from the user's IP address and all input values (for false sense of security ^_^)
$_SESSION['input_checksum'] = md5( $_SERVER['REMOTE_ADDR'] . '|' . join( '', $_SESSION['input_values'] ) );
// logic for data validation and confirmation HTML goes here...
}
/////////////////////////////////
// STEP 3 - Final Validation
/////////////////////////////////
// check for the existence of the session values from step 2
if( !empty( $_SESSION['input_values'] ) && !empty( $_SESSION['input_checksum'] ) )
{
// create comparison checksum for validation purposes
$_comp_checksum = md5( $_SERVER['REMOTE_ADDR'] . '|' . join( '', $_SESSION['input_values'] ) );
// check session and comparisson checksums
if( $_SESSION['input_checksum'] == $_comp_checksum )
{
// confirmation/validation looks good, proceed...
}
}
?>
I generally just put everything into Session variables AFTER I have validated the information. I have always had pretty good luck with that and since there is generally not a lot of user information like you are talking about the overhead really isn't all that bad. Now, if you are talking A LOT of data then you may want to consider a different method.
Like I said, this may not be the most elegant solution but it will certainly work.
How about to share your needed information into the session after page1?
Well here you got everything you need:
php - session reference
Ugly and asking for trouble? I think you're on to something.
Ideally, you could store previously-entered data in session state, which stores the data on the server.
Alternatively, if the state needs to be stored in the browser page, you can use the method you mentioned, or you can do something a bit more like Microsoft's "view state" variable.
Essentially, you serialize your data, perhaps encrypt and/or sign the result, and then base64 encode the whole lump and stick the result in hidden variable on the page. The advantage of doing it this way include (a) not polluting your namespace with old variable names, (b) not confusing form-filling addons and utilities, (c) tamper-resistant variable storage (esp. if encrypted or signed).
In Yahoo or Google and in many websites when you fill up a form and if your form has any errors it gets redirected to the same page.
Note that the data in the form remains as it is. I mean the data in the text fields remains the same.
I tried ‹form action="(same page here)" method="post or get"›. It gets redirected to the page, but the contents of the form gets cleared.
I want the data to be displayed.
You know how tiresome it will be for the user if he has to fill up the entire form once again if he just forgets to check the accept terms and conditions checkbox.
Need help!
You need to do this yourself. When the page gets posted you'll have access to all the form values the user entered via $POST['...']. You can then re-populate the form fields with this data.
Here is a modified version of what I use for very simple websites where I don't want/need an entire framework to get the job done.
function input($name, $options = array()) {
if(!isset($options['type'])) $options['type'] = 'text';
$options['name'] = $name;
if(isset($_POST[$name]) && $options['type'] != 'password') {
$options['value'] = htmlspecialchars($_POST[$name]);
}
$opts = array();
foreach($options as $key => $value) {
$opts[] = $key . '="' . $value . '"';
}
return '<input ' . implode(' ', $opts) . '/>';
}
(I have a few similar functions for <select> and <textarea> and so on)
When you're building fields you can do something like:
First Name: <?=input('first_name')?>
Last Name: <?=input('last_name')?>
Password: <?=input('password', array('type' => 'password'))?>
If you process your forms in the same page as the form itself, they will get auto filled if there are any errors. Most frameworks, though, do all of this for you (and in a much better way than the code above), I personally suggest CakePHP or CodeIgniter.
This is not done automatically. They get values from post / get and then assign the values the user typed to the template. What you see is html that was generated from the script that handled user values.
If you put your form and the form data processing in the same script, you can easily print out the data that has already been entered in the form, e.g.:
$valid = false;
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['name']) && $_POST['name'] == 'Hugo') {
$valid = true;
} else {
echo '<p>Seriously, you have to enter "Hugo"!</p>';
}
// more data processing
if ($valid) {
echo '<p>Everything’s fine!</p>';
}
}
if (!$valid) {
echo '<form action="" method="post">';
echo '<p>Please enter "Hugo": <input type="text" name="name" value="', (isset($_POST['name']) ? htmlspecialchars($_POST['name']) : ''), '"></p>';
echo '<p><input type="submit"></p>';
echo '</form>';
}
Well this is not nice example but that’s how it works.
a lot of frameworks do this job for you, so dont waste your time doing this manually
You'll have to check the data within the same file, and if it is correct, then you redirect to the correct location. Then you can use the $_POST or $_GET information the user posted and he can fix the error(s).
You can use two approachs (they're not mutually exclusive):
Use JavaScript to help the user before he submits the form. That way, you save a roundtrip to the server.
What you asked for:
In the form, fill the value attributes of the fields with the data sent back from the server. For example: you send a field name, which you get as $_POST['name'] in PHP (assuming you used method='post'. If you send back the data and modify that field adding value='<?php $_POST['name']; ?> you should get your data back.
If you're using a template or framework system (I've incorporated the Smarty engine into several projects of mine), you can usually tweak the templates so they automatically fill fields with values if they detect that the $_POST[$variable] value corresponding to their field is set.
As for the passwords, as far as I understand it (I could be wrong): it's a convention that minimizes the amount of time that password is being sent over the wire, hence shrinking the window for anyone who may be sniffing to pick up on the text. It's just good practice to leave password fields blank, is all.