39

I want to find out whether an incoming HTTP_REQUEST call from a third party website is coming from the list of domains that I defined.

I know that HTTP_REFERER can be used to find out where the third party domain is, but it is not secure enough. People can spoof it or use Telnet to fake it.

So, how about HTTP_ORIGIN? Is it sent from all browsers? Is it secure?

Also, can people fake the REMOTE_ADDR in a HTTP_REQUEST call?

TRiG
  • 9,249
  • 6
  • 50
  • 101
murvinlai
  • 43,517
  • 50
  • 120
  • 169
  • @RichardW11 where can i find info about $_SERVER['ORIGIN']? you sure is it correct? – Gianpaolo Di Nino Nov 12 '13 at 10:31
  • @GianpaoloDiNino Hi, I deleted my comment because I wanted to clarify. That day was busy, and the comment wasn't very accurate. Here is what I do know. When I was creating a javascript api, connecting via a php app. The mozilla browser wasn't accepting my request. When researching. It turned out that I was looking for HTTP_ORIGIN, however the browser was sending $_SERVER[ORIGIN]. To solve the problem I just created the script to look for either version. You can verify this on your end. It might also be the version of mozilla. Let me know if this helps. – RichardW11 Nov 12 '13 at 13:47
  • 1
    @RichardW11 i did not find anything about that. We use the http_origin + http_host as fallback. – Gianpaolo Di Nino Nov 12 '13 at 14:59
  • @GianpaoloDiNino yes odd. Here is screenshot from Mozilla. http://i.imgur.com/9YEvpfj.png Oddly enough, other javascript request on the site, doesn't have that header being sent. What browsers will send the http_host? – RichardW11 Nov 12 '13 at 15:25
  • 1
    @GianpaoloDiNino just doubled checked. When in the network view of mozilla, it shows Origin, but it turns out they are just cleaning up the headers to only show Origin. However, in reality it is sending http_origin. Thanks for challenging. Your help was appreciated.+1 – RichardW11 Nov 12 '13 at 15:45

6 Answers6

61

HTTP_ORIGIN is a way to protect against CSRF (Cross Site Request Forgery) requests. Currently it is implemented only by Chrome (as of Nov 2011). I tested Firefox and Opera, but they failed.

Its name in the request header is Origin. On the server in my PHP script I see it as HTTP_ORIGIN in the $_SERVER array. This header is sent only in some cases, when protection against CSRF is required (only POST should be sufficient). Here is list of all requests whether it is set or not:

https://wiki.mozilla.org/Security/Origin

  • Anchor tag - NO
  • Window navigation - NO
  • IMG - NO
  • iframe, embed, applet - YES
  • Form (GET and POST) - YES
  • SCRIPT - YES
  • stylesheets - NO
  • dependent loads from stylesheets - NO
  • Redirects - YES
  • XHR - YES

The Origin header is implemented only in Chrome, unfortunately. It was announced first in January 2010 on Google Chrome's blog:

http://blog.chromium.org/2010/01/security-in-depth-new-security-features.html

CSRF Protection via Origin Header

The Origin header is a new HTML5 feature that helps you defend your site against cross-site request forgery (CSRF) attacks. In a CSRF attack, a malicious web site, say attacker.com, instructs the user's browser to send an HTTP request to a target server, say example.com, that confuses the example.com server into performing some action. For example, if example.com is a webmail provider, the CSRF attack might trick example.com into forwarding an email message to the attacker.

The Origin header helps sites defend against CSRF attacks by identifying which web site generated the request. In the above example, example.com can see that the request came from the malicious web site because the Origin header contains the value http://attacker.com. To use the Origin header as a CSRF defense, a site should modify state only in response to requests that either (1) lack an Origin header or (2) have an Origin header with a white-listed value.

I am just implementing CSRF protection in my PHP script, I personally use Chrome, so that is sufficient for me, I hope other browsers will catch up with Chrome soon.

What is funny is that Mozilla invented that security feature, as you can read lots of documentation of that Origin header on its website, but they still didn't have time to implement it ;-)

HTTP_ORIGIN seems to contain only protocol and domain, without slash at the end: "http://www.example.com" - even if you submit the form from "http://www.example.com/myform/".

A simple protection against CSRF in PHP script:

if ($_SERVER['REQUEST_METHOD' == 'POST']) {
    if (isset($_SERVER['HTTP_ORIGIN'])) {
        $address = 'http://' . $_SERVER['SERVER_NAME'];
        if (strpos($address, $_SERVER['HTTP_ORIGIN']) !== 0) {
            exit('CSRF protection in POST request: detected invalid Origin header: ' . $_SERVER['HTTP_ORIGIN']);
        }
    }
}

This script could still be upgraded to support PORT other than 80 (Origin contains the port when it's different than 80), HTTPS connections, and submitting the forms from different subdomains (ex. sub.example.com => posting request to www.example.com).

Kalessin
  • 2,192
  • 2
  • 22
  • 24
Czarek Tomczak
  • 17,826
  • 5
  • 45
  • 54
  • Just a note that my Mac Chrome 18.0 says "Origin" in the header not "HTTP_ORIGIN". – Mauvis Ledford May 08 '12 at 22:12
  • 1
    @MauvisLedford: You mean the server side ? That code is php so "HTTP_ORIGIN" is php-only probably. Original header should be "Origin". That code was tested with Apache server, some of the $_SERVER keys might be missing or differ on other servers. – Czarek Tomczak May 26 '12 at 15:12
  • Checking the Origin header helps protect against XSRF but does not provide complete protection. Note that you report the the Origin header is not set for IMG tags meaning an attacker can use that to attack your site. Also you said that you only use Chrome. It's what browser your users use that matters. See http://j.mp/learn-xsrf to learn how to fully protect your app. – Vroo Aug 28 '13 at 01:55
  • @Vroo Nobody said it is a complete protection for everyone. IMG tag could be used for an attack only if you do not follow good practices. You should never allow to modify sensitive data through GET requests. – Czarek Tomczak Aug 28 '13 at 09:25
  • I added the comment because I thought people reading through your comment might think that checking Origin was enough. I didn't mean that you thought that. Security bugs are frequently caused by developers not following good practices sufficiently. For example, a bug that allows data to be modified via GET request combined with insufficient XSRF protection when Origin header is missing = vulnerability. – Vroo Sep 04 '13 at 21:02
30

HTTP_ORIGIN is neither sent by all browsers nor is it secure.

Nothing sent by the browser can ever be considered safe.

ceejayoz
  • 165,698
  • 38
  • 268
  • 341
  • 7
    Browsers play a vital role in enforcing web security. If you trust the browser, you can trust the `HTTP_ORIGIN` they send. There are instances when you don't want to trust any random HTTP client. In cases like that, your comment applies. Nothing from the browser can be trusted. – Manki Oct 18 '14 at 00:38
  • 9
    @Manki You can never trust the browser. – ceejayoz Oct 18 '14 at 04:28
  • 1
    @ceejayoz You can rely on the browser acting on the *users* best behalf. It won't protect *you* from the user, but it can help protect both developer and user from a third party (as in the case of CSRF/session-hijacking) when combined with HTTPS. – erb Oct 03 '16 at 07:17
  • @erb Meaningless if true (you still have to distrust the user/browser input), and false anyways. The browser could be infected with malware that makes it send out malicious requests the user isn't aware of. – ceejayoz Oct 03 '16 at 14:04
  • @ceejayoz If that's the case then the user's cookies have already been compromised and the user's session could be hijacked regardless. I'm not saying you should protect the server by relying on `HTTP_ORIGIN`, but you can protect the user from CSRF/session hijacking using it. – erb Oct 03 '16 at 15:24
  • @erb You seem to be focusing on CSRF, but there are other issues at play. A developer might save `HTTP_ORIGIN` into the database and cause a SQL vulnerability, for example. It should be treated like any other piece of data a malicious user could control. – ceejayoz Oct 03 '16 at 16:10
  • 6
    @ceejayoz I know there is more at play, and I never said it was a good idea to use it for anything else than CSRF protection. I just don't like blanket statements like *"Nothing sent by the browser can ever be considered safe"* when it's certainly safe for CSRF protection, it all depends on what you do with it. I don't say the server should trust the browser, I say that if the user uses a secure browser, then the server can use that to secure the *user* from attack. I still agree with the statement when used as a heuristic, but I find it slightly crude. Sorry if I'm nitpicking unnecessarily. – erb Oct 03 '16 at 16:33
16

People here are thinking about this all wrong -- the 'CORS' standard isn't so the server doesn't get hacked, even if it helps that in addition to what it does. The purpose is to allow 'THE BROWSER' to have a way of easing up on requests that go against the same origin policy. If the client and the server are on the same page, then the 'CLIENT' can decide whether or not to allow the request.

Obviously by having the server participate in the decision you are helping in the security process.

But it won't protect the server from unauthorized access - that is what passwords and cookies are for.

The client can be (as someone mentioned) a telnet tool, where every single thing crafted is fake.

But one of Chrome's, and FF's, etc, selling points is that they will help you by not allowing Javascript to go outside of the same origin sandbox, which means the only thing by default that can be compromised is the stuff that is on the 'attackers' own website. Or other sites that decide to not be secure.

CORS is the technology that allows you to say -- hey, I want users to be able to consume my snazzy service from the javascript on this other site they use. So I'm gonna add this site to my exceptions. Which means you are helping your authorized users poke a hole in their browser security for that particular site. Which means a hole that a hacker can exploit. Thus the care with which you set up the service, right?

This means that any site that doesn't have CORS set up is by default secure from Cross Site Scripting from a compliant browser (barring bugs and hacks of course). The browser will ask if this service wants to participate in the origin site's javascript, and if the cross site says "I don't know nothing about this damn site", then the browser's javascript engine will close the connection and dump the data.

So just to summarize -- CORS doesn't help you make thing secure. It helps you make a hole in your browsers ability to make a user more secure. But hopefully in a managed way.. and only for particular sites..

Gerard ONeill
  • 3,193
  • 31
  • 22
14

HTTP is a plain-text protocol. The ENTIRE request header/body structure can be faked to say anything you want.

Marc B
  • 340,537
  • 37
  • 382
  • 468
6

Upgraded:

function isOriginAllowed($incomingOrigin, $allowOrigin)
{
    $pattern = '/^http:\/\/([\w_-]+\.)*' . $allowOrigin . '$/';

    $allow = preg_match($pattern, $incomingOrigin);
    if ($allow)
    {
        return true;
    }
    else
    {
        return false;
    }
}

$incomingOrigin = array_key_exists('HTTP_ORIGIN', $_SERVER) ? $_SERVER['HTTP_ORIGIN'] : NULL;
    $allowOrigin    = $_SERVER['HTTP_HOST'];

    if ($incomingOrigin !== null && isOriginAllowed($incomingOrigin, $allowOrigin))
    {
        exit("CSRF protection in POST request: detected invalid Origin header: " . $incomingOrigin);
    }

Example:

  • http:// media.mydomain.com TRUE
  • http:// offline.mydomain.com TRUE
  • http:// domen1.mydomain.com TRUE
  • http:// domen_1.mydomain.com TRUE
  • http:// domen-1.mydomain.com TRUE
  • http:// ololomydomain.com FALSE
  • http:// mydomain.com TRUE
  • http:// pro.mydomain.com TRUE
  • http:// super.pro.mydomain.com TRUE
  • http:// super.pro.fakemydomain.com FALSE
  • http:// pro.fakemydomain.com FALSE
Sergey Karasev
  • 3,243
  • 2
  • 21
  • 24
5

Everything in the HTTP request can be faked.

Andy Lester
  • 81,480
  • 12
  • 93
  • 144
  • 11
    Except REMOTE_ADDR is not part of the HTTP protocol, and can't be effecively faked when using HTTP. – Michael Borgwardt Dec 30 '10 at 21:38
  • @MichaelBorgwardt what about proxy ? – Nasir Iqbal Oct 17 '17 at 12:07
  • 4
    @nasirbest It's not fake in that case either, the request really did come from the proxy. That the proxy was forwarding the request from somewhere else is beyond the scope of this question and not really different from a case where the request is done by a browser instance running on a server but controlled via VNC by a user sitting in front of a completely different computer. – Michael Borgwardt Oct 17 '17 at 13:06