13

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.

Juan
  • 151
  • 1
  • 2
  • 9
  • 1
    I've also tried checking the URL in PHP, but then I found out that was an amateur mistake. – Juan Jul 01 '09 at 16:43
  • Ok. I've looked over what you guys have given me, and I have thought of a different idea using what you guys gave me...What if I randomized a set of characters => converted to a variable, posted it as the hidden value so it changes every time, and check for that when my script runs? – Juan Jul 01 '09 at 17:54
  • amateur mistake again.....sorry. – Juan Jul 01 '09 at 18:00
  • thanks everyone! couldn't have done it without everyone's help! – Juan Jul 01 '09 at 21:30

7 Answers7

15

"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 ...
   ...........................................

}
T.Todua
  • 53,146
  • 19
  • 236
  • 237
  • This allows a 500 Internal Server Error for "Uncaught Exception - Encryption failure" (I determined it to be at line 143 of Cryptor script) when the 'temp_random' string is missing or incomplete (as I tested using Developer Tools.) I resolved this by implementing a "try...catch" statement. I also chose to add a 403 server response for the exit message. Additionally, there is a misspelling of HTTP_REFERER – Tim R May 14 '23 at 03:22
10

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
JohannesB
  • 1,995
  • 21
  • 35
Tom Ritter
  • 99,986
  • 30
  • 138
  • 174
8

$_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.

AndyMcKenna
  • 2,607
  • 3
  • 26
  • 35
  • What if I did randomized the number and posted it as a hidden value, but it changes. – Juan Jul 01 '09 at 17:55
  • 1
    oh wait, they can just remove that hidden value and make it work....lol. dangit,just when I think i'm getting the grips of PHP. – Juan Jul 01 '09 at 17:56
  • I think I finally figured it out, I used session_start, made a sessions variable when the form was shown, and then checked to see if the variable was set to submit! – Juan Jul 01 '09 at 21:25
  • Are you using server side sessions? – AndyMcKenna Jul 01 '09 at 23:02
  • No, I am not using server side sessions. And my logic up above didn't didn't work. sorry guys. – Juan Jul 02 '09 at 15:38
  • the only problem when I MD5 my string is that, the person can grab that hidden value and place it on their site. This will only work temporarily until a new string is generated in the hidden value. in other words until someone refreshes the page. – Juan Jul 02 '09 at 16:01
  • figured out how to use the http referer on this page: http://www.trap17.com/index.php/Check-Referrer-Prevent-Linking-Sites_t40295.html – Juan Jul 06 '09 at 19:54
  • 2
    You can't trust the HTTP_Referrer though, it's easily spoofed. – AndyMcKenna Jul 06 '09 at 20:29
  • yeah, i'm also using the randomized hidden field and checking it before submit using sessions. any other ideas that are useful for checking unique user identity and preventing CSRF? – Juan Jul 06 '09 at 20:36
  • no kidding, about easily spoofing the http referer - I used the Firefox Add On: https://addons.mozilla.org/en-US/firefox/addon/953 and quickly spoofed the referer, Thanks for the heads up! – Juan Jul 06 '09 at 20:44
  • Oh ok, I thought you meant you were only check the Referrer. – AndyMcKenna Jul 06 '09 at 21:15
2

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
}
Matt Bridges
  • 48,277
  • 7
  • 47
  • 61
  • 2
    REMOTE_ADDR could be spoofed, so if there were a MITM attack this would prove worthless. Also after I've made a single request I could easily make more by simply duplicating the hash, which would render this worthless, the 'unique' part of the hash needs to be more 'unique', which is why I chose to use time() witch provides the current epoch time. – UnkwnTech Jul 01 '09 at 16:52
  • The "attacker" isn't the client, its another server serving a form page to *his* clients. – Matt Bridges Jul 01 '09 at 16:54
  • 1
    Either way it can still happen just as easy. – UnkwnTech Jul 01 '09 at 16:56
  • 1
    I believe the same AJAX-Remote-Loading comment on Unkwntech's answer applies to this as well. True XSRF protection requires cookies, hidden form input, and time-within-epsilon considerations. – Tom Ritter Jul 01 '09 at 16:58
0

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.

Peter Stone
  • 3,756
  • 4
  • 23
  • 14
0

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.
SUKUMAR S
  • 187
  • 2
  • 8
0

There's a typo in the highest score answer. It should be $_SERVER['HTTP_REFERER'] instead of $_SERVER['HTTP_REFERRER'].

packam49
  • 1
  • 2
  • 1
    You should add a comment to the accepted answer with this info instead of creating a new answer. – Adrian Ghiuta Feb 14 '23 at 21:38
  • @AdrianGhiuta You are correct, however, commenting requires having a minimum of 50 reputation points. Answer and Edit are the only response options available for new members. – Tim R May 14 '23 at 03:27