Protecting your site from form tampering....

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

timvw wrote: This method does prevent CSRF. It requires that the user has visited my form page (to recieve a token)..

I don't care if he uses the data to generate a custom form, or a script that posts it (This is the nature of HTTP). All i care about is that he has a valid token..

External parties don't have this token, so they can't trick the user to post it...
If that is all that you're solving with that method, then the simple fact that you're using post not get as the form submission method (and you pull the variables from $_POST on the receiving page) is sufficient. The token doesn't gain you anything extra. As the malicious logic link uses a GET style query string....
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

It does make a difference...

For example an evil site with some javascript that makes your browser post to target site..
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

If the evil JS can do that, it might also be able to do the screen scrapping to inject the copied token.
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

Agreed :(
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

Ah, but then you need to be logged in to get the token! So it can't get the token! Ahaha!
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Well we're getting off-topic, but in his model he is protecting a real, logged-in user, from visiting a malicious "Cross Site Scripting" style site. So the user is logged in, and the other script could see the token.
timvw
DevNet Master
Posts: 4897
Joined: Mon Jan 19, 2004 11:11 pm
Location: Leuven, Belgium

Post by timvw »

Back to the real problem:
How to accomplish the rotation of keys? (You don't want that the row <-> key mapping is constant in time)
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Well in the "Solution 2" method of generate and store a mapping table, you could just start your index at random(0,1000000). So the lookup table changes for every form, but because you've stored either the whole LUT in the session or just the starting offset you're safe.

In the case of "Solution 1" (HMACing), I'd probably take a generation approach and use cron to expire/rotate keys.

Your secret_config.inc has

Code: Select all

$current_hash_key=...;
$current_secret_key=...;
$last_hash_key=...;
$last_secret_key=...'
Every N hours (4-8 is probably more than sufficient for most sites) rotate $current to $last and generate a new pair. (suitable use of MD5(getmypid,time, random, etc) is good enough for these.)

If the current key doesn't yield a match and you're within M minutes of the changeover, re-try using the last value. (Set M depending on how long an average user is to remain on the form without submitting it, probably capping at N/2).
josh
DevNet Master
Posts: 4872
Joined: Wed Feb 11, 2004 3:23 pm
Location: Palm beach, Florida

Post by josh »

How about this:

Add a column to your users table `token`, then every page view you update it with sha1(uniqid()):

Code: Select all

<?
$currenttoken=sha1(uniqid());

$oldtoken = mysql_query("SELECT `token` FROM `users` WHERE `user` = '$user' LIMIT 1");
// mysql_fetch_array and stuff

mysql_query("UPDATE `users` SET `token` = '$currenttoken' WHERE `user` = '$user' LIMIT 1 ; ");
?>
output $currenttoken to all forms, in every form have a hidden value "is_a_form" set it to 1. Then on any script that accepts form input, or even after the above code, on every page:

Code: Select all

<?
if ($_POST['is_a_form']) {
    if ($_POST['token']==$oldtoken) {
         // Accept the form
    } else {
        // Form tampered
    }
}
?>
This would probably be sufficient for any of my php applications.
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

That suffers from at least two problems.

1) its still vulnerable to the screen scraping forgery attack.

2) it would break anyone who opens multiple pages/tabs on your site (only the most recent tab would work)
User avatar
Ambush Commander
DevNet Master
Posts: 3698
Joined: Mon Oct 25, 2004 9:29 pm
Location: New Jersey, US

Post by Ambush Commander »

screen scraping forgery attack.
Isn't that the responsibility of the browser to secure that?
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Ambush Commander wrote:
screen scraping forgery attack.
Isn't that the responsibility of the browser to secure that?
No, there's no way for it to do so. (And besides that would be trusting something under the control of the user, which you can never do....)
User avatar
shiflett
Forum Contributor
Posts: 124
Joined: Sun Feb 06, 2005 11:22 am

Post by shiflett »

I think I might be able to clear up some confusion.
nielsene wrote:It stops only the trivial CRSF exploit, but not a forged POST submission which is just as trivial. It does NOT test if they are coming from your page.

I could write a page on my website that POST'd to yours. I go to your site, view source, and add the token from your page to my page. Now your receiving script thinks my form is legitimate.
I don't think you understand what CSRF is, so let me briefly explain. (If I'm mistaken, please accept my apologies. My opinion is based on your comments here.)

CSRF is a category that includes all attacks where the attacker causes a victim to send arbitrary HTTP requests to a target site. Thus, the client sending the request is not the attacker, and this form of attack is especially effective when the victim has an established relationship with the target site. Most forms of access control, including those at the network layer, can be avoided with a CSRF attack.

Thus, by including a token in each form, you can effectively prevent CSRF. Yes, you can visit the form and view source, but this gains you nothing. You can already submit the form yourself legitimately - spoofing it only lets you avoid client-side restrictions. Knowing your own token does not help you forge a request from someone else - you would have to know their token.

Hope that helps.
User avatar
nielsene
DevNet Resident
Posts: 1834
Joined: Fri Aug 16, 2002 8:57 am
Location: Watertown, MA

Post by nielsene »

Yes, I'm not altogether clear on CSRF. I'm used to XSS (Cross Site Scripting), the two seem very similar to me.

The token method does not stop XSS, nor does it look like it stops CSRF. The malicious logic that the attacker's site causes the third-party client/victim to issue a dangerous request to the orginial server, can still acquire the clients token to use it its created request.
User avatar
shiflett
Forum Contributor
Posts: 124
Joined: Sun Feb 06, 2005 11:22 am

Post by shiflett »

nielsene wrote:Yes, I'm not altogether clear on CSRF. I'm used to XSS (Cross Site Scripting), the two seem very similar to me.
Yeah, they're actually not very similar at all. Some people within the security community are trying to promote a new name - session riding. Their reason is to more clearly distinguish CSRF from XSS. I understand the motivation, but I personally feel that cross-site request forgery is much more accurate description of what is happening.
nielsene wrote:The token method does not stop XSS, nor does it look like it stops CSRF. The malicious logic that the attacker's site causes the third-party client/victim to issue a dangerous request to the orginial server, can still acquire the clients token to use it its created request.
The token has nothing to do with XSS, so you're right about that.

For the other, try to think of a specific exploit that would allow you to obtain the victim's token. If you can think of an exploit that works, you should immediately share this, because using a token like this is currently considered the best practice to protect an application from CSRF. I think you'll be able to better appreciate the value in this approach as you try to develop an exploit. I'd be happy to help poke holes in anything you try, and I certainly hope there are holes, else we're all in big trouble. :-)
Post Reply