2

I have an application on https://app.example.com. The application requires users to login; if they are not logged in they are redirected to https://app.example.com/login.php when attempting to access any URL on this domain.

When the user has logged in successfully the application uses native PHP sessions to maintain the login state.

We have a separate application on https://db.example.com. This has been built with CakePHP 3. It is configured so that a user will be unable to access any URL on this domain unless they have logged in through https://app.example.com first. This was done by configuring the session.cookie_domain to .example.com and then setting the cookie used for sessions to PHPSESSID:

// config/app.php on db.example.com
'Session' => [
    'defaults' => 'php',
    'cookie' => 'PHPSESSID',
    'timeout' => '0',
    'ini' => [
        'session.cookie_domain' => '.example.com',
        'session.cookie_httponly' => 'off'
    ]
];

There is a script included from app.example.com on db.example.com which runs on every request. This - combined with the ability to read the session cookie - means we are checking the user is logged in before they can access anything on db.example.com; they get redirected to app.example.com/login.php if not.

Up to this point everything works fine.

The problem is that I'm trying to make a CURL request (using Cake's in-built functionality for this: https://book.cakephp.org/3.0/en/core-libraries/httpclient.html) in part of my application on db.example.com to another URL also on db.example.com but for response is a 302 redirect to https://app.example.com/login.php.

The request is made from https://db.example.com/foo to https://db.example.com/bar - which also requires an id parameter in the POST data - like this:

// Request made by /foo:
public function foo()
{
    $this->autoRender = false;

    $http = new Client();

    $response = $http->post('https://db.example.com/bar', [
        'id' => '12345'
    ]);

    debug($response);
}

The output from the above is a HTTP 302 with the location set to the login page on app.example.com:

object(Cake\Http\Client\Response) {
    [protected] code => (int) 302
    [protected] cookies => null
    [protected] reasonPhrase => 'Found'

// ...

'Set-Cookie' => [
    (int) 0 => 'PHPSESSID=g9aeqt2acol6av03rqul2puo20; path=/; domain=.example.com; secure'
    ],
'Location' => [
    (int) 0 => 'https://app.example.com/login.php'
    ], 

}

I have read Keeping session alive with Curl and PHP but can't see what else to configure to solve this?

I also don't understand the cookies => null part of the debug information, when Set-Cookie has the correct session/cookie/domain details.

Environment details:

  • Both app.example.com and db.example.com are on the same server.
  • Both use PHP 7.0.33
  • db.example.com uses CakePHP 3.5.13 as a framework; but app.example.com uses vanilla PHP (no framework).
  • Web server is Apache 2 / CentOS.
Andy
  • 5,142
  • 11
  • 58
  • 131
  • I don’t see you handling the session cookie anywhere in your code that makes the cURL request. Cookies received with any response will be stored _“in the originating instance of Http\Client”_ (quote docs) - but since you are starting with `$http = new Client();` here, I don’t think that should contain _any_ cookies at that point. – misorude Dec 17 '18 at 14:46
  • @misorude That's an interesting point although I don't understand what to do to change/fix it. My understanding is that because I've configured the "shared session" and `PHPSESSID` between the 2 sub-domains it should work. Things work fine in the browser but I accept it will be different here because it's the server (not the browser) making the request? – Andy Dec 17 '18 at 14:56
  • The question you linked to already explains what would need to be done using native cURL - CURLOPT_COOKIEJAR/CURLOPT_COOKIEFILE would need to be set. Check https://stackoverflow.com/q/45406915/10283047 for how this is done using guzzle. (Not sure if that is what CakePHP uses, or if that has its own implementation of an HTTP client?) – misorude Dec 17 '18 at 15:06

1 Answers1

2

The HTTP client will not auto-populate itself with cookies from the application it's running in, or any other application state for that matter, and as mentioned by @misorude in the comments, your code doesn't set any cookies to the client, hence it won't send any.

You'll have to set the required session cookie yourself, for example like this for a single request:

$data = [
    'id' => '12345'
];
$config = [
    'cookies' => [
        //'PHPSESSID' => $this->request->getSession()->id() // before CakePHP 3.6
        'PHPSESSID' => $this->getRequest()->getSession()->id()
    ]
];
$response = $http->post('https://db.example.com/bar', $data, $config);

The supposed cookies property on the response object being empty is a debug display "problem", there is no custom debug output for that class, hence PHP defaults are being used, which will include protected properties like $cookies, which however is not being populated until you're invoking the proper methods for reading cookies, that is getCookies(), getCookie(), getCookieData(), or getCookieCollection().

See also

ndm
  • 59,784
  • 9
  • 71
  • 110
  • `Call to undefined method App\Controller\FooController::getRequest()`. If I populate `$config['cookies']['PHPSESSID']` with `$this->request->cookies['PHPSESSID']` (or hardcode the string in) it's still giving a 302 and the same issue. – Andy Dec 17 '18 at 16:29
  • 1
    @Andy `getRequest()` is only available as of CakePHP 3.6, I've updated the example. I don't really have any further suggestions on your issue, as the shown example works as is, you'll have to do some debugging to check what's wrong, use a network sniffer to check that the request is being issued correctly, and/or check what exactly the request looks like that hits your vanilla PHP application, make sure that the requested session is valid, that there aren't any other requirements (like referer/ip/etc checks)... – ndm Dec 17 '18 at 16:58
  • I'm marking this as the correct answer as what @ndm has written is correct. I've found that the error I'm getting is due to a script on our vanilla PHP application (`app.example.com`) as opposed to the way the session cookie is being passed. The information in this answer is still required however. – Andy Dec 18 '18 at 10:59