General security

Securing Cookies Using HTTP Headers

Srinivas
July 23, 2015 by
Srinivas

In the previous articles in this series on defending against web attacks using HTTP headers, we have seen the usage of X-Frame-Options and X-XSS-Protection headers. In this article, we will explore using HTTP headers to secure cookies.

Introduction:

Cookies are one of the most sensitive items during a user's session. An authentication cookie is as powerful as a password. Security of these authentication cookies is an important subject. This article demonstrates how we can implement some of the cookie attributes in PHP applications in order to protect cookies from certain attacks.

Cookie protection using HTTP Headers:

HttpOnly:

It is a known fact that, Cross Site Scripting is one of the dangerous vulnerabilities that allows an attacker to steal cookies from the user browser. HttpOnly is introduced to disable the ability to read cookies using external JavaScript. Even if an application is vulnerable to XSS, it is not possible to read cookies when HttpOnly flag is enabled.

Let us launch our sample application we used in the previous articles to see how it can be accomplished.

First, lets look at the headers from the HTTP response.

[plain]

HTTP/1.1 200 OK

Date: Sun, 12 Apr 2015 15:07:14 GMT

Server: Apache/2.2.29 (Unix) mod_fastcgi/2.4.6 mod_wsgi/3.4 Python/2.7.8 PHP/5.6.2 mod_ssl/2.2.29 OpenSSL/0.9.8y DAV/2 mod_perl/2.0.8 Perl/v5.20.0

X-Powered-By: PHP/5.6.2

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Set-Cookie: PHPSESSID=a2ed2bf468dd811c09bf62521b07a023; path=/

Content-Length: 820

Keep-Alive: timeout=5, max=100

Connection: Keep-Alive

Content-Type: text/html; charset=UTF-8

[/plain]

As we can see, there are no additional flags with the Set-Cookie header and thus an attacker can read this session id if the application is vulnerable to XSS.

In order to avoid this, we can implement HttpOnly flag, which enables us to send cookies only over http protocol but not JavaScript.

Enabling HttpOnly flag in your application:

The sample code snippet below shows a way to set HttpOnly flag in PHP applications.

[php]

<?php ini_set("session.cookie_httponly", "True"); session_start(); session_regenerate_id(); if(!isset($_SESSION['admin_loggedin'])) { header('Location: index.php'); } if(isset($_GET['search'])) { if(!empty($_GET['search'])) { $text = $_GET['search']; } else { $text = "No text Entered"; } } ?>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Admin Home</title>

<link rel="stylesheet" href="styles.css">

</head>

<body>

<div id="home">

<center>

</br>

<legend><text id=text><text id="text2">Welcome to Dashboard...</text></br></br> You are logged in as: <?php echo $_SESSION['admin_loggedin']; ?> <a href="logout.php">[logout]</a></text></legend>

</br>

<form action="" method="GET">

<div id="search">
<text id="text">Search Values</text><input type="text" name="search" id="textbox"></br></br>

<input type="submit" value="Search" name="Search" id="but"/>

<div id="error"><text id="text2">You Entered:</text><?php echo $text; ?></div>

</div>

</form>

</center>
</div>

</body>

</html>

[/php]

As we can see in the above piece of code, the following line is used to enable HttpOnly.

[plain]

ini_set("session.cookie_httponly", "True");

[/plain]

Let us now observe the headers and see if HttpOnly is enabled. Below are the response headers intercepted using Burp proxy.

[plain]

HTTP/1.1 200 OK

Date: Sun, 12 Apr 2015 15:03:15 GMT

Server: Apache/2.2.29 (Unix) mod_fastcgi/2.4.6 mod_wsgi/3.4 Python/2.7.8 PHP/5.6.2 mod_ssl/2.2.29 OpenSSL/0.9.8y DAV/2 mod_perl/2.0.8 Perl/v5.20.0

X-Powered-By: PHP/5.6.2

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Set-Cookie: PHPSESSID=36cb82e1d98853f8e250d89be857a0d3; path=/; HttpOnly

Content-Length: 820

Keep-Alive: timeout=5, max=100

Connection: Keep-Alive

Content-Type: text/html; charset=UTF-8

[/plain]

As we can see in the headers above, set-cookie header looks as shown below with HttpOnly flag enabled.

Set-Cookie: PHPSESSID=36cb82e1d98853f8e250d89be857a0d3; path=/; HttpOnly

The real use of this HttpOnly flag is shown below. When an attacker finds an XSS and tries to read the cookies using JavaScript, it won't work.

As we can see in the above figure, the script we executed is unable to read the cookies though the application is vulnerable to XSS.

Secure:

The next cookie attribute is "secure". We often see websites that run on both HTTP and HTTPS. When an application sends its cookies over HTTP, it is possible that they can be hijacked using various ways since they are transmitted in clear text format. "secure" attribute on set-cookie header forces your application to send cookies only over HTTPS.

Below is the code snippet that shows how we can use "secure" flag in PHP applications.

[php]

<?php ini_set("session.cookie_secure", "True"); session_start(); session_regenerate_id(); if(!isset($_SESSION['admin_loggedin'])) { header('Location: index.php'); } if(isset($_GET['search'])) { if(!empty($_GET['search'])) { $text = $_GET['search']; } else { $text = "No text Entered"; } } ?>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>Admin Home</title>

<link rel="stylesheet" href="styles.css">

</head>

<body>

<div id="home">

<center>

</br>

<legend><text id=text><text id="text2">Welcome to Dashboard...</text></br></br> You are logged in as: <?php echo $_SESSION['admin_loggedin']; ?> <a href="logout.php">[logout]</a></text></legend>

</br>

<form action="" method="GET">

<div id="search">
<text id="text">Search Values</text><input type="text" name="search" id="textbox"></br></br>

<input type="submit" value="Search" name="Search" id="but"/>

<div id="error"><text id="text2">You Entered:</text><?php echo $text; ?></div>

</div>

</form>

</center>
</div>

</body>

</html>

[/php]

The following line from the above code enables secure attribute on the cookies.

[plain]

ini_set("session.cookie_secure", "True");

[/plain]

Let's look at the response headers to see how it looks like when the above code is run.

[plain]

HTTP/1.1 200 OK

Date: Sun, 12 Apr 2015 15:14:30 GMT

Server: Apache/2.2.29 (Unix) mod_fastcgi/2.4.6 mod_wsgi/3.4 Python/2.7.8 PHP/5.6.2 mod_ssl/2.2.29 OpenSSL/0.9.8y DAV/2 mod_perl/2.0.8 Perl/v5.20.0

X-Powered-By: PHP/5.6.2

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Set-Cookie: PHPSESSID=f95afc96ecb7acc6c288d31f941e682f; path=/; secure

Content-Length: 820

Keep-Alive: timeout=5, max=100

Connection: Keep-Alive

Content-Type: text/html; charset=UTF-8

[/plain]

As we can see from the above headers, the secure attribute has been enabled on set-cookie header. In my localhost, I didn't enable HTTPS support for this application. When we refresh the page, since we do not have HTTPS to send this cookie over a secure channel, the session will not be active anymore over HTTP. This is because, this session cookies are not sent to the server because it requires HTTPS.

Killing the session upon closing the browser:

It is quite common for the user to close the browsers without clicking the logout button. When we use sensitive applications, it is necessary that the session has to be expired when we close the browser forcibly without logging out.

The following lines of code can be used in PHP to accomplish this.

[plain]

session_set_cookie_params(0);

session_start();

[/plain]

Assume that we are running the application with the above property set. Now, login to the application and close the browser. If we reopen the page now, the session will be inactive.

To check if this property is enabled or not, we can use a cookie editor like "EditThisCookie" in chrome.

Login to the application and open "EditThisCookie" extension as shown below.

As we can see in the above figure, "Session" is checked. This ensures that our session will not be active upon closing the browser.

The same can also be seen from chrome's developer tools as shown below.

Following are some other cookie attributes

Domain: This attribute gives control on the domains that can access the cookies.

Path: it specifies the path of the domain that can have access to the cookie set by the server.

Expiration: It specifies when this cookie has to be expired. So that the cookie won't be valid anymore.

We can set all these attributes in PHP applications just with three lines of code as shown below.

[plain]

ini_set("session.cookie_secure", "True"); //secure

ini_set("session.cookie_httponly", "True"); //httponly

session_set_cookie_params(3, '/', '.localhost'); //This cookie is valid for 3 seconds (max age)

// “/” ensures that this cookie is valid on all //paths of this domain

// since the domain is prefixed with dot, this //cookie is accessible from all the subdomains.

session_start();
[/plain]

When we load the page with the above attributes, the response headers will be as shown below.

[plain]

HTTP/1.1 200 OK

Date: Thu, 30 Apr 2015 03:04:11 GMT

Server: Apache/2.2.29 (Unix) mod_fastcgi/2.4.6 mod_wsgi/3.4 Python/2.7.8 PHP/5.6.2 mod_ssl/2.2.29 OpenSSL/0.9.8y DAV/2 mod_perl/2.0.8 Perl/v5.20.0

X-Powered-By: PHP/5.6.2

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0

Pragma: no-cache

Set-Cookie: PHPSESSID=f4d99777d9810bfedb6869acd556bc66; expires=Thu, 30-Apr-2015 03:04:14 GMT; Max-Age=3; path=/; domain=.localhost; secure; HttpOnly

X-XSS-Protection: 1

Content-Security-Policy: script-src 'self'

Content-Length: 820

Keep-Alive: timeout=5, max=100

Connection: Keep-Alive

Content-Type: text/html; charset=UTF-8

[/plain]

What should you learn next?

What should you learn next?

From SOC Analyst to Secure Coder to Security Manager — our team of experts has 12 free training plans to help you hit your goals. Get your free copy now.

In this article, we have seen how cookies can be secured using various attributes available with set-cookie response header. Though, these concepts can drastically improve the security of a web application, we cant solely depend on those headers to protect an application, rather we should consider using them to add additional layer of security.

Srinivas
Srinivas

Srinivas is an Information Security professional with 4 years of industry experience in Web, Mobile and Infrastructure Penetration Testing. He is currently a security researcher at Infosec Institute Inc. He holds Offensive Security Certified Professional(OSCP) Certification. He blogs atwww.androidpentesting.com. Email: srini0x00@gmail.com