How to protect your cookies using Citrix NetScaler
I recently did a web application firewall (WAF) project for a big company owning and hosting hundreds of websites. They did several penetration tests. One of them focussed on cookies. Citrix NetScaler did a great job protecting cookies, cookie tampering was impossible, but they had been able to steal cookies.
Stealing cookies is not that easy, especially if a website is well protected and XSS (cross site scripting) is blocked it is near to impossible. It would be easy stealing cookies using XSS: post document.cookies to a website of attacker’s choice, that’s it. But it’s also easy in a lab environment: Just pick them up, copy them to an other box and feed them back in. This is, what they did. And NetScaler failed (actually no surprise to me).
How does Cookie protection work?
We have several methods. In most cases, we need sessionization. Citrix NetScaler will store information about cookies (hashes) and will drop tampered cookies. We also could encrypt cookies. This would make cookie tampering hard for an attacker, as he has to guess (brut force) the key. We could cache (session-) cookies on Citrix NetScaler. In addition we could mark cookies HttpOnly or Secure. All these methods target cookie tampering, not cookie stealing.
A Cookie-Monster stealing cookies? What can we do?
Sure, NetScaler can’t do anything. NetScaler will add a session cookie to the existing cookies (change the default name from citrix_ns_id to something else, an attacker does not nescessarily need to know about Citrix NetScaler ADC protecting our website). A NetScaler will make 100% sure none can tamper cookies. But it will definitely allow stealing of cookies.
We need to put additional cookies into the data stream identifying the client. We need to find something specific.
The IP address
Unfortunately the IP address is not half as specific as people think. Mobile phone networks are typically NATing their users to the internet. It’s rather likely for us to share the same IP if we share the same mobile phone provider. An other drawback of the IP is: it may change while users move from one network to an other, let’s say move from home (there is Wi-Fi connected to DSL) to the street (using LTE coming from a totally different provider).
Even though there are some draw-backs: The IP address is a good thing to use. It would be easy for an attacker to fake it, if he only knew. So we have to keep it a secret and encrypt it.
There is a wide variation in User-Agent strings being sent from client to server. Clients differ in language, browser type and version, operating system and many more. It would be easy for an attacker to fake this string, if he only knew. So we have to keep it a secret and encrypt it.
sure, what ever you have, use it. My customer uses several things in parallel.
Implementing the solution
We will add cookies, so we need names for these. I’m a great fan of cheating. The more you cheat the less likely an attacker would understand your setup. So I’ll call my example cookies Tmp-Data and Default-Printer.
Creating these cookies
I create two rewriting policies in response direction:
Citrix NetScaler Policy Actions:
add rewrite action rw_act_setCookie_IP insert_http_header Set-Cookie "\"Default-Printer=\" + CLIENT.IP.SRC.TYPECAST_TEXT_T.ENCRYPT"
This policy action extracts client’s IP from HTTP request, converts it into text, and encrypts it.
add rewrite action rw_act_setCookie_User-Agent insert_http_header Set-Cookie "\"Tmp-Data=\" + HTTP.REQ.HEADER(\"User-Agent\").ENCRYPT"
This policy action extracts the User-Agent string from the original HTTP request and encrypts it.
Citrix NetScaler Policies
add rewrite policy rw_pol_setCookie_IP true rw_act_setCookie_IP
add rewrite policy rw_pol_setCookie_User-Agent true rw_act_setCookie_User-Agent
We are using true as a policy condition because we want this to be in done every request.
Binding these Policies
Just bind these policies to a vServer of choice. No matter if it’s a cs vServer, or a lb vServer.
Checking incoming traffic
Citrix NetScaler WAF will protect all cookies, including the ones we created, from being tampered. So we don’t have to worry about these cookies being tampered. But what if a request comes in, not containing these cookies? That’s more than possible: Every user session starts with a request not containing cookies. So we must allow requests without pre-existing cookies, or we could strip all cookies from an initial request. We must not allow requests containing all application cookies, but not our ones. If you are dealing with an existing website: There may already be persistent cookies stored on a client device. Persistent cookies usually don’t contain sensitive information. They store things like settings rather than the user’s identity.
What kind of policy will we use to check for cookies? There are two possible answer. My first one would be: Responder. Just drop requests, or redirect them to a safe location. Drawback of this is, it’s not all done at the same place. And responder policies usually don’t log (we may force them to log). So why not use Application Firewall policies instead? There are three built in profiles: APPFW_BYPASS, APPFW_RESET, APPFW_DROP, APPFW_BLOCK (for details see here). I will use APPFW_DROP to drop silently.
The Citrix NetScaler Application Firewall policy:
the policy dropping requests containing stolen cookies
add appfw policy appfw_pol_drop_wrongcookie "HTTP.REQ.COOKIE.VALUE(\"Default-Printer\").EQ(\"\").NOT && (HTTP.REQ.COOKIE.VALUE(\"Default-Printer\").DECRYPT.EQ(CLIENT.IP.SRC.TYPECAST_TEXT_T).NOT || HTTP.REQ.COOKIE.VALUE(\"Tmp-Data\").DECRYPT.EQ(HTTP.REQ.HEADER(\"User-Agent\")).NOT)" APPFW_DROP
This policy will be triggered if cookie “Default-Printer” is not empty and either “Default-printer” does not match the IP or “Temp-Data” does not match User-Agent.
the policy dropping requests with missing cookies
add appfw policy appfw_pol_drop_missingcookie "(HTTP.REQ.COOKIE.VALUE(\"Default-Printe\").EQ(\"\") || HTTP.REQ.COOKIE.VALUE(\"Default-Printe\").EQ(\"\")) && (HTTP.REQ.COOKIE.VALUE(\"<the session cookie of your application goes here>\").EQ(\"\").NOT" APPFW_DROP
This policy drops requests not containing “Default-Printer” cookie or “Temp-Data” cooke and at the same time contains your application’s session cookie.
I hope you liked my tricks. I’d be happy to hear your thoughts on this, just drop some words in the comment box to let me know about your thoughts. Feel free to link to my page when ever you like.