Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of cookies to eliminate cookie constent for unregistered users #3624

Open
jmacgreg opened this issue Apr 20, 2018 · 39 comments
Assignees
Labels
Enhancement:2:Moderate A new feature or improvement that can be implemented in less than 4 weeks. Privacy Any issue that impacts user privacy, such as GDPR and other privacy regulations.

Comments

@jmacgreg
Copy link
Contributor

jmacgreg commented Apr 20, 2018

Describe the problem you would like to solve
OJS creates a session cookie for every user who visits a journal. This requires journal providers under European GDPR laws to get consent to use a cookie from every user.

This is annoying and unnecessary for readers of the journal. And it is difficult to configure for publishers.

Describe the solution you'd like
Remove the requirement to use cookies for users without an account in the journal. That way, the journal only needs to seek consent to use cookies when a user registers a new account.

Who is asking for this feature?
Publishers operating under European GDPR laws.

Additional information
The comments below discuss several aspects of GDPR compliance:

  • Our use of IP addresses in internal statistics is being changed for 3.4 so that IP addresses are anonymized by default.
  • The OJSSID session cookie is only needed to store language settings, but this does not fall under GDPR requirements for a cookie notice.
  • It was recommended below to have a config option to handle the cookie notice for journals that may still require a cookie notice -- either because they want to use third-party code that uses cookies or because their legal departments requirement to do so. This work is not included in this issue and should be filed separately.

This issue overlaps with general improvements to session/cookie management: #6340.

The original issue description is below:

OJS/OMP need a cookie consent option. This should:

  • provide a pop-up note describing the application's cookie policy;
  • stop any cookie creation until this consent statement has been agreed to.

I am not sure if this should be an "option", configurable by JMs, but if it is the option should probably go under Site Access Options.

@jmacgreg jmacgreg added this to To Do in GDPR Compliance Enhancements via automation Apr 20, 2018
@jmacgreg
Copy link
Contributor Author

Notes from @ajnyga, pulled over from other docs:

  1. I think cookies are only used to store a session ID (cookie called OJSSID). But that session ID is then connected to the sessions database table that has user IP and browser details strored. So the cookies themselves are not personal data, but they are linked to such data. I am actually not 100% sure if OJS needs a session if the user does not log in? At the moment it does create the OJSSID cookie immediately when the user enters a OJS page.

While the OJSSID cookie itself does not contain personal data, I think the most important thing is to erase the old sessions from the sessions database table as fast as possible.

What I am thinking here is that would it be possible to get the cookie consent as part of the user registration. Alec of course knows if sessions are needed also for users that are not logged in.

  1. Note that OJS does create a cookie also for visitors. But after writing the comments above, I realized that OJS does use a csrf tokens also in search form that are stored as part of sessions. But not sure if that is actually needed.

But if OJS can be easily used without cookies when user is not logged in, then maybe add the cookie consent to the login form? Just a simple checkbox, "I accept cookies".

@ajnyga
Copy link
Collaborator

ajnyga commented May 2, 2018

An overview on how the above suggestion could work (ping @jmacgreg @asmecher @bozana):

  1. Only start new user sessions (and create OJSSID cookies) when user is logged in (check that the login, register and search forms work without csrf tokens for users who are not logged in?)
  2. In the login form, add a cookie consent checkbox. The user has to check it in order to login.
  3. When user gives consent while logging in, save that consent to the database (user_settings) and add a new cookie "gdpr_consent". This cookie will check the consent checkbox automatically when user opens the login form the next time (not actually needed, but would make things smoother).

The alternative of course is that you ask for the consent from all users visiting an OJS site, also those that are not logged in. But I can not figure out where/how you would save the user consent for users who do not accept cookies because I have understood that you need to have the consents before creating cookies and OJS now creates one immediately when you enter an OJS site.

Happy to help here if you just point to way you want to go.

@asmecher
Copy link
Member

asmecher commented May 2, 2018

@ajnyga, sounds good, though of course you could use the existing OJSSID cookie to store the GDPR consent status rather than introducing an entirely new one. Unless you think it would be useful to give the two cookies different characteristics.

There are a few things that use sessions before a user is logged in (e.g. the language), so this would be a good optional behavior rather than something OJS always did (at least until GDPR-type requirements become more common, which is possible too).

@ajnyga
Copy link
Collaborator

ajnyga commented May 3, 2018

thanks @asmecher
You are right, you could just store the OJSSID and then check if that exists when the user returns. No reason to add new cookies.

I believe that cookies that store things like language selections are not regarded as cookies that need consent under GDPR because they do not contain or lead to personal data. So one option would be to add selections like language to another cookie. See https://www.automated-intelligence.com/news-and-events/blog/cookies-gdpr-need-know/ ("if you use cookies to uniquely identify a device or the person using that device, that’s now treated as personal data under GDPR"). But this is something @jmacgreg could confirm.

Also, I am not sure how it is enforced, but theoretically GDPR also applies to journals that have users coming from the EU, which probably means the majority of journals in Canada (http://www.canadianlawyermag.com/article/getting-ready-for-gdpr-3607/). Of course the main target here are companies, not journals.

@ajnyga
Copy link
Collaborator

ajnyga commented May 3, 2018

Another question @asmecher how does OJS empty the sessions table at the moment? Thinking if we could have an automated task that does that for inactive sessions once a day.

@asmecher
Copy link
Member

asmecher commented May 7, 2018

@ajnyga, that's part of PHP's session manager support; see session_set_save_handler, which lib/pkp/classes/session/SessionManager.inc.php calls to map PHP's session management into the sessions database table. The gc (garbage collection) function is documented at the link above, including some php.ini parameters that can fine-tune its behavior.

@ajnyga
Copy link
Collaborator

ajnyga commented May 11, 2018

@jmacgreg @asmecher (and @NateWr hoping to see your comments as well, you are not out of the European union yet!)

Round-up of the things I am planning to do:

  • New setting to config.inc.php require_cookie_consent on/off (@asmecher requested above that this would be optional and I think this is the best place to control that)
  • In SessionManager around the line https://github.com/pkp/pkp-lib/blob/master/classes/session/SessionManager.inc.php#L33 I will add a if clause that checks if require_cookie_consent is enabled. if it is AND there is no cookie consent cookie available, do not create a new session. If require_cookie_consent is disabled just continue normally.
  • If a cookie consent cookie does not exist, show the cookie consent banner in the footer. It will say something like "The site uses cookies. Some functionalities like changing language, registering or logging in to the system will not work without them. See the Privacy Policy for a description on how cookies are used. [Allow Cookies]"
  • Clicking on the [Allow Cookies] button will use Javascript to create the cookie consent cookie. The cookie only stores the domain name.
  • If cookie consent cookie is not available and require_cookie_consent is enabled, disable the registration form and login form submit buttons and add a message notifying that you need to allow cookies to do this.

Why use a new cookie? Because, if I have understood correctly, the OJSSID has a very short lifespan and I think we want to prevent the situation where users have to click on the cookie consent all the time. The cookie consent cookie does not have any personal data, so we can give it a long lifespan.

Problems:

  • it would make sense that the cookie consent would be site wide,right? But here again we have the problem that 3.1.1.1 will not a have site wide privacy policy setting. So while the cookie consent text has to have a link to the privacy policy text, where does that link lead to?
  • Blocking the creation of cookies will create these errors:
PHP Fatal error:  Call to a member function getUserId() on null in lib/pkp/classes/security/Validation.inc.php on line 369
PHP Fatal error:  Call to a member function getSessionVar() on null in classes/i18n/AppLocale.inc.php on line 81
PHP Fatal error:  Call to a member function getUser() on null in lib/pkp/classes/core/PKPRequest.inc.php on line 581
PHP Fatal error:  Call to a member function getSessionVar() on null in lib/pkp/classes/security/Validation.inc.php on line 384
PHP Fatal error:  Call to a member function getCSRFToken() on null in lib/pkp/classes/template/PKPTemplateManager.inc.php on line 1453

But most of these can be prevented by first checking if $session has a value. With the CSRF token you can just set to CSRF token to null if there is no session. I can add these changes to a pr, but hope that especially with the last one @asmecher evaluates if there are security implications.

What do you think about the overall plan here? This should not be too much work actually.

The other option would be to change OJS so that no cookies are ever created if the user does not log in and the cookie consent is asked when the user logs in. But I could not figure out how to do as a feature you can enable/disable like Alec requested. I think it requires a lot more changes to the code.

@NateWr
Copy link
Contributor

NateWr commented May 14, 2018

I am probably not the best person to comment on a cookie notice, because I think they're often used when not needed. For that reason, I like the use of a config option here. I think it's ok to have any specific request link to that context's privacy policy. If you'd like, you could add a site-wide privacy policy field to use for requests to the site-wide context.

@ajnyga
Copy link
Collaborator

ajnyga commented May 14, 2018

In GDPR case cookie notice is needed if the cookie has personal data. I know that OJSSID does not have, but it is connected to some pretty detailed data in the sessions table. So to be honest, I am not 100% sure the OJSSID cookies actually requires a cookie notice according GDPR.

See for example https://webmasters.stackexchange.com/questions/114973/are-session-cookies-exempt-from-consent-under-gdpr

What do you think, @jmacgreg @bozana ?

@jmacgreg
Copy link
Contributor Author

Agreed with the config option. My reading of the above link, @ajnyga, is that session cookies like OJS' probably don't need a notice, and we can advise people of that, but others will likely want the option.

@ajnyga
Copy link
Collaborator

ajnyga commented May 15, 2018

Thanks for the feedback.

@asmecher can you as well look at my suggestion here: #3624 (comment)

If it sounds ok, I can do a pr based on that. I think it has a big impact on the way the system works so just to be sure I am missing something. The code changes are actually not that big I think.

@asmecher
Copy link
Member

@ajnyga, sorry for the delay.

Broadly speaking, without cookies (and without a replacement session mechanism), the $session object will either not exist or will be re-created with each request. Of those two options, obviously it makes sense not to create one. So yes, we'll need to go through for code that expects $session to always be available and ensure that it can handle a null.

I suspect there will not be many of these. The $session object is primarily used to map to the $user object and that is aleady nullable.

Another option -- just mentioning it for the sake of completeness -- is to use URL-based SIDs until the user has agreed to the cookie policy. I see a lot of potential downsides (e.g. security) and no real need to go there.

@ajnyga
Copy link
Collaborator

ajnyga commented May 18, 2018

thanks! when do you plan to release 3.1.1.1?

@asmecher
Copy link
Member

We were hoping to have it out already, but it looks like it'll still be 1-2 weeks.

@ajnyga
Copy link
Collaborator

ajnyga commented May 18, 2018

Ok! (maybe this should be mentioned in the forum GDPR thread?)

@ajnyga
Copy link
Collaborator

ajnyga commented May 22, 2018

@NateWr @asmecher @jmacgreg

Hoping for one more comment to this issue...

Cookies that do not have personal data do not require consent. The session cookie itself does not have personal data, but the sessions table does have. However, I began to think whether we could just minimize the personal data in the session.

Before the user is logged in, the session table only has these:

  • session_id
  • ip_address
  • user_agent
  • csrf_token
  • some timestamps

I believe we could just encrypt the IP address and the user_agent string before we save it to the sessions table and always compare encrypted values.

For the login form, we could add a consent checkbox regarding the privacy policy, which would then mention what is saved to the cookie and to the sessions table.

What do you think? Is there a reason why we could not encrypt the IP and the user_agent?

@ajnyga
Copy link
Collaborator

ajnyga commented May 22, 2018

The above would as simple as adding

$ip = md5($request->getRemoteAddr());
$userAgent = md5($request->getUserAgent());

here: https://github.com/pkp/pkp-lib/blob/master/classes/session/SessionManager.inc.php#L61-L62
(not suggesting that we use md5, but you get the picture)

@asmecher
Copy link
Member

Hmm, I'm not sure, but I have a feeling that's starting to circumvent the purpose of the GDPR regulations -- we're storing a key that can uniquely track the user without their consent. I'd lean towards the options that don't generate cookies at all until the consent was given; did you hit a blocker there?

@ajnyga
Copy link
Collaborator

ajnyga commented May 31, 2018

In general GDPR does not care about cookies at all. It only cares about personal data. So if a cookie stores just some simple settings etc. you do not need consent.

IP address is regarded as personal data, but encrypted IP is clearly a different thing. At least I can not see how it would be personal data while it can not be used to identify a person.

But with the other approach I did not hit a blocker, just figured that this would be an easier solution and wanted to hear your thoughts. I am on a vacation right now (greeting from Mallorca), but will do a PR next Wednesday. I think only 4-5 files need minor changes.

Another thing with GDPR is that it encourages to limit the collection of personal data. So encrypting the IP address, if it is only used to compare existing sessions, would make OJS more GDPR compliant in any case. I have not found any other purpose for IP in the sessions table.

@ajnyga
Copy link
Collaborator

ajnyga commented May 31, 2018

@asmecher
Copy link
Member

asmecher commented Jun 6, 2018

There's a potentially overlapping request at #1179 to exempt bot user agents from session management.

ajnyga added a commit to ajnyga/ojs that referenced this issue Jun 9, 2018
ajnyga added a commit to ajnyga/pkp-lib that referenced this issue Jun 9, 2018
ajnyga added a commit to ajnyga/ojs that referenced this issue Jun 9, 2018
ajnyga added a commit to ajnyga/ojs that referenced this issue Jun 9, 2018
ajnyga added a commit to ajnyga/pkp-lib that referenced this issue Jun 9, 2018
ajnyga added a commit to ajnyga/pkp-lib that referenced this issue Jun 9, 2018
ajnyga added a commit to ajnyga/pkp-lib that referenced this issue Jul 6, 2018
ajnyga added a commit to ajnyga/pkp-lib that referenced this issue Jul 6, 2018
ajnyga added a commit to ajnyga/ojs that referenced this issue Jul 6, 2018
ajnyga added a commit to ajnyga/pkp-lib that referenced this issue Jul 6, 2018
ajnyga added a commit to ajnyga/ojs that referenced this issue Jul 6, 2018
@asmecher
Copy link
Member

asmecher commented Aug 8, 2018

With apologies for the delay, @ajnyga -- this is still waiting for review, correct?

@ajnyga
Copy link
Collaborator

ajnyga commented Aug 10, 2018

Sorry for the delay, this new github UI does not seem to show new messages the way the old one did...

This is not waiting for review yet, because it is not passing tests. I will maybe try another test run today. Have been busy with other things.

@asmecher
Copy link
Member

Not a problem, just making sure I haven't forgotten something.

ajnyga added a commit to ajnyga/pkp-lib that referenced this issue Aug 25, 2018
ajnyga added a commit to ajnyga/ojs that referenced this issue Aug 25, 2018
ajnyga added a commit to ajnyga/ojs that referenced this issue Aug 25, 2018
@ajnyga
Copy link
Collaborator

ajnyga commented Aug 25, 2018

A new try on this (tests running): pkp/ojs#2106

@NateWr NateWr added the Privacy Any issue that impacts user privacy, such as GDPR and other privacy regulations. label May 9, 2022
@NateWr
Copy link
Contributor

NateWr commented Aug 4, 2022

Is this still a requirement or are people satisfying their requirements through a plugin?

@GrazingScientist
Copy link

@NateWr , which plugins are there to a) make statistics opt-in and b) set cookies?

@marcbria
Copy link
Collaborator

marcbria commented Sep 3, 2022

Reading documentation about GDPR and OJS (by James, Antti-Jusi, Dulip... back in 2018) I arrive to this issue.
Documentation says "a future version of OJS will have an explicit cookie consent option").

Is this still a requirement or are people satisfying their requirements through a plugin?

Nate I don't know about any plugin for OJS 3.x to solve this... apart of an old attempt from Anti-Jussi (for OJS 3.1?).

Most of the people solved this adding a cookie-consent library to our header or with customHeader plugin... but:

  • CustomHeaders solutions depends on third party services (that generate dependences and could comply with GDPR or not).
  • Theming slolution require some skills.

So yes, I feel it's still important to find a normalized solution to say every OJS respects GDPR (at least at cookie level) "out of the box".

We used the brainsum library because:

  • Uses a free soft compatible license.
  • Starts blocking any existing cookie... (we only keep OJS ones as "funcional cookies").
  • You can group the rest of the cookies in groups (social media, tracking...) to let user enable what they like.
  • It's multilanguage.
  • Is responsive.
  • It's easy to use.

@NateWr NateWr added the Enhancement:2:Moderate A new feature or improvement that can be implemented in less than 4 weeks. label Sep 5, 2022
@NateWr NateWr changed the title Add cookie consent option Improve handling of cookies to eliminate cookie constent for unregistered users Sep 5, 2022
@NateWr
Copy link
Contributor

NateWr commented Sep 5, 2022

Ok thanks. I had a read over this issue and #6340 and I can see we're touching on several things. Based on the discussion above, we want to be able to run OJS without requiring cookie consent until a user registers. I have updated the issue title and description to reflect this.

The comments also discussed using a config option to require a cookie consent notice anyway. When work is ready for that, it should be handled in a separate issue. This issue will focus on running OJS without requiring cookie consent.

@ajnyga and @asmecher you've both been active in the discussion above. Can you review the updated comment at the top of this issue to see if it accurately reflects what we want to do?

@asmecher
Copy link
Member

asmecher commented Sep 5, 2022

Yes, @NateWr, that looks accurate. But we do need to consider how we store the user's current locale if we don't depend on cookies; in the installer a cookie is used directly, and once installed the system uses Session::setUserVar (which depends on the session cookie). Otherwise we should be fine.

@marcbria
Copy link
Collaborator

marcbria commented Sep 6, 2022

Sorry for the misunderstanding.
Reading it again, now it's much clearer to me.

But even if OJS is able to function without cookies (which, by the way, as they would be "legitimate cookies" I think is a desirable but not essential functionality), plugins will still use them, so the need for a way to block them is still necessary to comply with GDPR, don't you think?

@NateWr I searched, but I couldn't find any issue talking about the need of a cookie-consent feature. Should I open one, do you prefer talking it in Discussions or should I start a "feature request"?

@NateWr
Copy link
Contributor

NateWr commented Sep 6, 2022

@asmecher is the infrastructure lead so I'll let him decide where he wants to have that discussion.

@asmecher
Copy link
Member

asmecher commented Sep 7, 2022

I can't think of any plugins that rely on cookies (besides in the use of user sessions), and they would be free to use the PHP setcookie function as nicely or destructively as they want; I don't think we can enforce anything about that.

@marcbria
Copy link
Collaborator

marcbria commented Sep 8, 2022

I can't think of any plugins that rely on cookies

Probably I'm missing something but, only from the plugins.xml list, we have:

  • googleAnalytics
  • Twitter Block
  • AddThis
  • Matomo/piwik (when is not configured properly)
  • And CustomHeader that opens the door to almost anything...

But I recognize I thought we had more than this, so probably we can indicate this in the plugin description and relay in user's responsibility?

I feel like we are straying from the main topic of this thread, so if the conversation continues maybe we can talk about it in another thread, discussion, FR or mattermost?

@asmecher
Copy link
Member

asmecher commented Sep 9, 2022

Those plugins don't rely on cookies, but the services they integrate might. But again, I don't think we have any way to prevent that from happening -- the custom header plugin is a good example; people can put whatever JS they want into it. Maybe I misunderstood what you're proposing?

@eddoff
Copy link
Contributor

eddoff commented Nov 15, 2023

If I have understood the EU regulations (GDPR and ePrivacy) correctly, one cannot save cookies without a consent from the user.

The cookies in OJS/OMP are not strictly necessary for readers. Hence, OJS/OMP cookies require consent accroding to:
"This means cookies cannot be set when the webpage is first opened. You can only set the cookie and use the information collected through it once you have obtained the user's consent." (quote from link above)

Therefore, I think you need to regulate this so that the user needs to give a consent before OJS/OMP can save any cookie (answer yes or no). If a user has dismissed cookies, they need to be asked again for consent if they wish to login.

@eddoff

@marcbria
Copy link
Collaborator

PKP and CRAFT-OA projects are working together to make OJS full GDRP compliant "out of the box".

The approach is just the way you summarize: no cookies will be created till visitor's explicit agreement.

Not sure when all this work will see the light but won't be so far.

@asmecher, now I noticed I missed to answer your comments:

Those plugins don't rely on cookies, but the services they integrate might. -- the custom header plugin is a good example; people can put whatever JS they want into it.

Exactly. The services called by the plugin asks for a JS that creates a cookie.

But again, I don't think we have any way to prevent that from happening
Maybe I misunderstood what you're proposing?

In parallel to the work Dulip is doing to remove the OJS session cookie, what I was proposing was to develop a plugin that removes any cookies that have been created by third party services.

That is easy to implement with libraries like cookieconsent.

Actually, that's what we were implementing in our journals to deal with cookies from Google Analytics (which we don't need anymore because now we use plausible), Twitter, etc.

Here is a test journal: https://revistes.uab.cat/periodicum/

One possible answer is "our obligation is to OJS, not to third party services" but third party services are called from OJS plugins... so it doesn't seem out of place to me to apply an extra protection mechanism to cover these cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement:2:Moderate A new feature or improvement that can be implemented in less than 4 weeks. Privacy Any issue that impacts user privacy, such as GDPR and other privacy regulations.
Projects
Status: Backlog
Development

No branches or pull requests

8 participants