27

I'm making a system with CakePHP that needs to be decently secure, because we're dealing with money, customer's accounts, etc. So far everything's been working great, until I've had to integrate with a payments platform in which I need to redirect to their site and they redirect back to mine.

This works fine in my dev machine (debug = 2), but in production, when the customer is redirected back, he gets a login prompt instead of landing back in his "logged in area". After much digging I found this is because CakePHP sets session.referer_check, which invalidates sessions if the HTTP_REFERER comes from another host than mine.

Now, normally, i'd disable this without a second thought, but in this system i'm a bit more concerned about security than normal.

My question is what exactly is session.referer_check supposed to protect me from?
What kind of attack/exploit/bad thing can be done to my site if I turn it off?

I'm guessing that there's gotta be some reason why this exists, but I can't imagine what it'd protect me from.

Could you give me any ideas?
Is it safe disabling this?

Thank you
Daniel

Daniel Magliola
  • 30,898
  • 61
  • 164
  • 243
  • Are you 100% it's checking the referrer and not a token sent with forms in a previous request to protect against CSRF? – Pablo Fernandez Feb 01 '11 at 15:50
  • 100% sure. CakePHP is not checking the referrer, it's just turning on a referer_check (a PHP feature that checks the referrer itself: http://www.php.net/manual/en/session.configuration.php#ini.session.referer-check) - Also, the result I see is logging people off. What CakePHP does when it doesn't like the CSRF token it gets (or when it doesn't get one) is "blackholing" (blank page, basically) – Daniel Magliola Feb 01 '11 at 15:57
  • Similar: http://stackoverflow.com/questions/22079477/cakephp-session-is-lost-after-an-oauth-redirect – trante Feb 27 '14 at 21:16

3 Answers3

24

This is to provide limited protection for Session Fixation and CSRF/XSRF. Checking the referer is a valid method of stopping xsrf. A better method of stopping session fixation is Session.use_only_cookies, because a hacker cannot set a cookie on a victims browser for a domain he doesn't already control.

However, Session.referer_check is easy to bypass. Its just looking for a substring in the referer domain. If the substring is missing all together, which happens if the originating url is https:// then the session id will be invalid. However, becuase its a substring and not a full string then you could bypass this for www.somedomain.com by refering from www.somedomain.com.some_hacker.com. So in short, I think this is completely useless.

mikej
  • 65,295
  • 17
  • 152
  • 131
rook
  • 66,304
  • 38
  • 162
  • 239
4

Bear in mind that basically all the referer_check is doing is something like:

$pattern = "/^http:\/\/www\.myurl\.com(\/.*)*$/";

if(!empty($_SERVER['HTTP_REFERER']) && !preg_match($pattern, $_SERVER['HTTP_REFERER'])) {
   session_destroy();
}

It is annoying that PHPs built in referer_check won't accept an array of URLs, but you can always make your own one that does.

So for CakePHP, you could do something like the following:

// ADD THIS TO /app/config/config.php
$config['CustomSecurity'] = array(
    'accept_referers' => array(
        'http://www.my_site.com',
        'https://www.other_allowed_referer.com',
    )
);

// ADD THIS TO /app/app_controller.php

private function referer_check(){
   if(!empty($_SERVER['HTTP_REFERER'])) {
      $accept_referers = Configure::read('CustomSecurity.accept_referers');
      $referer_accepted = false;
      foreach($accept_referers as $referer) {
         $pattern =  '/^'.preg_replace('/(\.|\/)/','\\\$1',$referer).'(\/.*)*$/';
         if(preg_match($pattern, $_SERVER['HTTP_REFERER'])) 
            $referer_accepted = true;
      }
      if(!$referer_accepted) {
         $this->Session->destroy();
         exit;
      }
   }        
}

AND IN YOUR app_controller::before_filter FUNCTION, CALL:

$this->referer_check();

... or something like that anyway ... sorry about the code formatting, the textarea was being obtuse :)

Arth
  • 12,789
  • 5
  • 37
  • 69
James
  • 41
  • 2
3

Checking the referer in this way can help protect against Cross-site request forgery.

Ideally you'd want a way to have the referer check match either your own domain or the domain of the payment platform, but as it is a simple substring check rather than a pattern match I don't think this would be possible.

If you disabled this you should put other measures in place to protect against such attacks.

mikej
  • 65,295
  • 17
  • 152
  • 131
  • Referer checks can be spoofed. Nonces or one-time tokens are the standard for protecting against CSRF. – gravelpot Feb 01 '11 at 15:05
  • I assumed as much. Now, in my site, you can't change **anything** using GET's, only POST's, the site won't remember you after you close the browser, and CakePHP already does the "magic token" generation to avoid XSRF form POSTs. With all that in mind, can I safely disable this? – Daniel Magliola Feb 01 '11 at 15:05
  • @gravelpot: Headers can be spoofed by an attacker, but that's not what CSRF is about, it's about having the user's browser do the requests itself from another (malicious) site to your (good) site. In that case, you can't spoof a referrer. (And i'm using the tokens, and everything on my site is behind a login wall that doesn't "remember you") – Daniel Magliola Feb 01 '11 at 15:07
  • 1
    Given what you've described I would say you can safely disable this but I Am Not A Security Expert. The reason to use other techniques as well as referer checking to protect against XSRF are that 1) some users/proxies may withhold referer info and you don't want to prevent them using your app 2) there could be a bug in a browser or plugin that allows referers to be spoofed (this is rare) as mentioned in the Wikipedia article. – mikej Feb 01 '11 at 15:16
  • @gravelpot you are mistaken. It is impossible to spoof referer in a CSRF attack. – rook Feb 01 '11 at 18:33
  • @rook, not trying to be argumentative, but the link you give in your answer above describes two different ways that the referer can be spoofed in a CSRF attack? I am just trying to understand, since what I have always been taught is 1) Don't trust any headers, and 2) the best protection against CSRF is nonces. – gravelpot Feb 01 '11 at 19:13
  • @gravelpot I'll let you in on a little secret. I wrote that section of the owasp article. Now i'm telling you it is 100% impossible to **SPOOF** the referer on a modern system in a CSRF attack. Also XSS bypasses all CSRF protections (even your precious nonce) except a Capthca or asking for the users password. – rook Feb 01 '11 at 21:07