Jump to content

What would it take to make my public-facing site entirely cookieless?


Recommended Posts

Hi!

I'm puzzling over what it would take to make the public side of my ProcessWire-driven website entirely cookieless.

This site doesn't use public-side editing or $page->editURLs or anything like that, and I haven't added any features that get or set cookie info directly. I can't think of any interaction where the public site needs to know that I'm logged in to ProcessWire. But I do see that ProcessWire sends a set-cookie header for each page URL request, regardless.

I imagine there are several or many possibilities along a gradient of complexity, based on different needs. But I don't know where to start looking into this. What paths are available? And what are the tradeoffs for serving a site entirely without cookies?

Don't get me wrong: I am very happy with the way it works by default, making it super easy to implement front-end functionality for logged-in users! For most of my sites, I use that in some form or another.

Thanks in advance for any guidance you can offer!

 

Link to comment
Share on other sites

Exactly what Robin pointed to. Following is a slightly modified version in the site/config.php that also works in situations like accessing the site via CLI or bootstrap and is aware of admin page name changes.
All session settings above the function can be modified to your needs for accessing your backend only.

Spoiler

/*** 3. SESSION *********************************************************************************/
$config->sessionName = 'wire';
$config->sessionNameSecure = '';
$config->sessionExpireSeconds = 86400;
//$config->sessionAllow = true;
$config->sessionChallenge = true;
$config->sessionFingerprint = 0;
$config->sessionForceIP = '';
$config->sessionCookieSecure = 1;
$config->sessionCookieDomain = null;
$config->sessionHistory = 0;
$config->userAuthHashType = 'sha1';
$config->loginDisabledRoles = array(
    'login-disabled'
);

// DISABLE COOKIES FOR FE ONLY, AS WE NEED COOKIES ON THE BE (!)
$config->sessionAllow = function($session) {

    // if there is a session cookie, chances are user is logged in
    if($session->hasCookie()) {
        return true;
    }

    // if requested URL is an admin URL, allow session
    $adminPagename = wire()->pages->get('id=2')->name;
    if(isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], "/{$adminPagename}/") === 0) {
        return true;
    }

    // otherwise disallow session
    return false;
};

 

 

  • Like 3
Link to comment
Share on other sites

Have you tried that setup in a recent 3.0.17x version of ProcessWire?
Had almost the exact same setup running but it broke with 3.0.177 for some reason.

Didn't dig deeper into it as that project uses other cookies now as well.

Link to comment
Share on other sites

5 hours ago, wbmnfktr said:

Have you tried that setup in a recent 3.0.17x version of ProcessWire?
Had almost the exact same setup running but it broke with 3.0.177 for some reason.

Nope, - sites where I run this are between 3.0.148 and 3.0.165 currently.

Link to comment
Share on other sites

Thank you all!

I did some initial testing on ProcessWire 3.0.165 with confusing results.

When I set $config->sessionAllow using a function, as described here and in the linked article, there was no set-cookie header when I looked at a public-facing URL with curl, but my browser was still getting a cookie—even in Private mode, and even after deleting the cookie and doing a hard refresh of the page. It worked as expected when logging in to ProcessWire.

When I set $config->sessionAllow to false (without checking for cookies or an admin URL), I still got a cookie when visiting a public-facing URL in my browser; again, even in Private mode, and even after deleting the cookie and doing a hard refresh of the page. curl still showed me no set-cookie header. Unsurprisingly, I could not log in to ProcessWire with sessionAllow set to false. 🙂

Maybe I did something wrong. I did not add configuration for any of the other session settings mentioned by @horst
, and that might be the root of my problem. The blog article @Robin S linked to did not mention those under the heading "More Session Control", so I thought that the sessionAllow setting could operate independently of altering other default configurations.

Thanks again! I'll try looking this again later in the week, but I welcome any insight anyone may offer before then!

Link to comment
Share on other sites

1 hour ago, johnstephens said:

When I set $config->sessionAllow to false (without checking for cookies or an admin URL), I still got a cookie when visiting a public-facing URL in my browser;

It's working here for me.

$config->sessionAllow = true;

2021-05-31_173400.thumb.png.b5ebaf0e45083777c55c8e4ce72f811f.png

$config->sessionAllow = false;

2021-05-31_173435.thumb.png.0e24c9e950cfc2d522926a9ef4ed6a9c.png

  • Like 1
Link to comment
Share on other sites

22 hours ago, wbmnfktr said:

Have you tried that setup in a recent 3.0.17x version of ProcessWire?
Had almost the exact same setup running but it broke with 3.0.177 for some reason.

Didn't dig deeper into it as that project uses other cookies now as well.

 

17 hours ago, horst said:

Nope, - sites where I run this are between 3.0.148 and 3.0.165 currently.

 

I updated and tested a site locally and it is working as expected with PW version 3.0.175 and 3.0.179 !

Only thing what happend once after changing / updating the wire-directory was an error screen about spoofed session. I think it had to do with the already existing cookies in my browser (generated with the previous PW version) that leads to that error. When changing the wire folder AND first delete all browser cookies before entering the login page and run a login, everything works as expected.

 

  • Like 1
Link to comment
Share on other sites

Same test: $config->sessionAllow = true;

curl -I https://cyclexo.com/
HTTP/2 200 
date: Tue, 01 Jun 2021 18:02:34 GMT
server: Apache
expires: Thu, 19 Nov 1981 08:52:00 GMT
cache-control: no-store, no-cache, must-revalidate
pragma: no-cache
x-powered-by: ProcessWire CMS
set-cookie: wires=puiah8o9og8bspm6frk2q2litk; path=/; secure; HttpOnly
tt-server: t=1622570554533288 D=64984
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
content-security-policy: upgrade-insecure-requests;
strict-transport-security: max-age=300
content-type: text/html; charset=utf-8

22659931_ScreenShot2021-06-01at2_02_55PM.thumb.png.554eabdb14689d8ec1b9c48df42f23d5.png

And $config->sessionAllow = false;

curl -I https://cyclexo.com/
HTTP/2 200 
date: Tue, 01 Jun 2021 17:59:52 GMT
server: Apache
x-powered-by: ProcessWire CMS
expires: Thu, 19 Nov 1981 08:52:00 GMT
cache-control: no-store, no-cache, must-revalidate
pragma: no-cache
set-cookie: wires=uiid7c27b3gfnh3tkds12283lv; path=/; secure; HttpOnly
tt-server: t=1622570392087006 D=433082
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
content-security-policy: upgrade-insecure-requests;
strict-transport-security: max-age=300
content-type: text/html; charset=utf-8

1058069490_ScreenShot2021-06-01at2_01_43PM.thumb.png.e1dc96bcfd4acee651bb991894a87c4f.pngThis is with $config->sessionAllow at the bottom of my config.php file, without any other session config.

Is that the right place to set this? Is there anything that might be overriding this elsewhere?

Thanks again!

Link to comment
Share on other sites

I think config.php is the correct place. I use that technique on my site at pwgeeks.com if you want to check it works there.

Here's the code I'm using...

$config->sessionAllow = function($session) {
    if($session->hasCookie()) return true;
    if(!isset($_SERVER['REQUEST_URI'])) return false; // CLI invocation of script?
    if(!empty($session->config->urls->admin) && strpos($_SERVER['REQUEST_URI'], $session->config->urls->admin) === 0) return true;
    return false;
};

Have you made any changes to the example function that was linked above?

Link to comment
Share on other sites

27 minutes ago, netcarver said:

Have you made any changes to the example function that was linked above?

Thank you, @netcarver!

Yes. The code in the blog post had the admin path as /processwire/, and I changed it to /my-admin-path/ —but that was the only change I made.

I replaced it with the code you posted, and I'm seeing the same behavior. I feel stumped.

 

Link to comment
Share on other sites

@johnstephens Ok, so if you dump the values of $config in somewhere like ready.php, are you still seeing that $config->sessionAllow is a function?  Just wondering if it's being overwritten somewhere. I'd also grep the codebase (particularly in site/modules) to see if sessionAllow is being set anywhere unexpected.

Link to comment
Share on other sites

What I've found out, that if you have a Form builder form on your site, a cookie will be set even with the config setting mentioned above.

To avoid this you have to disable the cross site scripting protection.

  • Like 3
Link to comment
Share on other sites

1 hour ago, DV-JF said:

What I've found out, that if you have a Form builder form on your site, a cookie will be set even with the config setting mentioned above.

To avoid this you have to disable the cross site scripting protection.

OOOOOOOHH! That makes sense. Yes, I am using a FormBuilder form on the site.

Disabling cross-site scripting protection sounds bad, but I don't understand the relative risks or dangers. What are the trade-offs? If you disable XSS protection for the plugin, are there other reasonable countermeasures one can take?

Link to comment
Share on other sites

4 hours ago, netcarver said:

@johnstephens Ok, so if you dump the values of $config in somewhere like ready.php, are you still seeing that $config->sessionAllow is a function?  Just wondering if it's being overwritten somewhere. I'd also grep the codebase (particularly in site/modules) to see if sessionAllow is being set anywhere unexpected.

Thank you, netcarver! I grepped my whole site directory for "sessionAllow" earlier and couldn't find anything. I just had a chance to dump $config into ready.php and here was some of the session-relevant output:

<!--
    ["sessionAllow"]=>
    object(Closure)#6 (1) {
      ["parameter"]=>
      array(1) {
        ["$session"]=>
        string(10) "<required>"
      }
    }
    ["sessionChallenge"]=>
    bool(true)
    ["sessionFingerprint"]=>
    int(1)
    ["sessionForceIP"]=>
    string(0) ""
    ["sessionCookieSecure"]=>
    int(1)
    ["sessionCookieDomain"]=>
    NULL
    ["sessionHistory"]=>
    int(0)
-->

 

  • Like 1
Link to comment
Share on other sites

@johnstephens Ok, so it's still your function (closure) so it's probably not being overwritten, and that seems right.

DV-JS's point is really good. You need to turn off CSRF ( XSS) protection in your form's settings as anything that uses $session->CSRF will turn on the session cookie again.

Selection_103.thumb.png.3bde880e4cb92920a2eae32633ee9b73.png

See the documentation for the sessionAllow variable in wire/config.php for more information.

  • Like 2
Link to comment
Share on other sites

Thank you, @netcarver and @DV-JF!

So I go into this with my eyes open, what are the implications of turning off CSRF protection? Based on the description above, matching the submission with the session that saw the form may mitigate spam. Are there other perils I should know about that the default CSRF protection may defend against? Or is it an overzealous countermeasure when it comes to a simple contact form?

Link to comment
Share on other sites

As your site is essentially running stateless (without cookies or possibly having state encoded into get values holding IDs etc) and no operations require authentication, I think there's very little risk for you.

If it's just a contact form or something of that nature, I am happy to (and do) turn off the CSRF protection and then add a honey-pot field + Turing test field to deal with spam. Zero spam so far. I can PM you a link to a site set up like this if you want to take a look.

Check out OWASP on CSRF for more information.

  • Like 3
Link to comment
Share on other sites

Thank you for the perfectly comprehensible explanation! Not only does this solve the problem, but I see that it's a setting I can adjust form-by-form, in case I need something more complex in other contexts!

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...