Browser cookies play an important role in nearly all modern websites and applications. From tracking user-interaction through services like Google Analytics, through to maintaining the state of customer shopping carts in eCommerce applications. Cookies can also contain session tokens for web applications to ensure that user sessions are maintained between browser page refreshes.
Although security weaknesses in cookies are a relatively low-risk vulnerability on their own, they can often present a significant risk when combined with other vulnerabilities that may be present in a web application, such as Cross-Site Scripting (XSS).
Cookies containing sensitive information (such as session tokens) can offer an attacker an easy way to gain access to a web application, if the cookies have not been handled correctly. If an attacker is able to obtain the session token of an authenticated user session, they will often be able to access the application as the legitimate user – without requiring the user’s credentials. This method of attack is also known as Session Hijacking.
When a cookie is created by a web application and is sent to be stored in a user’s browser, there are a number of directives that can be configured in the cookie declaration, which determine how the cookie should be handled by the browser. These directives include the following:
- ‘Secure’ Flag
- ‘HTTPOnly’ Flag
- Cookie Scope (Domain & Path Attributes)
- Cookie Expiry
The following example from a web server’s HTTP response shows a cookie being set using the Set-cookie HTTP response header:
Set-Cookie: session=219ffwef9w0f; Path=/; Secure; HttpOnly
If the Secure flag is included as part of a cookie declaration, the web browser will be instructed to only transmit the cookie over network connections that are encrypted using the SSL or TLS protocols.
Ensuring that cookies are only transmitted over an encrypted channel, prevents the cookie from being obtained in plaintext by an attacker, who is able to intercept the network traffic between a user’s web browser and the server to which the user is connecting.
Although the majority of web applications are now only available over HTTPS, the Secure flag should still be included on all cookies. If a future server misconfiguration unexpectedly exposes the application over HTTP, the cookie could be obtained by a malicious user who had access to the unencrypted network traffic. Also, if a user mistypes the URL with the ‘http’ prefix instead of ‘https’, the cookie would be sent by the browser regardless of whether the HTTP service was listening on the web server – also potentially allowing the cookie to be obtained by a malicious user.
Ensuring cookies are only sent through HTTP requests provides protection against attacks such as Cross-Site Scripting (XSS), which will often be used by attackers to access browser cookies containing sensitive information such as user session tokens.
Stealing the session token belonging to a legitimate user is one of the more common goals of an attacker during a Cross-Site Scripting (XSS) attack. If no further protection mechanisms are in place to validate session tokens, it is possible that a stolen session token could be used by an attacker to access the web application as part of a Session Hijacking attack.
The cookie scope determines where a cookie is valid and is set using the Domain and Path directives in the cookie declaration.
The Domain directive of a cookie determines the domain that the cookie will be valid for. For example, a cookie which has been declared to include the directive “domain=shop.myapp.com” will be valid for the shop.myapp.com domain.
It is also possible to set a wildcard value for a cookie domain, so that the cookie is valid for all subdomains of the parent domain. For example, a cookie which has been declared to include the directive “domain=.myapp.com” will be valid for all subdomains of the myapp.com domain.
Allowing cookies to be declared with a wildcard domain scope is often used in applications where there is Single Sign-On (SSO) functionality across multiple applications on the same web server. This functionality allows a user to authenticate once with an application and their authenticated session will remain valid for the other applications on the same domain. This prevents the user from having to re-authenticate when moving between applications.
While using a wildcard domain scope for cookies provides increased convenience for end-users, allowing a cookie to be valid for all subdomains can present a significant risk. If an attacker is able to obtain a cookie containing a session token for an application, the session token will potentially be valid for the applications on each of the subdomains of the web server if no further session validation mechanism is in place. This could allow an attacker to easily pivot an attack into other applications if they have been able to obtain a cookie containing a valid session token.
The Path directive of a cookie determines the URL path for which the cookie will be valid. For example, if a cookie has been declared to include the directive “path=/“, the cookie will be valid for all application paths, from the root directory downwards on the web server.
If a single application resides on the web server and the application is accessible at the root of the domain (for example www.myapp.com/), setting the cookie path to the root directory (i.e. “path=/“) is perfectly acceptable, as there are no other applications which will potentially share the same cookie.
If multiple applications reside on the web server and the cookie path has been set to the root directory, the cookie will be valid for all of these applications. This means that if a second application resides at “…/newapp” on the same server, the cookie will also be valid for this application.
If multiple applications are hosted on the same web server, declaring cookies which contain session tokens that are also scoped to the root directory is considered to be exceptionally poor security practice. If an attacker is able to obtain a cookie for one application on the web server, they may be able to use this cookie to authenticate with all other applications that reside in subdirectories on the server, if no further session validation mechanism is present.
In the scenario that multiple applications are hosted on the same web server, it is strongly recommended, that where possible, the individual applications are placed into their own subdirectory (e.g. “…/myapp1”, “…/myapp2” etc) and that individual cookies are declared which are only valid for their respective application path.
Cookies can be configured with an expiry date & time, after which, the cookie will no longer be considered valid by the web browser.
The following example show the “expires” directive being used to set a cookie to expire on the 31st July 2019 at 7am:
“expires=Wed, 31-Jul-2019 07:00:00 GMT”
If no expiry has been configured for a cookie, the cookie and it’s contents will be deleted when the browser application has been closed.
For cookies that contain session tokens or other sensitive information, it is important to ensure that the cookie has not been configured with an expiry date or time in the future. If a malicious user is able to access the workstation of a valid user, they will be able to obtain the cookies from the browser cache if the cookies have not yet expired. This could allow the attacker to steal the session token(s) of the legitimate user and then use them to access the application.
Inspecting your Application Cookies
Before we can examine a cookie being created by the application, we need to locate an action in the application that will cause a cookie to be created. In the majority of applications containing login functionality, a cookie will be created after a user successfully authenticates with the application. It is also possible that when a user first browses to the application that a cookie will also be generated. This is often the case in eCommerce applications where the contents of a shopping basket need to be maintained between page refreshes – before a user has been prompted to authenticate (often during the checkout process).
There are a number of ways to check how cookies are being set by a web application; however, for the purposes of this article, there are two ways which we use regularly on penetration tests.
Cookie Capture Through a Proxy
During a penetration test of a web application, the consultant performing the test will use a proxy application (such as Burp Suite or ZAP Proxy) to intercept the network traffic that is sent between the web browser and the server. This allows the HTTP requests and responses to be examined in detail and in some cases modified before they are forwarded. For the purposes of this article, it assumed that the reader will understand how to set up a proxy application to intercept browser network traffic and that they will be able to view the HTTP requests and responses that are sent to and from the web application that is being assessed.
When the application sends a cookie back to the users web browser, it will do so using the “Set-cookie” HTTP response header, so in your proxy application, you should see something similar to the following in the HTTP response which is sent to your web browser:
Set-Cookie: session=219ffwef9w0f; Path=/; Secure; HttpOnly
Inspecting Cookies Within your Browser
There are several ways to check how cookies are being set directly on the web browser itself.
Once you have been able to inspect the cookie(s) that your application is generating, the following checks should be carried out on the cookie:
The Secure flag should be set on all cookies containing sensitive information (such as session tokens), regardless of whether or not the application is only available over HTTPS.
The HTTPOnly Flag should be set on all cookies which contain sensitive information, to prevent them from being obtained by an attacker in conjunction with a Cross-Site Scripting (XSS) attack.
The domain directive should be set to the specific domain that the application is accessed from and should not be configured as a wildcard domain. This will prevent a cookie being valid for multiple applications which may reside on other subdomains.
If a single application is present on the web server, it is acceptable to have the cookie path scoped to the root directory. If multiple applications reside on the same web server however, the individual applications should be placed into their own subdirectory (e.g. “…/myapp1”, “…/myapp2” etc) and individual cookies should be declared which are only valid for their respective applications path.
All cookies which contain sensitive information (such as session tokens) should not be configured to have an expiry date & time when they are declared. This will reduce the likelihood of an attacker gaining access to sensitive information from the web browser cache once the browser application has been closed. Cookies which have been declared without an expiry directive will be deleted from the web browser cache once the browser has been closed by the user.
The correct handling of browser cookies is by no means a technically-challenging concept; however, vulnerabilities relating to cookies are incredibly common during penetration tests and are often one area of web application security that many developers overlook. Although weaknesses in the way that cookies are implemented are considered to be a relatively low-risk vulnerability, when combined with other vulnerabilities that may be present in the web application, they can allow for sensitive information to be leaked and in some cases allow for user sessions to be compromised by an attacker.