views:

790

answers:

8

I'm putting together a site that will make itself available for user input. I was wondering if writing a function like:

if(getenv("HTTP_REFERER") != 'http://www.myURL.com/submitArea'){
        die('don\'t be an jerk, ruin your own site');   
    }else{
        // continue with form processing    
    }

is enough to prevent cross site form submissions.

EDIT: And if not, what is the best practice for preventing forms from being submitted from other hosts?

A: 

No, this is not enough. It is trivial for someone to insert the "Referer:" header into an HTTP request to that page.

You need some form of authentication.

George Edison
..........what?
Longpoke
@Long: And the reason for the downvote and short comment are...? After reading the other comment you made, you really are on the brink of getting in trouble. False downvoting is quite serious.
George Edison
@George Edison: Because these answers are all _wrong_, except for The Rook's. I wrote "...what", because there is no need for authentication to determine that _your_ code is being executed by code from _your_ domain, a simple boolean check will do, which is what the referer effectively is, ableit convoluted since the web lacks several fundamental security mechanisms.
Longpoke
@Long: **No, you are wrong.** The `Referer` **can** be spoofed. It is trivial. It is sent by the browser and as such *should not be trusted!*
George Edison
@Longpoke: you're confusing XSS with CSRF. XSS is indeed one of major sources for CSRF, but certainly not the only one. See [this answer](http://stackoverflow.com/questions/2397952/am-i-under-risk-of-csrf-attacks-in-a-post-form-that-doesnt-require-the-user-to-b/2398772#2398772) for an example.
BalusC
Ohh man. Imagine if my kernel by default would let any process use any syscall with any parameters. *This* is what the web is. The web doesn't check _who_ called your code. A user sessioned into your website is the process. You don't want other processes invoking arbitrary functions on your process. *This* is what checking the referer prevents. If your browser's referer can be forced into lying about where it came from by some code on some website, this is clearly a major bug in the browser and defeats the concept of referer even existing.
Longpoke
@Longpoke: You misunderstand. I'm not saying you could necessarily force *someone else's* browser into exploiting the `Referer`, but that **you yourself** could forge the `Referer` using your own code.
George Edison
@George Edison if you still want to stand by this answer you must post some supporting evidence. Ideally you should post a CSRF exploit that you have written. Here are some that i have written (http://milw0rm.com/author/677)
Rook
@Long: No, I'm not going to write an exploit - I don't do that sort of thing. It doesn't matter anyway - in theory, any HTTP header received can contain any data **no matter what you expect.**
George Edison
@George Edison exactly my point you don't write exploit code so you don't fully understand the issue at hand. How can you write a security system without fully testing it?
Rook
@George Edison: It is irrelevant that the user can change his referrer at will, he can also give out his pending anti-csrf nonce to everyone at will. **Did you even read the qeustion?**
Longpoke
@Long: Maybe the question needs rewording cuz we don't understand what you're saying the way it's written.
George Edison
@George Edison: It's very simple, OP is asking if his CSRF check is secure, and it is.
Longpoke
Spoofing the referrer is easy, but on *your computer* you might as well change the whole request; the critical bit is the submitted data (which has to be confirmed and sanitized anyway, but that's an other issue). The question is: can it help you steal sessions of other users?
Kobi
This is not about validation/filtering, this is about CSRF.
Kai Sellgren
@Kai: Then yes, you need to change the question's wording.
George Edison
Why the sudden downvotes? Any reason?
George Edison
@GeorgeEdison - not just downvotes, this answer is now getting flagged as Spam. Don't know why precisely, but I guess some people don't like the tone of your answer, or the general tenor of the associated comments.
APC
@George: the reason is probably the answer being wrong, as many people tried to point out
Andreas Bonini
+5  A: 

Nope - HTTP_REFERER can be freely spoofed on client side and is not a reliable indicator of where a request came from.

Update: I misread the part about cross site forgery: For this, checking the referer is a valid security measure, because CSRF rely on manipulated links pointing to protected pages (that the attacked user has privileges on). User @Rook is correct.

The only exception is if the attack can happen from within the web application that is being attacked, e.g. by injecting malicious JavaScript code. In that case, a referer check is useless because the attack is coming from a "safe" URL, but so is arguably a solution based on a session or one-time token, because the token is in reach of the malicious JavaScript and can be easily retrieved.

However, using a one-time token is highly preferable to protect against this kind of attacks because HTTP_REFERER is stripped out by some proxies.

Pekka
I'm assuming I wouldn't want someone slamming my form handler from another url.
Jascha
@Jascha the most adequate way would be using a session or a one-time token that gets set in the form, and gets checked when you process the form. (You would store the token in a database.) That way, you could at least make sure that people have to request your form first (if that really is of help.)
Pekka
@pekka, Awesome responses. Thank you.
Jascha
When an adversary can inject javascript to run in the actual domain instead of requesting from another domain, it's **XSS**, not CSRF. The solution is simple, don't allow people to inject javascript in your page.
Longpoke
@Longpoke point taken, you're right.
Pekka
+2  A: 

Using a SESSION will most likely be the better route to prevent cross site form submissions.

gurun8
I'm using that as well. I'm just so terrified I'm looking to add as many failsafes as possible.
Jascha
Have you considered using some form of captcha?
gurun8
That is going to be level three. I thank you kindly for your responses.
Jascha
If you're going hog wild like that with multiple levels of security, throwing in a HTTP_REFERER checker into the mix won't hurt anything. It might be bit overkill but if it makes you or your client sleep better at night, so be it, right? Using it as a single line of defense is not the best choice.
gurun8
Exactly. The funny thing is, this time the client is me, and it makes me more anxious as it's my own reputation on the line. I don't want to enter the social scene with egg all over my face.
Jascha
+3  A: 

Actually yes, checking the referer is enough to patch a CSRF vulnerability. Although it is trivial to spoof the referer on your OWN BROWSER it is impossible to spoof it on another browser (via CSRF) because it breaks the rules.

In fact checking the referer is very common to see on embedded network hardware where Memory is scarce. Motorola does this for their Surfboard Cable Modems. I know this first hand, because I hacked them with csrf and then they patched it using a referer check. According to the Department of Homeland Security this is the most dangerous CSRF vulnerability ever discovered and in the top 1,000 most dangerous software flaws.

Rook
@the rook, thanks so much for sending this. I'm going to add all of the elements supplied in these answers, but seeing the "Referer BLOCKED BLOCKED BLOCKED BLOCKED" made me breath a little easier. thanks!
Jascha
@Jascha I'm happy to help.
Rook
-1 to every answer except this.
Longpoke
@Longpoke Hah, damn, although I usually vote people down for suggesting a vulnerability this specific case isn't well known. Google's browser sec handbook is awesome, everyone should read it.
Rook
This answer is **WRONG** and should be avoided. As I mentioned in my answer, you should use some form of authentication.
George Edison
Rules mean **nothing**. It it comes from the browser or the user it **should never ever ever be trusted**. That's just how it is.
Josh K
after some thought, I believe Rook is correct. When it comes to browsers and security, I always assume *some* browser, somewhere, is going to screw this up, but per the google reference, [they all get it right](http://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest). So unless you're talking about custom executables, or if you want to be totally safe -- which is completely defensible -- checking referer might be good enough.
Jeff Atwood
@Jeff: But all it takes is someone writing their *own* browser and **poof!** it all goes down in smoke. Besides, the post you mention just covers AJAX calls.
George Edison
Am I going to get an email about my comment edit? Sure remove a the profanity (sorry) but is the rest really that awful?
Josh K
Even then, this would block innocent clients/proxies as well which doesn't send the expected referer by configuration/nature.
BalusC
Exactly. I don't know **who** keeps upvoting this answer - it is definitely and fundamentally wrong.
George Edison
@George: Agreed. It's like saying you never need police officers because *everyone will follow the rules*. Assume **nobody** will follow the rules and you'll be much safer.
Josh K
Do you guys not understand the difference between untrusted code can do and what a user can intentionally do to screw himself over? I'm sure the only reason SO doesn't rely on referer only is because some people disable their referers due to paranoia.
Longpoke
According to you people's logic, if someone uses a nonce in each POST request, I could modify my browser such that remote code from other sites will go discover that nonce and use it.
Longpoke
@Longpoke: In which case this solution would be **crap**. Do you understand that there's a difference between *in theory* and *in practice*?
Josh K
@Long: According you *your* logic we don't need any safeguards because everyone will "play by the rules."
Josh K
@Josh K: Sorry, but you just don't understand a very fundamental point here. You **CAN'T** use your website to force someone's browser to spoof a referer. If you can, the browser is **BROKEN**, and thus insecure.
Longpoke
@Long: I'm sorry, just like you can't say, attack someone's computer to create a DDOS attack? Done that. Can't install a trojan horse that will allow you to modify the browser? Done that. Can't exploit a **security hole in the browser**? Done that. I utterly **fail** to see the merit in saying "You can't do it because it's not allowed. If you can't then it's broken" when you **cannot** guarantee the validity of **any** user input **whatsoever**. **THAT** is the fundamental point here, not if the browser can be compromised.
Josh K
@Jeff Atwood Thank you very much, and at the rest of you **WOW** I had no idea this would be so controversial an everyone must read the Google Browser sec handbook, even if you agree with me.
Rook
@Longpoke: Josh K. is **absolutely right.** Anything that can be hacked will be.
George Edison
@Josh K, please read the question, the accepted answer, and go read up on CSRF, and _then_, post something, because you aren't even talking in the context anymore... What you are arguing for is completely irrelevant. An exploit is game over, what's your point?
Longpoke
*[sigh]* _` ` ` `_
George Edison
@Long: Please re-read my *many* comments. My point is that if you get information from the browser it should **never** be trusted. **Ever**. Can I make it ultra bold? ****Never****, ****ever****. Guess not, but I hope you get my point.
Josh K
@Josh K, I read _all_ your posts. That point is irrelevant in this context. I doubt you even know what CSRF is.
Longpoke
@Long: I think we can all agree that the question needs rewording either way. Why don't you fix it up?
George Edison
@George Edison: No, it doesn't.
Longpoke
@George Edison actually its been heavily modified and I like it how it is.
Rook
This is wrong for two reasons. First, the header is often omitted. Second, if the CSRF attack happens on the actual site, the referrer will be correct. For example, placing an image on the actual site in some comments area can launch a CSRF attack. Other than that, the claims of some people about rules are irrelevant. They probably do not understand what CSRF really is.
Kai Sellgren
@Kai Not in a post request. And checking a referrer isn't really "trusting" user input. Just sayin'.
Sam152
@Kai, What you described is XSS.
Longpoke
@Kai Sellgren What you are describing would be a method of bypassing this CSRF security system specifically for GET based csrf on *some* applications. Clearly this patch works well for Motorola's modems, but if you where to allow custom image url's make sure you use POST for important state changes (such as changing your password). This use of POST vs GET is covered by RFC 2616 parts 9.3 and 9.5, and yes I realize that most web app developers ignore RFC's.
Rook
If the "Referer" can be spoofed (or removed), it will be. Comparing a session token with a POST token is easier and more secure.
Lotus Notes
@Byron It is impossible to spoof the Referer on someone else's browser using CSRF. I have posted supporting evidence, you have not.
Rook
@Longpoke No, what I described had nothing to do with XSS. In fact, it had nothing to do with JavaScript or "scripting". It was only about sending requests on the behalf of the user.
Kai Sellgren
@The Rook Only if they're using one of those browsers. I guess you're right in that it will cover almost every situation, but why do this method if there's the possibility? And what happens if the REFERER header has been hidden? The only downside I can think of for CSRF session tokens is that they require cookies to be enabled unless you are passing session id's through the url. However, CSRF usually only targets priveleges of logged-in users. If you regenerate the token for every form, it seems completely hackproof to me. Although thanks for opening my eyes, I wish I could undo my downvote.
Lotus Notes
@Kai, hmm actually you're right, it's not XSS. But the referrer method is still secure as long as you don't let people post image links to your authenticated pages or just use POST for authenticated actions. If only the web was built to be secure :(
Longpoke
@Byron Is it possible to use CSRF tokens in cookies? Do you have an example of this implemented? AFAIK the only way to do it right is to send them through GET/POST requests and generate a new one each time. Either way you still need a cookie to avoid being logged out when you close the page and open a new one. "And what happens if the REFERER header has been hidden" User gets blocked.
Longpoke
@Byron Thats exactly why this question is so awesome! I think a lot of people have learned something from this (even Jeff Atwood!). I went ahead and made an edit to the page if anyone wants to change their vote.
Rook
@Longpoke If I am correct, CSRF tokens require cookies unless you use session id's in the url (generally considered unsafe). The idea is that you're comparing that the token stored in the Session matches the one embedded in a hidden field from the form over POST data. Remember that using sessions stores a cookie with the session identifier on the client's machine, but you could also do it manually with your own cookie if you wanted instead of using sessions. @The Rook Still can't change my vote :(. Anyway, how do you address the problem of the missing 'REFERER'?
Lotus Notes
@Byron, you indeed do _not_ need a cookie for POST tokens. Ex: **1)** User visits site with no cookies. **2)** User logs in through POST. **3)** Site generates a page such that every link includes a token in it, storing `{token -> user_id}` in a persistent map **4)** User clicks a link, submitting the token. **5)** Server drops the token after processing the request, proceed to step **3**. It's insecure to put tokens in GET requests because the user could then copy the link to an outside source; the user should be able to copy whatever he wants, he doesn't have to care if it's insecure.
Longpoke
@Longpoke Ahh, I forgot about that method. But yeah, I prefer having pretty url's so session id's or CSRF token's in GET parameters are not ideal.
Lotus Notes
+2  A: 

Yes, it's secure

Unfortunately, the holy text encourages to provide an option to disable the referrer (but you still can't invent your own mechanism of what a referrer is); so indeed, someone could disable referrers on his browser, thus denying himself access to your site.

Your solution is secure, but users can legitimately complain if your site only works with referrers enabled.

This is very sad because it now means that there is no sane way to ensure your site isn't secure against CSRF.

Alternative

The only other thing you can really do is put a nonce in each authenticated request to your web service. This however is somewhat dangerous because you have to make sure every request point on your web service validates the nonce. However, you can use a framework to do this for you, to somewhat migitate this annoyance. Stack Overflow itself seems to be using nonces.

However

Referrers in principle are a bit more secure because you can just apply a global rule that says no request can take place unless the referrer is in the same domain. This is acceptable if you are willing to drop users who disable referrers.

Contrary to what nonsense people are saying here, you can't issue an HTTP request with a spoofed header from non-privileged browser code. As expected, Flash was able to do this once to bypass anti-csrf that relies on referrer, but it was patched. Why was it patched? Because the HTTP RFC dictates what the referrer is, and thus, you are not allowed to change the meaning of it in your client code, lest your client be insecure.

Someone here even claimed that a Java applet can issue arbitrary headers over HTTP to another domain, but that is simply not the case because a sandboxed Java applet is not allowed to make requests to anything, except the domain on which it was loaded from. He quickly removed his comment before anyone could correct him...

Case in point

A web browser is an HTTP client. An HTTP client must conform to the HTTP RFCs; thus it has to adhere to what the RFC states a referrer header should look like. Since a web browser is an HTTP client, any application embedded in a web browser must not be able to make requests that violate HTTP protocol. The fact is that every violation of a standard is a potential security hole.

In any case: there is no proper way to determine weather a request is from your domain or an adversary's domain. This is just one of the many sad defects of the web. You must use one of these mentioned workarounds.

Longpoke
What nonsense! You say yourself `Flash was able to do this once to bypass anti-csrf that relies on referrer, but it was patched.` So what about the people who are using the unpatched version? You clearly do **not** understand what we are saying. **ANY REQUEST CAN BE FORGED.** The actual TCP packets could be manipulated... then what?
George Edison
**NO**. Flash is known for it's constant stream of critical security flaws. And the rest of what you said is irrelevant.
Longpoke
the forgery issue is less relevant than the fact that **some proxies (or crazily configured browsers) will strip all referrers**, as BalusC noted in a comment on the question. So if you demand a certain referer, you may be blocking legitimate users as well.
Jeff Atwood
@Long: You don't think that some people never update their software? What dream land are you living in? In theory it's secure, in practice if it's so important then there should be additional methods in place to secure it, namely use a `SESSION` and a unique auth_token in the form.
Josh K
@Josh L, most browsers are coded in C, and have the occasional stack smash vulnerability and if not, semantic vulnerabilities. Using referrer is just as secure as an auth token. Jeff is correct, the only bad thing that can happen is the user gets blocked. It's just as likely for a browser to break referrer rules as it is for the browser to have some other vulnerability.
Longpoke
Why was this downvoted? No reason?
Longpoke
+2  A: 

Whilst it is impossible to spoof a Referer in another user's browser, it is easy to spoof a lack-of-referrer (eg. using a meta-refresh), in addition to some user-agents not sending a Referer at all.

So either you allow missing-referrer and have non-watertight XSRF protection, or you require a referrer that matches your site, in which case you take a big hit to accessibility. That hit might be acceptable if the only person using the script is you, and you know you'll always be using a browser/firewall/proxy/etc combination that passes referrers through reliably. But for anything you expect other people to use, it's generally not a good idea.

Referer is quite a weak anti-XSRF mechanism. Much better to use a per-user/event token issued by the server that must come back to the server to validate the submission.

$query = "SELECT * FROM users WHERE name = '$name'";

Potential SQL injection vulnerability. Please use mysql_real_escape_string or parameterised queries.

input.setAttribute('name', 'add_bar');

input.setAttribute('value', '');

Don't use setAttribute on HTML attributes. There are bugs in IE that stop it working in some cases, and there are some attributes that don't do what you think. For example, setting the value attribute is not the same as setting the value property. The property holds the current value of the form field; the attribute only holds the ‘default value’ of the field, to which it will be reset if an <input type="reset"> is used. This maps to the defaultValue property. In some browsers, setting the default value also sets the value, but this is non-standard and not to be relied upon.

Use the DOM Level 1 HTML properties, they're both more readable and more reliable:

input.name= 'add_bar';
input.value= <?php echo json_encode(generate_session_token(), JSON_HEX_TAG); ?>;

Use json_encode to create values for JavaScript literals. Although you can be sure an MD5-sum will not contain characters special to JS like ' or \, or the </ sequence that ends a <script> block (against which the HEX_TAG argument is protecting), it's not the output template's job to know what the session token may contain. This is a safe way to output any string into a <script> block.

See this question for an approach to generating anti-XSRF tokens that requires no extra token-storage in the session or database.

bobince
It should be noted that XSS bypasses both token based xsrf protection as well as a referer check. Would you care to elaborate on how one approach is weaker than another?
Rook
A: 

Actually, you can use a dynamic address for that form. Like when user go to your form you forward him to a page with random address such as www.something.com/form.php forwarded to www.something.com/form.php?13mklasdkl34123 where 13mklasdkl34123 is randomly generated for each user and store it in $_SESSION. Upon receiving form submit, you can check the referrer address is the address you generated for the user. Referrer can be spoofed, but a dynamic referring cannot as the spoofer cannot know the address unless he (individually) visit your form page.

VOX
**NO**. 1. Referrer being spoofable is not an issue here. The only issue about the referrers solution is that it denies service to some users. 2. Your solution is flawed. If you do it this way, you **MUST** generate a new token for **every** request. This means each time you load a page, the server stores a token corresponding to your session id, and all links outputted by the server have that token in them. When you load a new page, the process is repeated. You **do not** use the same token for each request, or you can be owned in several ways such as screenshots and referrals to other sites.
Longpoke
correction to my previous comment: There are actually ways to use a single token, but this is not one of them.
Longpoke
+1  A: 

Unfortunately, some privacy conscious web users ensure their browser does not pass anything for the referer, meaning you might end up blocking 'real' users.