18

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?

Howard Zoopaloopa
  • 3,798
  • 14
  • 48
  • 87
  • 1
    I hate to say it but you got the wrong answer for this great question. Please read my post, I have supporting evidence. – rook May 15 '10 at 20:49
  • @The: No, your answer is clearly wrong. I **can** forge `Referer` headers and it **is** a security hazard. – Nathan Osman May 16 '10 at 03:06
  • 6
    @george doesn't matter, this wouldn't be happening in *your* browser but someone else's browser. – Jeff Atwood May 16 '10 at 03:21
  • @Jeff: Sorry, but I don't understand what you're saying. What do you mean this won't be happening in *my* browser? – Nathan Osman May 16 '10 at 03:28
  • @Jeff: If I can forge requests in my browser, what's the problem with finding a browser exploit (hole) that will allow me to do it in other people's browsers? Firefox / Chrome / Opera, they all aren't exactly beacons of security. The **only** way to work is to assume *everything* that gets handed to you (from the browser, client, other sites, database) is completely and utterly **trash**. Sanitize your data and don't leave any room for error. If you have a form that has the potential to mess with things you should always authenticate it with something **you control**, like the SESSION. – Josh K May 16 '10 at 03:35
  • @George: He means that it's safe because the browsers play by the rules. And you can tamper with your browser and make it do that, but there is *no possible way* it could *ever* happen in someone else's browser unless it was deliberate. – Josh K May 16 '10 at 03:37
  • @Josh: Ah, I see. But you should **never** trust user input - **no matter what!** – Nathan Osman May 16 '10 at 03:48
  • @George: That's basically what we're getting at, yes. ;) – Josh K May 16 '10 at 03:56
  • 3
    @Jascha damn this is awesome question, and I don't mind getting the -3. – rook May 16 '10 at 04:00
  • 1
    Franky, there is no way to prevent someone from submitting form data from "somewhere else". If you are talking about CSRF, then checking the Origin header or using tokens is the right thing to do, but no, you cannot prevent me from submitting the form values via Telnet no matter how much you try. You can only build foolproof technologies on the top of your system to keep the kiddies away. – Tower May 16 '10 at 06:52
  • 2
    @Jascha, if you want to get responses for the code sample, then I feel that's a different question altogether. I'd propose to take it out of the question (and optionally post it as a new one), as the original question already created a lot of discussion. Please let's keep focussed on the REFERER part. Thanks. – Arjan May 16 '10 at 07:25
  • @Jascha this new code maybe considered a "deference in-depth approach", however both CSRF checks will be bypassed by a XSS vulnerability. You should use a random number like `uniqid(mt_rand(), true)`, not the password hash. It should be noted that there is very little evidence to support many of the counter arguments to my post. – rook May 16 '10 at 07:57
  • @The Rook, Thanks again, I took it off the page as it really is a whole other can of worms. But I did what you suggested. Actually, now I'm going with cookies and stored session ids using the uniqid() method... the site doesn't need to be THAT secure, so I hope this at least thwarts the average hack. – Howard Zoopaloopa May 16 '10 at 14:45

6 Answers6

10

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
  • 442,112
  • 142
  • 972
  • 1,088
3

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

gurun8
  • 3,526
  • 12
  • 36
  • 53
  • 1
    I'm using that as well. I'm just so terrified I'm looking to add as many failsafes as possible. – Howard Zoopaloopa May 15 '10 at 19:18
  • Have you considered using some form of captcha? – gurun8 May 15 '10 at 19:21
  • That is going to be level three. I thank you kindly for your responses. – Howard Zoopaloopa May 15 '10 at 19:25
  • 2
    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 May 15 '10 at 19:56
  • 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. – Howard Zoopaloopa May 16 '10 at 00:07
3

Actually yes, according to the OWASP CSRF Prevention Cheat Sheet in most cases 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. This vulnerability received a severity metric of 13.5 and 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 of all time.

rook
  • 66,304
  • 38
  • 162
  • 239
  • @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! – Howard Zoopaloopa May 15 '10 at 23:53
  • @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 May 16 '10 at 02:33
  • 2
    This answer is **WRONG** and should be avoided. As I mentioned in my answer, you should use some form of authentication. – Nathan Osman May 16 '10 at 03:06
  • 6
    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 May 16 '10 at 03:16
  • 8
    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 May 16 '10 at 03:18
  • 2
    @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. – Nathan Osman May 16 '10 at 03:21
  • 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 May 16 '10 at 03:22
  • 7
    Even then, this would block innocent clients/proxies as well which doesn't send the expected referer by configuration/nature. – BalusC May 16 '10 at 03:24
  • 1
    Exactly. I don't know **who** keeps upvoting this answer - it is definitely and fundamentally wrong. – Nathan Osman May 16 '10 at 03:29
  • 2
    @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 May 16 '10 at 03:32
  • 1
    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. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 03:36
  • 1
    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. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 03:37
  • @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 May 16 '10 at 03:38
  • 2
    @Long: According you *your* logic we don't need any safeguards because everyone will "play by the rules." – Josh K May 16 '10 at 03:39
  • 2
    @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. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 03:42
  • 3
    @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 May 16 '10 at 03:46
  • 3
    @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 May 16 '10 at 03:49
  • 1
    @Longpoke: Josh K. is **absolutely right.** Anything that can be hacked will be. – Nathan Osman May 16 '10 at 03:50
  • 1
    @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? – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 03:56
  • 4
    @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 May 16 '10 at 04:02
  • 1
    @Josh K, I read _all_ your posts. That point is irrelevant in this context. I doubt you even know what CSRF is. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 04:10
  • @Long: I think we can all agree that the question needs rewording either way. Why don't you fix it up? – Nathan Osman May 16 '10 at 04:32
  • 1
  • @George Edison actually its been heavily modified and I like it how it is. – rook May 16 '10 at 06:01
  • 6
    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. – Tower May 16 '10 at 06:42
  • @Kai Not in a post request. And checking a referrer isn't really "trusting" user input. Just sayin'. – Sam Becker May 16 '10 at 06:46
  • @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 May 16 '10 at 07:49
  • 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 May 20 '10 at 18:27
  • 1
    @Byron It is impossible to spoof the Referer on someone else's browser using CSRF. I have posted supporting evidence, you have not. – rook May 20 '10 at 19:05
  • 1
    @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. – Tower May 20 '10 at 19:43
  • @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 May 20 '10 at 19:52
  • @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 :( – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 20 '10 at 19:59
  • @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. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 20 '10 at 20:12
  • 2
    @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 May 20 '10 at 20:24
  • @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 May 20 '10 at 21:18
  • 1
    @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. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 20 '10 at 22:50
  • @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 May 20 '10 at 23:25
3

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.

Community
  • 1
  • 1
bobince
  • 528,062
  • 107
  • 651
  • 834
  • 2
    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 May 16 '10 at 23:54
2

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.

Community
  • 1
  • 1
  • 1
    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? – Nathan Osman May 16 '10 at 05:00
  • **NO**. Flash is known for it's constant stream of critical security flaws. And the rest of what you said is irrelevant. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 05:07
  • 4
    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 May 16 '10 at 06:21
  • @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 May 16 '10 at 06:37
  • @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. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 07:18
-3

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
  • 2,883
  • 2
  • 33
  • 43
  • 1
    **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. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 16 '10 at 15:04
  • correction to my previous comment: There are actually ways to use a single token, but this is not one of them. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ May 22 '10 at 13:24