SpiderLabs / owasp-modsecurity-crs

OWASP ModSecurity Core Rule Set (CRS) Project (Official Repository)
https://modsecurity.org/crs
Apache License 2.0
2.45k stars 724 forks source link

Cookie defects for secure and httponly flags #128

Closed rcbarnett-zz closed 10 years ago

rcbarnett-zz commented 10 years ago

CORERULES-74: The Rules for settings httponly and secure flags for cookies caused me all sorts of grief. After some investigation I believe I have tracked the issue down to the way the Apache set header directive is used in combination with the fact many of our web apps use multiple cookies.

I have created some updated versions of the rules for consideration that seems to work for me with "fixing" multiple cookies, and fixing either, or, or both of the missing httponly and secure flags.

I'm not sure how to do this issue submission if I'm honest, so here are the text of the "replacement" rules I've created which should explain everything:

SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-_]?(id)?|cf(id|token)|sid))" "phase:3,id:'70007',t:none,pass,nolog,setvar:tx.sessionids=%{matched_vars}"

SecRule TX:SESSIONIDS "!(?i:\;? ?httponly;?)" "phase:3,id:'70008',t:none,setenv:new_httponly_cookie=%{matched_var},pass,log,auditlog,msg:'AppDefect: Missing HttpOnly Cookie Flag.'"

SecRule SERVER_PORT "@streq 443" "chain,phase:3,id:'70009',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag.'" SecRule TX:SESSIONIDS "!(?i:\;? ?secure;?)" "t:none,setenv:new_httponly_cookie=%{matched_var}"

Set an httponly and / or secure flag, but don't duplicate if it's already there

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

The main difference is I use sessionids as a collection to try and catch any instance of a cookie that's not secure or set httponly (otherwise if the last cookie is secure but earlier cookies are not the rule didn't kick in) and that I use Header edit rather than set, so update cookies to be secure rather then explicitly set them (setting them was causing me to lose all but the "last" cookie when multiple cookies were being set).

I'm not sure if my investigation and conclusion is correct, but as my apps work with the above rules configured and the "existing rules" () disabled I thought I should spread this to the wider community should the updates actually be sensible.

If my updates don't make sense then I give my apologies.

rcbarnett-zz commented 10 years ago

Original reporter: mrdaleroberts

rcbarnett-zz commented 10 years ago

rcbarnett: I believe you are correct in that the CRS' use of "Header set" vs. "Header edit" was causing issues. This updated ruleset should work. Please test and I will then add it to the CRS trunk in SVN -

#

Identifies SessiondIDs without HTTPOnly flag

#

SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(j?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))" "phase:3,id:'981183',t:none,pass,nolog,setvar:tx.sessionid=%{matched_var}"

SecRule TX:SESSIONID "!(?i:\;? ?httponly;?)" "phase:3,id:'981184',t:none,setenv:httponly_cookie=%{matched_var},pass,log,auditlog,msg:'AppDefect: Missing HttpOnly Cookie Flag.'"

SecRule SERVER_PORT "@streq 443" "chain,phase:3,id:'981185',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag.'" SecRule TX:SESSIONID "!(?i:\;? ?secure;?)" "t:none,setenv:secure_cookie=%{matched_var}"

SecRule TX:SESSIONID "!(?i:\;? ?httponly;?)" "chain,phase:3,id:'981186',t:none,setenv:httponly_cookie=%{matched_var},pass,log,auditlog,msg:'AppDefect: Missing HttpOnly and Secure Cookie Flag.'" SecRule SERVER_PORT "@streq 443" "chain,t:none" SecRule TX:SESSIONID "!(?i:\;? ?secure;?)" "t:none,setenv:secure_httponly_cookie=%{matched_var}"

Add missing httponly flag

Header edit Set-Cookie "%{httponly_cookie}e" "%{httponly_cookie}e; httponly" env=httponly_cookie

Add missing secure flag

Header edit Set-Cookie "%{secure_cookie}e" "%{secure_cookie}e; secure" env=secure_cookie

Add both missing httponly and secure flags

Header edit Set-Cookie "%{secure_httponly_cookie}e" "%{secure_httponly_cookie}e; secure; httponly" env=secure_httponly_cookie

rcbarnett-zz commented 10 years ago

mrdaleroberts: Apologies - you'll notice that the sme environment variable is used twice - obviously one should be "new_secure_cookie"

rcbarnett-zz commented 10 years ago

mrdaleroberts: After some further investigation and trying to accommodate SSL offloading where Apache doesn't listen on port 443 or do the SSL I have made some more updates

I am still getting problems though. I can't figure how to "add" an HTTPOnly or Secure header to all cookies regardless of whether any already come with such cookies set "cleanly". MY latest rules end up with this:

Set-Cookie: atlassian.xsrf.token=BOUD-NVXL-SUW0-PC4A|de6c576bfc7be3fb67e2b2aa19ce59fbe72b09bf|lout; Path=/gojira ;HTTPOnly ;Secure Set-Cookie: JSESSIONID=47168E11E2CA130E7440EA6BDF3EC28B; Path=/gojira; Secure; HttpOnly ;HTTPOnly ;Secure X-WAF-Events: TX:0, TX:1

Which has duplication of the httponly and the secure where a cookie already has it set. It works, but I'm not sure some client browsers might not get upset by this (rules below)

SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-_]?(id)?|cf(id|token)|sid))" "phase:3,id:'70007',t:none,pass,nolog,setvar:tx.sessionids=%{matched_vars}"

SecRule TX:SESSIONIDS "!(?i)^.*httponly;?(\R|,)" "phase:3,id:'70008',t:none,setenv:non_httponly_cookie=%{matched_var},pass,log,auditlog,msg:'AppDefect: Missing HttpOnly Cookie Flag.'"

SecRule SERVER_PORT "@streq 443" "chain,phase:3,id:'70009',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag 443.'" SecRule TX:SESSIONIDS "!(?i)^.*secure;?(\R|,)" "t:none,setenv:non_secure_cookie=%{matched_var}"

SecRule WEBAPPID "SECURE" "chain,phase:3,id:'70010',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag webapp.'" SecRule TX:SESSIONIDS "!(?i)^.*secure;?(\R|,)" "t:none,setenv:non_secure_cookie=%{matched_var}"

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

rcbarnett-zz commented 10 years ago

mrdaleroberts: Ryan,

Apologies for all the updates post your comment - I wasn't paying attention, I'd got wrapped up in trying to make this tidy.

I've tried your update without success, and with success ... this is driving me slowly mental so I'm going to come back to it with fresh eyes tomorrow.

I think there is still a problem with having multiple cookies being set on a single request especialyl where some have httponly set and some do not, but as I've been messing with this for too long today I'm going to leave it till tomorrow to have a proper look again.

Thanks for the response.

rcbarnett-zz commented 10 years ago

mrdaleroberts: OK, I've started with fresh eyes. So far it's not looking good. I've updated the CRS rules with your new version and also added the following for extra debugging (as things didn't appear to work):

Header add X-DALE-Set-Cookie "httponly" env=httponly_cookie Header add X-DALE-Set-Cookie "secure_cookie" env=secure_cookie Header add X-DALE-Set-Cookie "secure_httponly_cookie" env=secure_httponly_cookie

I get the following results:

bash-4.1 ~ $ curl -k -i https://gojira.pinpoint-dev.scee.net/gojira/secure/Dashboard.jspa 2>&1 | grep Set-Cookie X-DALE-Set-Cookie: httponly Set-Cookie: atlassian.xsrf.token=BOUD-NVXL-SUW0-PC4A|ffa2c015f659c4de96e3a44d66fa6fc805b99a41|lout; Path=/gojira Set-Cookie: JSESSIONID=6C507E3252BE25675E8AB81B5F2B83EB; Path=/gojira; Secure bash-4.1 ~ $

Which matches with what's seen in the logs - which suggests that the rule is triggering but then the header edit is failing to work as expected.

Audit log shows : Message: Warning. Match of "rx (?i:\;? ?httponly;?)" against "TX:sessionid" required. [file "/u01/apache2/conf/mod_security/enabled/modsecurity_crs_55_application_defects.conf"] [line "71"] [id "981184"] [msg "AppDefect: Missing HttpOnly Cookie Flag."]

Debug Log (L9) shows:

board.jspa][4] Executing operator "rx" with param "(?i:(j?sessionid|(php)?sessid|(asp|jserv|jw)?session[- _]?(id)?|cf(id|token)|sid))" against RESPONSE_HEADERS:Set-Cookie. [05/Oct/2011:10:21:01 +0100] [gojira.pinpoint-dev.scee.net/sid#d17c0e8][rid#10b08b58][/gojira/secure/Dash board.jspa][9] Target value: "JSESSIONID=9D63E42F41DE1BD9A2C6290CD4640704; Path=/gojira; Secure" [05/Oct/2011:10:21:01 +0100] [gojira.pinpoint-dev.scee.net/sid#d17c0e8][rid#10b08b58][/gojira/secure/Dash board.jspa][6] Ignoring regex captures since "capture" action is not enabled. [05/Oct/2011:10:21:01 +0100] [gojira.pinpoint-dev.scee.net/sid#d17c0e8][rid#10b08b58][/gojira/secure/Dashboard.jspa][4] Operator completed in 22 usec. [05/Oct/2011:10:21:01 +0100] [gojira.pinpoint-dev.scee.net/sid#d17c0e8][rid#10b08b58][/gojira/secure/Dashboard.jspa][9] Setting variable: tx.sessionid=%{matched_var} [05/Oct/2011:10:21:01 +0100] [gojira.pinpoint-dev.scee.net/sid#d17c0e8][rid#10b08b58][/gojira/secure/Dashboard.jspa][9] Resolved macro %{matched_var} to: JSESSIONID=9D63E42F41DE1BD9A2C6290CD4640704; Path=/gojira; Secure [05/Oct/2011:10:21:01 +0100] [gojira.pinpoint-dev.scee.net/sid#d17c0e8][rid#10b08b58][/gojira/secure/Dashboard.jspa][9] Set variable "tx.sessionid" to "JSESSIONID=9D63E42F41DE1BD9A2C6290CD4640704; Path=/gojira; Secure".

So the value is set, and you'd expect it to work as doing the same search and replace in "vi" results in a correct header.

rcbarnett-zz commented 10 years ago

mrdaleroberts: Looks like something bad with the Header edit

I made this update to try and force the replace:

Header edit Set-Cookie ".JSESSIONID." "%{httponly_cookie}e; httponly" env=httponly_cookie

And get this:

bash-4.1 ~ $ curl -k -i https://gojira.pinpoint-dev.scee.net/gojira/secure/Dashboard.jspa 2>&1 | grep Set-Cookie X-DALE-Set-Cookie: JSESSIONID=DD2BFA4B6C32967C64D1B1887069F36C; Path=/gojira; Secure; httponly Set-Cookie: atlassian.xsrf.token=BOUD-NVXL-SUW0-PC4A|27db6a84e33a4976a0391e3094a2bc040865aa13|lout; Path=/gojira Set-Cookie: %{httponly_cookie}e; httponly bash-4.1 ~ $

So it looks like, for some reason, the second bit doesn't take variable (i.e. the replace operator doesn't take variable)

rcbarnett-zz commented 10 years ago

mrdaleroberts: I suspect this is an apache "thing". I won't say bug, as it's quite possible, being as that's meant to be a regex that differentiating environment substitution from weird regex would be extremely hard in code and / or confusing for the user.

rcbarnett-zz commented 10 years ago

mrdaleroberts: Ryan,

Firstly, could you explain what exactly TX:SESSIONID "!(?i:\;? ?httponly;?)" does compared to what TX:SESSIONID "!(?i)httponly;?" does please ?

The latter regex is looking for httponly following by zero or 1 ";" characters and is modified to be case insensitive by the (?i) prefix AFAIK. I have no idea what, specifically, the former regex does other than the same thing with some sort of modifier, possibly, as best I can tell, including not capturing matches ?

I know I could read the regex man page, but I've tried that and it really, really doesn't explain anything that helps without weeks of reading and testing to understand it :( I though I was very competent (and considered such by my co-workers) in regular expressions but I've never had to understand the modifiers in this format before, or at least I suspect that's why I don't understand what this regex is doing specifically.

Any help would be greatly appreciated as I'm coming to the conclusion that mod_security is not the way to fix missing httponly and secure flags as but this might simply be my lack of understanding of the regex.

Secondly, mod_security, as far as I can see (please do prove me wrong - it'll really help), doesn't nicely handle the only if string X "is not in" the any one of a collection in a way I can see how to use (which we need to apply cookie flags if any cookie doesn't have flags set already).

In conclusion. The problem is as far as I can explain it that there is more than one Set-Cookie line coming out of JIRA (one session cookie from tomcat, and one from the JIRA application) and JIRA sets it's cookie wrong, whereas tomcat sets it's right. So either you add flags "again" to the already correct tomcat cookie because you apply to everything (which you can do without mod_security), or you don't add these to the JIRA cookie because mod_Security sees "a" cookie (the tomcat one) with them set correctly already. This is all then further complicated, it would appear, by the fact that depending what order the Set-Cookie headers come through from the back end app (tomcat's first, or JIRA's first) the behaviour changes as the last one takes precedence as far as mod_security is concerned.

None of this is because of anything wrong with Apache or mod_security as far as I can see, just a fairly edge case limitation on what mod_security / Apache can do (no env variable in Header edit for apache and no "foreach" type functionality in mod_security for multiple cookies)

Anyway, again, all help greatly appreciated, I'm hoping you'll point out how wrong I am and we'll end up with something that works nicely and only acts on the cookies that need fixing but not the others.

rcbarnett-zz commented 10 years ago

rcbarnett: Question - are you interested in using ModSecurity to alert when httponly/secure flags are missing (to identify Application Defects) or to actually just fix the issue. It seems like from your rules you are more interested in the latter as you are using nolog for the ModSecurity rules.

I think the accuracy issue here has to do with having multiple Set-Cookie response headers going out and these rules are not granular enough to know which ones need to have these flags added and which ones do not or should not. I originally tested this when only 1 session ID was being sent.

It is probably easier to just use the Apache Header edit directives themselves to fix the issues. Try this -

Add missing httpOnly flag

Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?:(?!httponly).)+)$" "$1; httpOnly"

Add missing secure flag

Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?:(?!httponly).)+)$" "$1; secure"

rcbarnett-zz commented 10 years ago

mrdaleroberts: In answer to the question, I'd like to have my cake and eat it I'm afraid. I want to fix things but I also want to have it recorded that they were broken so we can try to get people to fix them themselves at source. Thought to achieve this I think I can use your new update and more loosely couple the "fix" via mod_headers and the "notification" via mod_security. The one gotcha here is that I can't fix the secure flag where it shouldn't be (i.e. you don't want to add a secure flag for http cookies obviously) so I need to use mod_security to tell me if a site is secure.

I presume it would make sense to keep all this in mod_security rather than separate as it is a security fix to fix the httponly and secure flags and mod_security is the natural place for that ?

Also, I don't know if there's any benefit having a rule to identify and log httponly and secure flags as missing rather than just two rules, one for each so I've removed the third. I've also added a rule to detect missing secure flags on sites (like ours) where SSL if offloaded and so we have secure sites, not running SSL in apache or listening on port 443. This works by looking for SECURE in the webappid name (there may be better way so please point me in a new direction if there is one)

Regarding FIXING: I noticed you hadn't got the httponly or secure flags as case insensitive searches - I've update that - and now it appears to work perfectly for the "fix" part. I'll do some more testing though and update this issue if I find any problems. Current ruleset:

######### SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-_]?(id)?|cf(id|token)|sid))" "phase:3,id:'981183',t:none,pass,nolog,setvar:tx.sessionid=%{matched_var}"

SecRule TX:SESSIONID "!(?i:\;? ?httponly;?)" "phase:3,id:'981184',t:none,setenv:httponly_cookie=%{matched_var},pass,log,auditlog,msg:'AppDefect: Missing HttpOnly Cookie Flag.'"

SecRule SERVER_PORT "@streq 443" "chain,phase:3,id:'981185',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag.',setenv:secure_site" SecRule TX:SESSIONID "!(?i:\;? ?secure;?)" "t:none,setenv:secure_cookie=%{matched_var}"

SecRule WEBAPPID "@rx SECURE" "chain,phase:3,id:'981186',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag.',setenv:secure_site" SecRule TX:SESSIONID "!(?i:\;? ?secure;?)" "t:none,setenv:secure_cookie=%{matched_var}"

Add missing httpOnly flag

Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!httponly).)+)$" "$1; httpOnly"

Add missing secure flag

Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!secure).)+)$" "$1; secure" env=secure_site #########

Regarding LOGGING, I still can't get mod_security to log whenever "any" cookie has missing flags, even if there are others that do, I'm still not 100% on how to do this with mod_security as I still get nothing logged if one cookie has suitable flags and that's the last cookie set.

In case anyone else has my lack of understanding of the regex modifiers, here's a link I found that has some clearer explanations than the regex man pages, though I'm sure there are some that are even better: http://gnosis.cx/publish/programming/regular_expressions.html

Anyway, MANY thanks for your help on this so far.

rcbarnett-zz commented 10 years ago

rcbarnett: 1. For the "fix" part - your updates to the Header edit directives looks good and they should work.

  1. For the "alert" part - I think that the main problem with these current rules is that we are setting a variable with a static name "setvar:tx.sessionid" however there may be multiple cookie names that match in that regex. This will cause that variable to be overwritten with the last match. What we should do is update the setvar to include the matched_var data within the name as well like this -

SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))" "phase:3,id:'981183',t:none,pass,nolog,setvar:'tx.sessionid%{matched_var}=%{matched_var}'"

You can then update the next rule TARGET to use the regex specifier which would then check all matches from the previous rule -

SecRule TX:/SESSIONID_/ "!(?i:\;? ?httponly;?)" "phase:3,id:'981184',t:none,setenv:httponly_cookie=%{matched_var},pass,log,auditlog,msg:'AppDefect: Missing HttpOnly Cookie Flag.'"

Your remaining rules would then become this -

SecRule SERVER_PORT "@streq 443" "chain,phase:3,id:'981185',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag.',setenv:securesite" SecRule TX:/SESSIONID/ "!(?i:\;? ?secure;?)" "t:none,setenv:secure_cookie=%{matched_var}"

SecRule WEBAPPID "@rx SECURE" "chain,phase:3,id:'981186',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag.',setenv:securesite" SecRule TX:/SESSIONID/ "!(?i:\;? ?secure;?)" "t:none,setenv:secure_cookie=%{matched_var}"

Let me know if these help.

rcbarnett-zz commented 10 years ago

mrdaleroberts: It never even occurred to me that you could use the regex match on the session name with a prefix to allow tracking multiple cookie.

I've implemented this and tested it out and so far as I can tell we now get all cookies fixed and all "bad" cookies logged.

For clarity, the ruleset is now:

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

#

Identifies SessiondIDs without HTTPOnly flag

#

SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))" "phase:3,id:'981183',t:none,pass,nolog,setvar:tx.sessionid%{matched_var}=%{matched_var}"

SecRule TX:/SESSIONID/ "(?i)(.?)=(?!._; *httponly;?)" "phase:3,id:'981184',t:none,setenv:httponly_cookie,pass,log,auditlog,capture,msg:'AppDefect: Missing HttpOnly Cookie Flag for %{tx.1}.'"

SecRule TX:/SESSIONID/ "(?i)(.?)=(?!._; *secure;?)" "chain,phase:3,id:'981185',t:none,pass,log,auditlog,capture,msg:'AppDefect: Missing Secure Cookie Flag for %{tx.1}.'" SecRule SERVER_PORT "@streq 443" "t:none,setenv:secure_site"

SecRule TX:/SESSIONID/ "(?i)(.?)=(?!._; *secure;?)" "chain,phase:3,id:'981186',t:none,pass,log,auditlog,capture,msg:'AppDefect: Missing Secure Cookie Flag for %{tx.1}.'" SecRule SERVER_PORT "!@streq 443" "chain,t:none,setenv:secure_site" SecRule WEBAPPID "@rx SECURE" "t:none,setenv:secure_site"

SecMarker SECURE_WEBAPP

Add missing httpOnly flag

Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!httponly).)+)$" "$1; httpOnly"

Add missing secure flag

Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!secure).)+)$" "$1; secure" env=secure_site

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

You'll notice that I've tweaked a bit more to make sure that you can have non port 443 secure apps, but without double logging. Also you get the name of the cookie in question logged, which is useful if you have multiple cookies as it help track down which cookies, from where is bad. Hope this "final draft" all looks OK to you.

Once gain, thanks very much for all your help on this.

SEPARATE QUERY: On a slightly different note, if I am having to update mod_security rules (well, exclude them and implement modified version) would you like any updates that are required by "publicly avaiable applications" (such as JIRA) to be logged here so you can review them or is that something that should be done somewhere else ? Obviously you won't want rule modifications for custom internal applications, but things like JIRA or confluence or tomcat might be of use I'm guessing ?

rcbarnett-zz commented 10 years ago

rcbarnett: Made a few more updates -

1) Added thresholding of alerts so that it will only generate 10/day. Since these are app defects, you only need periodic alerting. 2) Change the phase to 4 to ensure that we have all of the response headers (phase:3 can be inconsistent when run in embedded vs. proxy mode).

#

Identifies SessiondIDs without HTTPOnly flag

# SecRule &GLOBAL:MISSING_HTTPONLY "@eq 0" "phase:4,t:none,nolog,pass,id:'981235',setvar:global.missing_httponly=0" SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))" "phase:4,id:'981183',t:none,pass,nolog,setvar:tx.sessionid%{matched_var}=%{matched_var}"

SecRule TX:/SESSIONID/ "(.?)=(?i)(?!.httponly.)(._$)" "chain,phase:4,id:'981184',t:none,setenv:httponly_cookie,pass,log,auditlog,capture,msg:'AppDefect: Missing HttpOnly Cookie Flag for %{tx.1}.'"
SecRule GLOBAL:MISSING_HTTPONLY "@le 10" "setvar:global.missing_httponly=+1,expirevar:global.missing_httponly=86400"

SecRule &GLOBAL:MISSING_SECURE "@eq 0" "phase:4,t:none,nolog,pass,id:'981236',setvar:global.missingsecure=0" SecRule TX:/SESSIONID/ "(._?)=(?i)(?!.secure.)(._$)" "chain,phase:4,id:'981185',t:none,pass,log,auditlog,capture,msg:'AppDefect: Missing Secure Cookie Flag for %{tx.1}.'" SecRule SERVER_PORT "@streq 443" "chain,t:none,setenv:secure_site"
SecRule GLOBAL:MISSING_SECURE "@le 10" "setvar:global.missing_secure=+1,expirevar:global.missing_secure=86400"

#

[ Fix Missing "httponly" Flag ]

# Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!httponly).)+)$" "$1; HttpOnly"

#

[ Fix Missing "secure" Flag ]

# Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!secure).)+)$" "$1; secure" env=secure_site

rcbarnett-zz commented 10 years ago

mrdaleroberts: Hi Ryan,

One question, you've altered the regex for identifying secure cookie and http_only cookie (or rather those that aren't). Your regex will wrongly flag some cookies (i.e. Set-Cookie: my_cookie=securepw1234) as being OK and not needing correcting will it not ?

I know this is an unlikely cookie value, but not impossible, especially for the secure cookie check as having a value that contains the word secure isn't completely unheard of. Was there something wrong with the regex I'd used (it wouldn't suprise me if there was - as mentioned earlier in the post I'm not a true expert).

Thanks

rcbarnett-zz commented 10 years ago

mrdaleroberts: Hmm, looks like running in phase 4 breaks it completely. Took me a while to figure out why nothing was flagging and secure cookies weren't being fixed. Then I remembered you'd changed the phase, switched back to phase 3 and all worked again.

rcbarnett-zz commented 10 years ago

rcbarnett: Hmm, yeah, I guess that the mod_headers Header directive runs earlier in the Apache request phase so we have to run in phase:3 to set the ENV for the secure cookie flag.

As for the regexes, I tested it both externally with Reggy for Mac OSX and internally in ModSecurity and it seems to work fine.

rcbarnett-zz commented 10 years ago

mrdaleroberts: I've done a little more testing and I can definitely confirm that setting the rules to phase 4 fails, which I guess makes some sense as that's body processing not header processing ?

I've confirmed that the logging limit works and stops logging after the few 11 events in a short period, I'll just presume the timer works to allow logging again a day later.

I still think the check for secure flags should be tighter, though please do tell me if I'm wrong. Currently I have this:

Identifies SessiondIDs without HTTPOnly flag

SecRule &GLOBAL:MISSING_HTTPONLY "@eq 0" "phase:3,t:none,nolog,pass,id:'981235',setvar:global.missing_httponly=0" SecRule &GLOBAL:MISSING_SECURE "@eq 0" "phase:3,t:none,nolog,pass,id:'981236',setvar:global.missing_secure=0"

SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))" "phase:3,id:'981183',t:none,pass,nolog,setvar:tx.sessionid%{matched_var}=%{matched_var}"

SecRule TX:/SESSIONID/ "(.?)=(?i)(?!._; ?httponly ?)" "chain,phase:3,id:'981184',t:none,pass,log,auditlog,capture,setenv:httponly_cookie,msg:'AppDefect: Missing HttpOnly Cookie Flag for %{tx.1}.'" SecRule GLOBAL:MISSING_HTTPONLY "@le 10" "setvar:global.missing_httponly=+1,expirevar:global.missing_httponly=86400"

SecRule TX:/SESSIONID/ "(.?)=(?i)(?!._; ?secure ?)" "chain,phase:3,id:'981185',t:none,pass,log,auditlog,capture,msg:'AppDefect: Missing Secure Cookie Flag for %{tx.1}.'" SecRule SERVER_PORT "@streq 443" "chain,t:none,setenv:secure_site" SecRule GLOBAL:MISSING_SECURE "@le 10" "setvar:global.missing_secure=+1,expirevar:global.missing_secure=86400"

[ Fix Missing "httponly" Flag ]

Header edit Set-Cookie "^(?i:([^=]+)=([^;]+)(?!.; ?httponly ?(;|$).))" "$1=$2; HttpOnly" env=httponly_cookie

[ Fix Missing "secure" Flag ]

Header edit Set-Cookie "^(?i:([^=]+)=([^;]+)(?!.; ?secure ?(;|$).))" "$1=$2; Secure" env=secure_site

rcbarnett-zz commented 10 years ago

rcbarnett: I am currently testing de-coupling the ModSecurity alerting from the mod_headers Header edit directives fixing the cookies. The advantage of this approach is that ModSecurity doesn't have to track/create TX vars.

#

[ Cookie's HttpOnly Flag Was Not Set ]

#

- http://websecuritytool.codeplex.com/wikipage?title=Checks#cookie-not-setting-httponly-flag

- https://www.owasp.org/index.php/HttpOnly

SecRule &GLOBAL:MISSING_HTTPONLY "@eq 0" "phase:5,t:none,nolog,pass,id:'981235',setvar:global.missing_httponly=0" SecRule GLOBAL:MISSING_HTTPONLY "@le 10" "chain,phase:5,id:'981184',t:none,pass,log,auditlog,msg:'AppDefect: Missing HttpOnly Cookie Flag for %{tx.1}.',tag:'WASCTC/WASC-15',tag:'MISCONFIGURATION',tag:'http://websecuritytool.codeplex.com/wikipage?title=Checks#cookie-not-setting-httponly-flag'" SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(.?)=(?i)(?!.httponly.)(._$)" "capture,setvar:global.missing_httponly=+1,expirevar:global.missing_httponly=86400"

#

[ Fix Missing "httponly" Flag ]

# Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!httponly).)+)$" "$1; HttpOnly"

#

[ Cookie's Secure Flag Was Not Set ]

#

- http://websecuritytool.codeplex.com/wikipage?title=Checks#cookie-not-setting-secure-flag

- https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Use_.22Secure.22_Cookie_Flag

SecRule &GLOBAL:MISSING_SECURE "@eq 0" "phase:3,t:none,nolog,pass,id:'981236',setvar:global.missing_secure=0" SecRule GLOBAL:MISSING_SECURE "@le 10" "chain,phase:3,id:'981185',t:none,pass,log,auditlog,msg:'AppDefect: Missing Secure Cookie Flag for %{tx.1}.',tag:'WASCTC/WASC-15',tag:'MISCONFIGURATION',tag:'http://websecuritytool.codeplex.com/wikipage?title=Checks#cookie-not-setting-secure-flag'" SecRule SERVER_PORT "@streq 443" "chain,t:none,setenv:secure_site" SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(.?)=(?i)(?!.secure.)(._$)" "capture,setvar:global.missing_secure=+1,expirevar:global.missing_secure=86400"

#

[ Fix Missing "secure" Flag ]

# Header edit Set-Cookie "^((?i:(?(COOKIE|TOKEN)|atlassian.xsrf.token|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))=(?i:(?!secure).)+)$" "$1; secure" env=secure_site

rcbarnett-zz commented 10 years ago

mrdaleroberts: That makes sense. I guess if there's no performance advantage to only doing the mod_headers stuff if we know we have to, then there's no real reason to link them together.

For the regexs I get this:

bash $ echo "Set-Cookie: mycookie=securepw1234"|grep -P '(.?)=(?i)(?!._; ?secure ?(;|$))' Set-Cookie: my_cookie=securepw1234 bash $ echo "Set-Cookie: mycookie=securepw1234"|grep -P '(.?)=(?i)(?!.secure.)(._$)' bash $

Which would suggest that we do need to be a little more specific in how we identify cookie flags and is about as specific as I think it gets (based on my reading of http://tools.ietf.org/html/rfc6265 and my decades old understanding of BNF never mind ABNF), but hey, mileage varies and all that and frankly trying to figure out these regexs is starting to send me slightly loopy, especially at the Apache search and replace point.

rcbarnett-zz commented 10 years ago

mrdaleroberts: With using your new rulset with the SecRule &GLOBAL:MISSING_SECURE, do we not risk missing out on logging certain "dodgy" cookies where we have 1 dodgy cookies used all over the place (perhaps a tomcat session cookie) that logs all the time and thus fills the "log it 10 times bucket" and so effective hides an application specific (and potentially much more important) administration cookie that only get issues and persists for short periods of time on rare occasions.

Is it possible to track individually named cookies with a 10 log message limit ? or will that create far too many global variable to track even if we only create ones for what look like session cookies ?

rcbarnett-zz commented 10 years ago

rcbarnett: This is a valid concern. With App Defects, there is a balance for needing to log all of them (like you would want for attack issues) vs. periodic alerting. The idea with App Defects is that you want to raise awareness to a certain class of vuln/misconfiguration so that the Admin can then look deeper to see if the issue is present in other areas.

I will check to see if we can be more granular.

rcbarnett-zz commented 10 years ago

mrdaleroberts: One very minor concern, regarding de-coupling the alerting and fixing, is that for our systems, I have built in a way to allow bypassing mod_security on our non production systems. This allows TEST teams to verify that any issue they encounter isn't in fact caused by mod_security rules. This is done, roughly, as follows:

In general apache configs:

RewriteCond %{HTTP_HOST} ^(?:.*\.|)(oursite-ote\.net)$ [NC,OR]
RewriteCond %{HTTP_HOST} ^(?:.*\.|)(oursite-test\.net)$ [NC,OR]
RewriteCond %{HTTP_HOST} ^(?:.*\.|)(oursite-dev\.net)$ [NC]
RewriteRule ^/modsecuritybypass$ / [CO=WAF-BYPASS:sceetesting:%1:60:/::httponly,L]

in mod_security "config file":

We use our own cookie test so we can test/bypass mod_security with any client browser

though we control what domains we accept the cookie from

SecRule REQUEST_HEADERS:Host ".*oursite(-ote|-test|-dev)?.net" "chain,phase:1,t:none,nolog,pass" SecRule REQUEST_COOKIES:WAF-BYPASS "^testing$" "t:none,ctl:ruleEngine=DetectionOnly,setvar:tx.regression_testing=1,setenv:regression_testing=true"

Obviously, wherever a "raw" Apache configuration directive appears in mod_Security rules files, the above ability to bypass mod_security is "broken". Which means someone hassling us busy admin types to disable it, let them test, re-enable it etc. All of this takes us time and slows down our testers, which isn't ideal.

Anyway, obviously this is a concern specific to us rather than the wider mod_security user base, but it's a very useful function if you have separate test teams that you'd like to provide more "self service" ability too in respect of mod_security.

rcbarnett-zz commented 10 years ago

mrdaleroberts: I know you're still investigating, but as part of testing I have noticed a couple of errors with the mod_header replace so I have and update to the regex's.

Here's the slightly updated mod_Security regex for identifying cookies:

SecRule RESPONSEHEADERS:/Set-Cookie2?/ "(?i:([.]?(COOKIE|TOKEN)|[aj]?sessionid|(php)?sessid|(asp|jserv|jw)?session[-]?(id)?|cf(id|token)|sid))" "phase:3,id:'981183',t:none,pass,nolog,setvar:tx.sessionid%{matched_var}=%{matched_var}"

Here's the fix for the dodgy rewrite (the one I had was adding parts of the cookies stuff to the end of the cookie) that does the adding of a flag cleanly:

[ Fix Missing "httponly" Flag ]

Header edit Set-Cookie "^(?i)([A-Z][.]?(?:COOKIE|TOKEN)|[aj]?sessionid|(?:php)?sessid|(?:asp|jserv|jw)?session[-]?(?:id)?|cf(?:id|token)|sid)=([^;]+)(?!.; ?httponly ?(;|$).*)" "$1=$2; HttpOnly" env=httponly_cookie

[ Fix Missing "secure" Flag ]

Header edit Set-Cookie "^(?i)([A-Z][.]?(?:COOKIE|TOKEN)|[aj]?sessionid|(?:php)?sessid|(?:asp|jserv|jw)?session[-]?(?:id)?|cf(?:id|token)|sid)=([^;]+)(?!.; ?httponly ?(;|$).*)" "$1=$2; Secure" env=secure_site

rcbarnett-zz commented 10 years ago

mrdaleroberts: Ooops - spot the deliberate mistake (apologies ignore previouse post fix parts)

[ Fix Missing "httponly" Flag ]

Header edit Set-Cookie "^(?i)([.A-Z][.]?(?:COOKIE|TOKEN)|[aj]?sessionid|(?:php)?sessid|(?:asp|jserv|jw)?session[-]?(?:id)?|cf(?:id|token)|sid)=([^;]+)(?!.; ?httponly ?(;|$).*)" "$1=$2; HttpOnly" env=httponly_cookie

[ Fix Missing "secure" Flag ]

Header edit Set-Cookie "^(?i)([.A-Z][.]?(?:COOKIE|TOKEN)|[aj]?sessionid|(?:php)?sessid|(?:asp|jserv|jw)?session[-]?(?:id)?|cf(?:id|token)|sid)=([^;]+)(?!.; ?secure ?(;|$).*)" "$1=$2; Secure" env=secure_site

rcbarnett-zz commented 10 years ago

rcbarnett: Updated Header directive.