Confusing ASP.NET session cookie rewriting with HttpOnly flag version 10
Hi Everyone, first post here so a little introduction.
I am a sysadmin/developer for a large insurance company and have just taken ownership of our F5 box. 12 Years IT experience so I can usually figure stuff out.. But not today.
We have been flagged on a pentest recently that we are not adding the Secure flag and HttpOnly flag to our cookies. Our site is very diverse so getting all the devlopment depts up to speed is a challange.
So I should be able to do this with an Irule right? (V10 so no HTTP::cookie HttpOnly "name" Enable, I can't use that) Well, I have a working solution, but I'd like some help on the behaviour described below:
Using Ncat on backtrack to see the raw traffic this is a standard response from the server for a .NET session: (No rule manipulation at this point)
-------------------------------------------------------------------------------------
user@bt:~ ncat mydomain.com 443 --ssl
HEAD /test.aspx HTTP/1.1
Host:mydomain.com
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 21216
Content-Type: text/html; charset=utf-8
Expires: -1
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Set-Cookie: ASP.NET_SessionId=d4or5si4ezfo3oiienjmzjug; path=/; HttpOnly
Date: Wed, 02 Jan 2013 15:26:51 GMT
-------------------------------------------------------------------------------------
OK so we have a pretty standard server response here.
Now when I write the following Irule Code:
when HTTP_RESPONSE {
set myValues [HTTP::cookie names]
foreach mycookies $myValues {
set currentValue [HTTP::cookie $mycookies]
HTTP::cookie remove $mycookies
HTTP::header insert "Set-Cookie" "$mycookies=$currentValue; HttpOnly; Secure; Path=/;"
}
}
This code seems simple enough, get a list of cookie names. Loop through them. Extract the value of the cookie. Delete it, recreate from stored name and value with the required flags. What I get is this:
-------------------------------------------------------------------------------------
X-AspNet-Version: 2.0.50727
Set-Cookie: HttpOnly
Date: Web, 02 Jan 2013 15:36:14 GMT
Set-Cookie: ASP.NET_SessionId=qmalso4555a4bjbuspcgsj55
Set-Cookie: HttpOnly=; HttpOnly; Secure; Path=/;
----------------------------------------------------------------------------------------
Errr OK, I now have 3 cookies? and two are called "HttpOnly"? This seems rather odd, lets do some debugging. It appears to be looping twice even though there is one cookie. So Ill add some header inserts as debug statements to show where I am in the loop:
when HTTP_RESPONSE {
set myValues [HTTP::cookie names]
foreach mycookies $myValues {
HTTP::header insert "Test" $mycookies
set currentValue [HTTP::cookie $mycookies]
HTTP::cookie remove $mycookies
HTTP::header insert "Set-Cookie" "$mycookies=$currentValue; HttpOnly; Secure; Path=/;"
}
}
So a header called test should print out everytime the loop is hit...
Response:
-------------------------------------------------------------------------------------
X-AspNet-Version: 2.0.50727
Set-Cookie: HttpOnly
Date: Web, 02 Jan 2013 15:41:14 GMT
Test: ASP.NET_SessionId
Set-Cookie: ASP.NET_SessionId=pwkwy1452plfijbhlqqtre45
Test: HttpOnly
Set-Cookie: HttpOnly=; HttpOnly; Secure; Path=/;
-------------------------------------------------------------------------------------
OK this is curious. We are looping twice for sure as the Test header appears twice and it seems that its seeing the HttpOnly flag as a cookie???? Also, Why has that Set-Cookie appeared ABOVE the timestamp????
What is going on here? Have I fundamentally misunderstood this programing language? Well since we are looping twice, lets force it to skip the second loop:
when HTTP_RESPONSE {
set myValues [HTTP::cookie names]
foreach mycookies $myValues {
if {$mycookies == "HttpOnly"} {
donothing DO NOT REMOVE THIS LINE!
}
else {
set currentValue [HTTP::cookie $mycookies]
HTTP::cookie remove $mycookies
HTTP::header insert "Set-Cookie" "$mycookies=$currentValue; HttpOnly; Secure; Path=/;"
}
}
}
Lets see what we get now:
-------------------------------------------------------------------------------------
X-AspNet-Version: 2.0.50727
Set-Cookie: HttpOnly
Date: Web, 02 Jan 2013 15:41:14 GMT
Set-Cookie: ASP.NET_SessionId=fc403oqe1aneu4fxcokdfup; HttpOnly; Secure; Path=/;
-------------------------------------------------------------------------------------
Wait a minute....wat? OK so the header insert for the session cookie now works. But we still have this mysterious extra Set-Cookie: HttpOnly. Where and why is this happening?
Now doing a little research I discovered that all .NET session cookies come with the HttpOnly flag by default and cannot be changed in IIS to remove this. OK that means I don't have to add the flag at all, infact I can use the built in "secure" function in HTTP::cookie, Great!
when HTTP_RESPONSE {
set myValues [HTTP::cookie names]
foreach mycookies $myValues {
switch $mycookies {
"ASP.NET_SessionId" {
HTTP::cookie secure "ASP.NET_SessionId" enable
}
default {
set currentValue [HTTP::cookie $mycookies]
if {$mycookies == "HttpOnly"} {
donothing DO NOT REMOVE THIS LINE!
}
else {
HTTP::cookie remove $mycookies
HTTP::header insert "Set-Cookie" "$mycookies=$currentValue; HttpOnly; Secure; Path=/"
}
}
}
}
}
So the above will loop through all cookies, check if the name is ASPNET etc and ONLY add the secure flag. Any other cookie will be rewritten and deleted.
This works fine for the .NET session cookie. All other cookies seem to work fine too, but I still get the odd "Set-Cookie: HttpOnly" for any other cookie thats being rewritten here alway appearing above the date field.
I have now fully tested this with session cookies from ASP.NET, standard ASP (VB6), and custom written cookies. The above code seems to work fine in all situations.
So my real question is... Am I a complete idiot and have missed something obvious here... or does this code loop twice for one cookie and seems to think that the HttpOnly flag is a cookie?
OK now add multiple cookies:
If I have 2 or more cookies AND add a HTTP::cookie remove "HttpOnly" statement to the code, behaviour gets odd depending where. If its in the httponly if statement, the first cookie that gets written has only its name and value (Like above). If its after the loop, the LAST Set-Cookie has the flags removed and only its name and value. In both these scenarios it seems to think that httponly is a cookie name and not a flag.
I'm guessing here that this is a v10 bug thats fixed in v11?