23

I'm running Apache 2.2.26:

Server version: Apache/2.2.26 (Unix)
Server built:   Jan 17 2014 12:24:49
Cpanel::Easy::Apache v3.22.30 rev9999 +cloudlinux

I'm attempting to use mod_headers to edit Set-Cookie headers and add the secure or httpOnly flag, but its not working at all (Does nothing, doesn't give HTTP 500 error).

I can use the "modify" "append", directives of the Header command without an issue, just not the edit. I do not know why...

I've tried many combinations, but this is what I have in my .htaccess:

Header edit Set-Cookie "(.)([Hh][Tt][Tt][Pp][Oo][Nn][Ll][Yy])?(.)" "$1$2 ;HTTPOnly"
Header edit Set-Cookie "(.)([Ss][Ee][Cc][Uu][Rr][Ee])?(.)" "$1$2 ;Secure"

I'm open to any solution that will automatically add the flags to every Set-Cookie response, without requiring the editing of code within the application. I do not have access to install additional items on the web server, but the web server has the standard very long list of Apache modules found on most web hosts.

Doug
  • 6,446
  • 9
  • 74
  • 107
  • I *might* have found the answer, but I'm not really sure what they're talking about: http://apache-http-server.18135.x6.nabble.com/Mark-cookies-as-secure-td5003557.html A PHP file is creating the cookie, which might be configured as CGI, I'm not sure. – Doug Jun 09 '14 at 21:21
  • http://serverfault.com/questions/235713/apache-mod-header-rule-to-change-all-cookies-to-secure ? – ax. Jun 12 '14 at 21:16
  • I saw that, but the problem there was it was Apache 2.2.3, and the edit was added in 2.2.4. I'm running Apache 2.2.26... so unless there is a way for the edit to be taken out I would think it would be there. I suppose maybe its an old version of mod_headers rather than an old version of Apache? I'll have to poke into that. – Doug Jun 13 '14 at 15:28

6 Answers6

53

The Header edit directive runs before your application produces a response, so if the application is producing the header you want to edit, that header won't yet exist at the time the directive runs, and there'll be nothing for it to edit.

You can fix this by using Header always edit (which runs after your application produces a response) instead:

Header always edit Set-Cookie (.*) "$1; HTTPOnly"

An example header, before applying the directive:

Set-Cookie: foo=bar; domain=.example.com; path=/

The same header after applying the directive:

Set-Cookie: foo=bar; domain=.example.com; path=/; HTTPOnly

I'm not sure what the directives in your question are meant to do exactly; what they actually result in when changed to Header always edit (assuming the same Set-Cookie header as in my example above) is e.g.

Set-Cookie: f ;HTTPOnlyo=bar; domain=.example.com; path=/

If you understand how regexes and backreferences work, it's obvious what's happening there, but presumably it's not what you want. The directive I've given at the top of this answer ought to work for you if, as you say, you want to add the flag to every Set-Cookie header; if your needs are more complex and I've misunderstood what you're trying to do with that search/replace, let me know.

EDIT: In case it isn't obvious: to add both flags, you can either modify the directive like so:

Header always edit Set-Cookie (.*) "$1; HTTPOnly; Secure"

... or use two directives:

Header always edit Set-Cookie (.*) "$1; HTTPOnly"
Header always edit Set-Cookie (.*) "$1; Secure"

The first approach seems more sensible to me, but it's largely a matter of taste.

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
  • Hi, I'm trying this Header always edit Set-Cookie (.*) "$1; HTTPOnly; Secure" approach on Apache 2.4.6 but it doesn't seem to work – NullEins Aug 29 '18 at 17:22
  • 1
    @NullEins It's important to note that `Secure` will require **HTTPS** for it to work. If you use `Secure` on **HTTP** then the cookie will not work. – Jeremy Sep 27 '18 at 15:50
  • See if I get it right. Inside my htacess I add this line `Header always edit Set-Cookie (.*) "$1; HTTPOnly; Secure"`? I have this lot of error https://prnt.sc/pehmj8 – Tiago Oct 03 '19 at 18:22
  • Thanks a lot for this solution. It works perfectly. For those searching for a solution to apply site-wide but exclude specific cookies, read here: https://stackoverflow.com/questions/41173890/edit-cookie-httponly-value – Ivan Oct 25 '19 at 13:44
  • 1
    how can I edit the header `Set-Cookie` for a specific cookie only, i.e I just want to add `HttpOnly, Secure` for the cookie name `_jsessionid` ? – Olivier Boissé Dec 19 '19 at 16:25
  • I've tried but as you can see didn't flag JessionID cookie for example. What's wrong? See at https://ibb.co/tPtQhJb – André Luís Oliveira Jul 03 '20 at 19:50
  • If your `Header always edit` directive isn't being applied to a particular header, take a look at the answer I just left. You probably (also) want `Header onsuccess edit`. – Coderer May 28 '21 at 14:03
11

Six years later, I may have solved this.

The Header directive is provided by mod_headers, and is structured as Header {condition} {action} {header name} {match} {replacement}. The key that I haven't seen anybody bring up in other answers is the first variable, condition.

condition can be always or onsuccess, per the description in the link above, but -- and this is why it took so long to crack -- always does not actually mean "always":

always is not a superset of onsuccess with respect to existing headers

More specifically, there are two tables of headers called "always" and "onsuccess" and a given module can put a value in either or both of them, and all values from both tables will be written in the response.

TL;DR: always be sure to edit headers in both tables:

Header always edit Set-Cookie (.*) "$1; HTTPOnly"
Header onsuccess edit Set-Cookie (.*) "$1; HTTPOnly"
Coderer
  • 25,844
  • 28
  • 99
  • 154
6

I was trying to set http, secure and samesite=strict on cookies.

This worked for me:

Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure

Samesite=strict provides protection againsts XSRF.

Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=strict

Hope it helps.

Sahith Vibudhi
  • 4,935
  • 2
  • 32
  • 34
  • See if I get it right. Inside my htacess I add this line `Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=strict`? I have this lot of error https://prnt.sc/pehmj8 – Tiago Oct 03 '19 at 18:17
4

This will add the tag to only those cookies that need it:

Header always edit Set-Cookie "^((?!;\s?[Ss]ecure).)+$" "$0; Secure"
Chagrin
  • 41
  • 2
  • Your regex is matching any Set-Cookie header that doesn't start with `; Secure` => obviously, it will also match any Set-Cookie already containing (but not starting with !) `Secure`, thus this will duplicate the `Secure` string in the resulting header. – Pigeo Jul 05 '23 at 18:03
0

make sure that mod_headers.so is enabled then add the following header in apache2.conf for debian based system or httpd.conf for rpm based system

 Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure

For lower than Apache 2.2.4 version use the following:

Header set Set-Cookie HttpOnly;Secure 

then Restart the server

Sanjoy Kanrar
  • 899
  • 8
  • 11
0

Most provided answers to this problem that can be found on the web generally fail on 4 things:

  1. HttpOnly, Secure, SameSite=… should not be appended a second time to the Set-cookie header if it's already there. (how do you think the web browser should interpret something like Set-Cookie: mycookie=foo; SameSite=Lax; SameSite=Strict => is it lax or is it strict ??)
  2. Secure is a good thing if using HTTPS but it will completely break the cookie if using HTTP, therefore we should conditionally add this keyword only on HTTPS connexions.
  3. (Almost?) Nobody really knows if we should just use Header edit or Header always edit or Header onsuccess edit or may both (shame on Apache's bad documentation!)
  4. In an ideal world, it should be the responsibility of the web developers to put the appropriate security keywords whenever they create cookies in their websites. The Header set Set-Cookie trick is here only to strengthen security of your Apache web server in case the developers didn't do their job properly (but that never happens, does it? :-p). Therefore, you should not destroy the work done by a good developper (who took time to put the right SameSite=… value when creating his/her cookie) by overwriting it with a SameSite=strict which is going to break the whole web app. (I give you an example: for Single Sign-On on a CAS server, you have to set the session cookie as SameSite=lax otherwise it simply doesn't work)

Here's a working solution that addresses all those things:

Header onsuccess edit Set-Cookie ^(.*(?i:HttpOnly).*)$ ;;;HttpOnly_ALREADY_SET;;;$1
Header onsuccess edit Set-Cookie ^((?!;;;HttpOnly_ALREADY_SET;;;).*)$ $1;HttpOnly
Header onsuccess edit Set-Cookie ^(?:;;;HttpOnly_ALREADY_SET;;;)(.*)$ $1
Header always edit Set-Cookie ^(.*(?i:HttpOnly).*)$ ;;;HttpOnly_ALREADY_SET;;;$1
Header always edit Set-Cookie ^((?!;;;HttpOnly_ALREADY_SET;;;).*)$ $1;HttpOnly
Header always edit Set-Cookie ^(?:;;;HttpOnly_ALREADY_SET;;;)(.*)$ $1

Header onsuccess edit Set-Cookie ^(.*(?i:SameSite=).*)$ ;;;SameSite_ALREADY_SET;;;$1
Header onsuccess edit Set-Cookie ^((?!;;;SameSite_ALREADY_SET;;;).*)$ $1;SameSite=strict
Header onsuccess edit Set-Cookie ^(?:;;;SameSite_ALREADY_SET;;;)(.*)$ $1
Header always edit Set-Cookie ^(.*(?i:SameSite=).*)$ ;;;SameSite_ALREADY_SET;;;$1
Header always edit Set-Cookie ^((?!;;;SameSite_ALREADY_SET;;;).*)$ $1;SameSite=strict
Header always edit Set-Cookie ^(?:;;;SameSite_ALREADY_SET;;;)(.*)$ $1

<If "%{HTTPS} == 'on'">
  Header onsuccess edit Set-Cookie ^(.*(?i:Secure).*)$ ;;;Secure_ALREADY_SET;;;$1
  Header onsuccess edit Set-Cookie ^((?!;;;Secure_ALREADY_SET;;;).*)$ $1;Secure
  Header onsuccess edit Set-Cookie ^(?:;;;Secure_ALREADY_SET;;;)(.*)$ $1
  Header always edit Set-Cookie ^(.*(?i:Secure).*)$ ;;;Secure_ALREADY_SET;;;$1
  Header always edit Set-Cookie ^((?!;;;Secure_ALREADY_SET;;;).*)$ $1;Secure
  Header always edit Set-Cookie ^(?:;;;Secure_ALREADY_SET;;;)(.*)$ $1
</If>

(these lines have been tested working on Apache 2.4.56. The <If> condition requires Apache 2.4 or later. Appart from that, the rest should work with Apache 2.2.4 or later)

Pigeo
  • 55
  • 5