www.invicti.com Open in urlscan Pro
2606:4700::6812:918  Public Scan

URL: https://www.invicti.com/white-papers/security-cookies-whitepaper/
Submission: On October 02 via api from GB — Scanned from GB

Form analysis 2 forms found in the DOM

POST

<form method="POST" class="ns-form mktoForm mktoHasWidth mktoLayoutLeft" data-forminstance="form-1" data-mktoformid="1172" id="" __bizdiag="196352765" __biza="W___" novalidate="novalidate" data-styles-ready="true">
  <style type="text/css">
    .mktoForm .mktoButtonWrap.mktoSimple .mktoButton {
      color: #fff;
      border: 1px solid #75ae4c;
      padding: 0.4em 1em;
      font-size: 1em;
      background-color: #99c47c;
      background-image: -webkit-gradient(linear, left top, left bottom, from(#99c47c), to(#75ae4c));
      background-image: -webkit-linear-gradient(top, #99c47c, #75ae4c);
      background-image: -moz-linear-gradient(top, #99c47c, #75ae4c);
      background-image: linear-gradient(to bottom, #99c47c, #75ae4c);
    }

    .mktoForm .mktoButtonWrap.mktoSimple .mktoButton:hover {
      border: 1px solid #447f19;
    }

    .mktoForm .mktoButtonWrap.mktoSimple .mktoButton:focus {
      outline: none;
      border: 1px solid #447f19;
    }

    .mktoForm .mktoButtonWrap.mktoSimple .mktoButton:active {
      background-color: #75ae4c;
      background-image: -webkit-gradient(linear, left top, left bottom, from(#75ae4c), to(#99c47c));
      background-image: -webkit-linear-gradient(top, #75ae4c, #99c47c);
      background-image: -moz-linear-gradient(top, #75ae4c, #99c47c);
      background-image: linear-gradient(to bottom, #75ae4c, #99c47c);
    }
  </style>
  <div class="mktoFormRow">
    <div class="mktoFieldDescriptor mktoFormCol">
      <div class="mktoOffset"></div>
      <div class="mktoFieldWrap mktoRequiredField"><label for="Email" id="LblEmail" class="mktoLabel mktoHasWidth">
          <div class="mktoAsterix">*</div>Work Email
        </label>
        <div class="mktoGutter mktoHasWidth"></div><input id="Email" name="Email" placeholder="Work Email *" maxlength="255" aria-labelledby="LblEmail InstructEmail" type="email" class="mktoField mktoEmailField mktoHasWidth mktoRequired"
          aria-required="true"><span id="InstructEmail" tabindex="-1" class="mktoInstruction"></span>
        <div class="mktoClear"></div>
      </div>
      <div class="mktoClear"></div>
    </div>
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow marketo-checkbox-row">
    <div class="mktoFieldDescriptor mktoFormCol">
      <div class="mktoOffset"></div>
      <div class="mktoFieldWrap"><label for="Consent_Explicit_Opt_In__c" id="LblConsent_Explicit_Opt_In__c" class="mktoLabel mktoHasWidth">
          <div class="mktoAsterix">*</div>
        </label>
        <div class="mktoGutter mktoHasWidth"></div>
        <div class="mktoLogicalField mktoCheckboxList mktoHasWidth"><input name="Consent_Explicit_Opt_In__c" id="Consent_Explicit_Opt_In__c" type="checkbox" value="yes"
            aria-labelledby="LblConsent_Explicit_Opt_In__c InstructConsent_Explicit_Opt_In__c" class="mktoField"><label for="Consent_Explicit_Opt_In__c" id="LblConsent_Explicit_Opt_In__c"></label></div><span id="InstructConsent_Explicit_Opt_In__c"
          tabindex="-1" class="mktoInstruction"></span>
        <div class="mktoClear"></div>
      </div>
      <div class="mktoClear"></div>
    </div>
    <div class="mktoFormCol">
      <div class="mktoOffset mktoHasWidth"></div>
      <div class="mktoFieldWrap">
        <div class="mktoHtmlText mktoHasWidth">I'd like to receive product and industry updates <script type="text/javascript" src="chrome-extension://nkimffhpgcdokjcjnffnpdajfbapllpk/packages/adobe-engage/injector.js"></script>
        </div>
        <div class="mktoClear"></div>
      </div>
      <div class="mktoClear"></div>
    </div>
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow">
    <div class="mktoFormCol">
      <div class="mktoOffset mktoHasWidth"></div>
      <div class="mktoFieldWrap">
        <div class="mktoHtmlText mktoHasWidth marketo-privacy-line">Your information will be kept <a href="https://www.invicti.com/legal/privacy-policy/" target="_blank" title="Privacy Policy" id="">private</a>. <script type="text/javascript"
            src="chrome-extension://nkimffhpgcdokjcjnffnpdajfbapllpk/packages/adobe-engage/injector.js"></script>
        </div>
        <div class="mktoClear"></div>
      </div>
      <div class="mktoClear"></div>
    </div>
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="utm_term__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="utm_source__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="direct">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="utm_medium__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="direct">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="utm_campaign__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="utm_content__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="GCLID__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="GA_Client_ID__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="GA1.1.264523853.1696232232">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="vwo_variation__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="2">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="vwo_id__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="410">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="Landing_Page_URL__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="https://www.invicti.com/white-papers/security-cookies-whitepaper/">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="Page_Prior_to_Form_URL__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="Form_Submit_URL__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="https://www.invicti.com/white-papers/security-cookies-whitepaper/">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="CountryCode" class="mktoField mktoFieldDescriptor mktoFormCol" value="GB">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoFormRow"><input type="hidden" name="Netsparker__c" class="mktoField mktoFieldDescriptor mktoFormCol" value="true">
    <div class="mktoClear"></div>
  </div>
  <div class="mktoButtonRow"><span class="mktoButtonWrap mktoSimple"><button type="submit" class="mktoButton btn btn--primary btn--full">Download</button></span></div><input type="hidden" name="formid" class="mktoField mktoFieldDescriptor"
    value="1172"><input type="hidden" name="munchkinId" class="mktoField mktoFieldDescriptor" value="923-HQB-396">
</form>

POST

<form method="POST" class="ns-form mktoForm mktoHasWidth mktoLayoutLeft" data-forminstance="form-1" data-mktoformid="1172" __bizdiag="199758147" __biza="W___" novalidate="novalidate"
  style="font-family: Helvetica, Arial, sans-serif; font-size: 13px; color: rgb(51, 51, 51); visibility: hidden; position: absolute; top: -500px; left: -1000px; width: 1600px;"></form>

Text Content

Netsparker is now Invicti
Get a demo
AppSec with Zero Noise Get a demo
Get a demo
 * Product
   * Overview
   * Features
 * Why Us?
   * Solutions
     * Industries
       * IT & Telecom
       * Government
       * Financial Services
       * Education
       * Healthcare
     * Roles
       * CTO & CISO
       * Engineering Manager
       * Security Engineer
       * DevSecOps
   * Comparison
   * Case studies
   * Customers
   * Testimonials
 * Pricing
 * About Us
   * Our Story
   * In the news
   * Careers
   * Contact us
 * Resources
   * Blog
   * White Papers
   * Webinars
   * Resource Library
   * Invicti Learn
   * Partners
     * Channel
     * MSSP
   * Support

M
E
N
U
All White Papers
All White Papers


TABLE OF CONTENTS

 1.  HTTP and Ticket System
 2.  How Do Cookies Work?
 3.  Cookie Attributes
     1. name:
     2. domain:
     3. secure:
     4. expires:
     5. max-age:
 4.  Modifying Cookies with JavaScript
     1. Protecting Session Cookies With httpOnly
 5.  Introduction to Session Cookies
 6.  Analyzing Sessions
     1. session_start()
     2. Other Options to Hide Sessions
 7.  Cookie Attributes in Terms of Security
     1. name[=value] Attribute
     2. domain Attribute
     3. path Attribute
     4. expires and max-age
     5. httpOnly Flag
     6. secure Flag
     7. SameSite Attribute
     8. Strict and Lax Values
 8.  Cookie Prefixes
     1. Examples of the Importance of Prefixes
     2. __Secure- Prefix
     3. __Host- Prefix
 9.  Extra Measures
 10. Conclusion

DOWNLOAD THIS WHITE PAPER AS A PDF

*
Work Email




*




I'd like to receive product and industry updates



Your information will be kept private.

















Download

Your information will be kept private

White Paper


SECURITY COOKIES

This paper will take a close look at cookie security and shed light on various
security measures that should be implemented in order to protect the content of
sensitive cookies, and to protect users against a range of different attacks.


HTTP AND TICKET SYSTEM

In many regards, the most common internet protocol, HTTP, works like the ticket
system in a subway. For HTTP servers, just like train personnel, it’s impossible
to identify every user when they interact with thousands of visitors on a daily
basis. For ticket inspectors, service tickets (just like cookies) are the only
way to differentiate users and find out whether they are authorized to ride with
the subway. HTTP similarly needs definitive proof that the user sending the
request is allowed to do so, at least when it comes to visiting restricted
areas, such as an admin panel. HTTP does so by using cookies and session IDs.  


HOW DO COOKIES WORK?

When it was first introduced, the HTTP protocol was only used to provide static
content, such as HTML files. But new methods to interact with visitors were
developed relatively quickly. One of the key features that was required to
ensure proper communication between server and client was to have a singular
value (an ID) to distinguish the requests of user A from those of user B. In
1994, a Netscape employee, Lou Montulli, realized the necessity of this ID when
he was designing a shopping application. He decided to adapt a magic cookie
concept that was widely used in Unix.

Soon after, cookies began to be supported with the release of the first version
of Netscape Navigator. Later, all browsers supported cookies. Besides a few
security additions, cookies preserved their initial structure. Despite rumors
stating otherwise, cookies are part of a simple database that does not contain
any malicious code, with each cookie able to hold up to 4KB of data.


COOKIE ATTRIBUTES

Let’s take a look at the cookie attributes. Before we begin, you should note
that the attributes must be separated with a semicolon and a space.


NAME:

Although cookies are generally set in the format name=value, browsers do not
strictly expect it to be this way. Therefore, cookies can be set with only the
name. Some web applications set cookies without a value in lieu of a boolean
data type. If the cookie name exists, its value is seen as being true. If it
doesn’t, the value is seen as being false. Below is such a cookie, that
indicates whether the user wants to open the page in full-screen mode.

Set-Cookie: fullScreen; path=/;

A website may set this kind of cookie to remember some settings you’ve made in
the future.


DOMAIN:

This is the attribute that specifies the domain to which the cookie will be sent
when you visit a website. Think of it in terms of train tickets again. You can’t
use a train ticket from Madrid to Barcelona to board an airplane. In the same
way, browsers won’t allow you to use a cookie for google.com on amazon.com. This
is an optional attribute. If it’s not specified, the domain name of the site
that set the cookie will be used implicitly.

A cookie may be also be used in multiple subdomains belonging to the same
domain. For instance, a cookie set for example.com, may be sent with the
requests to mail.example.com, calendar.example.com or crm.example.com.

Set-Cookie: Scanner=Invicti; domain=example.com

path: The path attribute is another optional attribute for cookies. It will
specify the exact path that needs to be present in the URL for the cookie to be
sent.

If the path is  ‘/’, the browser will send the cookie along with all the
requests to example.com, regardless of the path. If the path is /foo, all the
requests to example.com/foo and example.com/foo/baz will contain this cookie.

If no path attribute is given, the default path value, which is the page on
which the cookie was set, will be used.

Set-Cookie: Scanner=Invicti; path=/foo


SECURE:

When you mark the cookie as secure, you make sure that, in addition to the
domain and path matching above, the connection type has to be HTTPS for the
cookie to be sent. If this optional attribute isn’t given, the cookie will be
sent to all requests that match the domain and path, regardless of its state of
security. You can only set this attribute in an HTTPS request. You should keep
in mind that this variant of a same origin policy is fundamentally different
from the same origin policy that JavaScript adheres to, in that it doesn’t take
the protocol into account by default.


EXPIRES:

The optional expires attribute specifies the amount of time a cookie will be
stored in the browser. If it’s not specified, the cookie will be stored as long
as the browser is open. It’s deleted when the browser is closed. The cookies
that have the expires attribute set to a date in the distant future, are known
as Persistent Cookies. The expected format for the expires attribute is: Wdy,
DD-MM-YYYY HH:MM:SS GMT:

Set-Cookie: Scanner=Invicti; domain=example.com; path=/; expires=Sun, 21-02-2018 08:25:01 GMT

If you want to delete a cookie from the client browser, you set a past date as
the expires value, since cookies are deleted automatically after they expire:

Set-Cookie: Scanner=Invicti; domain=example.com; path=/; expires=Sun, 21-02-1977 08:25:01 GMT


MAX-AGE:

This one is nearly identical to the expires attribute, but it uses seconds
instead of an actual date.

Set-Cookie: Scanner=Invicti; domain=example.com; path=/; max-age=86400

If the value is 0 or lower, the cookie will be removed from the user’s browser.


MODIFYING COOKIES WITH JAVASCRIPT

Using the document.cookie object, you can generate cookies and change their
content using JavaScript.

You can read the cookies the client browser sent to your website by reading the
value of the document.cookie attribute. They are stored in the following format:

Cookie1=Cookie1Val; Cookie2=Cookie2Val;

You can use the following code to set cookies using JavaScript:

document.cookie = "name=Invicti; domain=example.com; path=/";


PROTECTING SESSION COOKIES WITH HTTPONLY

Cookies can be read, modified and deleted by JavaScript. Although there are
countless benefits to storing various user data for JavaScript to read, some
cookies should be off limits for JavaScript. One example is a session cookie,
which is only of use to the server, but should not be accessible by JavaScript,
as it allows easy session hijacking if an XSS vulnerability is present.

In 2002, Microsoft developed the httpOnly flag, which was later added to the
HTTP Cookie specifications, and has since been supported by all modern browsers
to prevent this specific security risk.

If the cookie has an httpOnly flag set, the browser will only send it together
with HTTP requests, but will not make it available to JavaScript, hence the name
httpOnly.

You can label a cookie with httpOnly by simply adding the httpOnly attribute in
the Set-Cookie header:

Set-Cookie: PHPSESSID=tgce245t7alseaugc36nvbu681; domain=lab.local; path=/; httpOnly

This screenshot shows the Google Chrome Developer console.



It shows two cookies: one is called Cookie and the value ‘Normal’, the other is
called PHPSESSID with a session ID as its value. Additionally,
the PHPSESSID cookie is marked as ‘httpOnly’, indicated with a checkmark
highlighted with a red circle.



If you read the value of document.cookie, you will see that only the cookie that
wasn’t marked as ‘httpOnly’ (Cookie) is available for JavaScript, unlike
the PHPSESSID cookie.


INTRODUCTION TO SESSION COOKIES

Let’s take a look at how cookies and session handling have developed since 1994.

All visitors to your websites are of equal importance. However, some visitors
may have advanced privileges, and are therefore able to access certain content
that others can not. These visitors may be page editors or those who purchase
services from you.

As the administrator of the webpage, you need to integrate a login form for
users to introduce themselves and therefore receive the special treatment they
require. Since HTTP is a stateless protocol, it can’t really tell users apart or
remember whether they are already logged in. Instead of asking users to
repeatedly fill out the login form to access each page they visit, you can use
this cookie to automatically authorize them. (Warning, do not use code like this
in production. It’s insecure!)

Set-Cookie: isLogged=Yes;

This cookie allows the website to save the user information, and identify them
when required.  We can set multiple cookies within the browser limit in one HTTP
response by using the Set-Cookie header. After a successful login, the response
will look like this:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Set-Cookie: isLogged=Yes;
Set-Cookie: username=Customer;

It’ll look like this from the server side:

<?php
if($_COOKIE["isLogged"]=="Yes") {
	echo "Welcome ".$_COOKIE["name"];
} else {
	echo "Please log in!";
}
?>

However, nothing prevents a user with malicious intentions from crafting a
cookie that would log them into the website, even though they don’t have an
account. This can easily be done with JavaScript and document.cookie, or by
using curl or a similar tool.



After the cookie is manipulated, the user can log in:




ANALYZING SESSIONS

Instead of exposing the variables and values in a cookie, which may affect our
web applications’ workflow, decision mechanisms, and authorization processes,
the session mechanism was developed to protect those elements from potential
attacks.

With sessions, the critical data is kept away from the client-side by generating
IDs that refer to the sensitive data on the server-side. These IDs are then
stored on the client-side in the form of cookies. What makes them secure is that
they are typically very long and unpredictable, which makes it practically
impossible for an attacker to guess another user’s session ID.

Every time the user makes a request to the server, the browser sends the user’s
session ID to the server, which allows it to identify the user and access the
stored data for the respective session ID.


SESSION_START()

In PHP applications, when the session_start() function is called, the first
thing PHP will do is to check whether a cookie was sent or not (by default, its
name is PHPSESSID).



You can view the path to the directory that contains the PHP session files using
the phpinfo() function.



If the PHPSESSID cookie is not sent with the request, PHP will request the
browser to set a cookie with the name PHPSESSID.



This cookie contains a long and secure session ID.



Once the session_start function checks whether the PHPSESSID value is valid,
it’ll proceed to step two: check whether there’s already a session file that
contains the ID of the PHPSESSID cookie.



If the file doesn’t exist, it will create it in the following format:
“sess_PHPSESSIDVALUE“. But if the file exists, it’ll open the file, deserialize
the serialized object it contains and store it in a global variable called
$_SESSION.

Here’s an example of how data is stored in session files:

<?php
session_start();
if(authenticate($user, $passwd)) {
	$_SESSION["loggedIn"] = "yes";
	$_SESSION["last_login"] = date("Y-m-d H:i:s");
} else {
	echo "Try again!";
} 



To see what’s stored in the session variable you can call the var_dump function
with $_SESSION as its argument.

<?php
session_start();
var_dump($_SESSION);

This is the result.

array(2) { ["loggedIn"]=> string(3) "yes" ["last_login"]=> string(19) "2016-02-19 13:48:20" }


OTHER OPTIONS TO HIDE SESSIONS

PHP stores the session data in text files by default. Even though it has several
advantages, such as accessing and reading session files rapidly, you might want
to store session files elsewhere for security measures.



Since the www-data has permission for the directory, which holds the session
data, you have to protect the files carefully against the risk of cookie data
being stolen, because that puts all users into danger.

On the other hand, for those who prefer distributed systems for their web
applications, storing session data in text files is not possible. Even though
multiple machines can access the files in a folder system, the sequential access
will slow down the system or lock it down altogether.

Thankfully, using PHP’s session handler functions, you can direct and customize
all the events from generating a cookie, choosing where it’s stored, to it’s
removal. You have to use the session_set_save_handler() function to specify the
functions which will be triggered in the events from the start to the end of the
session.

bool session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_sid ] )

Let’s take a look the six parameters assigned to this function.

 1. callable $open(string $savePath, string $sessionName): It will be called
    together with the session_start function to start the session. It must
    return (bool) TRUE or (bool) FALSE.
 2. callable $close: The supplied function acts as a destructor method for the
    classes. It must return (bool) TRUE or (bool) FALSE.
 3. callable $read(string $sessionId): This function must return a serialized
    session data. The value returned here will be deserialized and submitted to
    the super global $_SESSION.
 4. callable $write(string $sessionId, string $data): It’s called when the
    session is going to be ended or when the session values are going to be
    saved.
 5. callable destroy($sessionId): When the session is ended, this function is
    called. It must return TRUE or FALSE.
 6. callable gc($lifetime): It’s called randomly by PHP, and should clear up the
    old session data.
 7. callable create_sid(): This is an optional parameter that expects a function
    that is called when a new session key is required. It has to return a
    string.


COOKIE ATTRIBUTES IN TERMS OF SECURITY

In this section, we will take a look at all the components of the cookies that
might make an attack surface and discuss the possible attacks, their effects,
and methods of protection.

As we stated above, a cookie has determinants such as a name-value pair,
expires, path, domain, and httpOnly and secure flags.  


NAME[=VALUE] ATTRIBUTE

The only obligatory parameter is the name parameter. We can evaluate the name
and the value differently from the attacker’s perspective.

The name, isn’t an attack vector on it’s own, but it can give some idea of the
application to the attacker, allowing the user to get to know the target and
form an attack strategy.

For example, PHP applications use PHPSESSID cookie by default. You can find out
the major platforms/frameworks and their cookie names in the table below:

Cookie NameFramework / PlatformJSESSIONIDJava PlatformASPSESSIONIDIIS WEB
ServerASP.NET_sessionidMicrosoft ASP.NETCFID/CFTOKENCold
Fusionzope3ZopecakephpCakePHPkohanasessionKohanalaravel_sessionLaravelci_sessionCodeigniter

The value is the most important point that makes the user unique on the server
side, and acts as a reference to their data on the server-side. If an attacker
can read it, the user’s session can be stolen. Therefore, the value set here
must be hard to guess and unique. You can’t for example use a value like
user:admin and obfuscate it (e.g. by using base64). Instead you should always
use a proper session ID.

COOKIE HEADERS

But even if the session ID is cryptographically secure, attackers can still
employ an attack known as Session Fixation. This means that instead of guessing
the value of the session ID cookie, the attacker sets that cookie for you. This
attack often occurs if the cookie value is visible in the URL.

http://www.example.com/index.php?PHPSESSID=Attacker

When you click such a link, or visit it unknowingly, e.g. through a hidden
iframe on another page or as one of multiple redirects, your new session ID will
be set to the string “Attacker”. If you log into your account, PHP will write
any session information, such as your username, email address, access level and
other things into the file associated with this ID. However, the attacker now
knows the value of your session ID, since he set it himself. All he has to do is
to set his own session id to the string “Attacker”. Since PHP associates this
session ID with your account and the attacker sends it to the vulnerable
application, it will think that the attacker and you are the same person. This
grants the attacker access to your account. There are ways to mitigate this, for
example by storing the IP address of the user in the session and compare it to
the IP of the user who makes the request. That means only one person can be
logged in at any given time, but it might also cause issues for users whose IP
often changes, such as TOR and mobile users.



“Simple example of Session Fixation attack” by OWASP is licensed under CC BY-SA
4.0.

SOLUTION TO A SECURE SESSION

After a successful authorization process, the session keys must be regenerated
and sent to the user with the code below. It’s important to regenerate the
session ID in order to avoid session fixation attacks.

<?php
$_SESSION['logged_in'] = FALSE;
if (check_login())
{
	session_regenerate_id();
	 $_SESSION['logged_in'] = TRUE;
}


DOMAIN ATTRIBUTE

The domain attribute is crucial for the security of cookies. Let’s look at a
website with subdomains to see why that’s the case.

Before anything else happens the cookies’ domain and the hostname of the
requested URL are compared. The other criteria is checked only if that
comparison was successful.

But what is considered as “same origin” yet again differs significantly from
JavaScript’s understanding of the term. While JavaScript requires an exact match
of two hostnames, cookies are a little less picky as it’s perfectly legal to set
a cookie for a subdomain.

EXAMPLE OF SETTING THE DOMAIN ATTRIBUTE

Let’s assume there are two different accounts created on the
webhoster badsites.local. Each of them has their own subdomain.

victim.badsites.local

attacker.badsites.local

The if you visit the domains above, you can read the content their owners made
available.  However, if the owners want to change said content they need to
login to badsites.local and do the necessary changes over the control panel.

When the user victim logs in to the system, this is the HTTP request their
browser sends.

POST http://badsites.local/control.php HTTP/1.1
Host: badsites.local
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 35

username=victim&password=victim

There are three possible domain property values the cookie might contain. To
make things even more complicated, different browsers might behave differently
for each of them. The browsers used in the following test are Chrome
69.0.3497.100, Internet Explorer 11, Mozilla Firefox 44.0.2 and Edge
42.17134.1.0.

Case 1: Domain value of the cookie is not set:

HTTP/1.1 200 OK
Set-Cookie: PHPSESSID=ock3keuidn0t24vrf4hkvtopm0; path=/;
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

As you can see the domain parameter wasn’t set. All of the tested browsers react
similarly. Google Chrome, Edge, Internet Explorer 11 and Firefox will not send
the cookie with the requests done for the subdomains.

There is an exception though, since older versions of Internet Explorer (such as
11.0.10240.17443 and below) will send this cookie even if one of the subdomains
under badsites.local is requested. That means, that once the owner visits
attacker.badsites.local, while being logged in to badsites.local, the attacker
can read the victims cookies and take over their account.

GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: tr,en-US;q=0.7,en;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: attacker.badsites.local
Cookie: PHPSESSID=ock3keuidn0t24vrf4hkvtopm0

Case 2: Cookie domain value is set to badsites.local:

You can see the second possibility below. The domain attribute might be set to
badsites.local.

HTTP/1.1 200 OK
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Set-Cookie: PHPSESSID=1fr54qg3j9rf77toohcpcsk8h0; path=/; domain=badsites.local
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 66
Content-Type: text/html

All of the tested browsers – IE, Edge, Chrome, and Firefox – will add the cookie
generated by badsites.local to the requests to attacker.badsites.local.

GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: tr,en-US;q=0.7,en;q=0.3
Host: attacker.badsites.local
Pragma: no-cache
Cookie: PHPSESSID=1fr54qg3j9rf77toohcpcsk8h0

Yet again, this may lead to an account takeover if you visit a malicious
badsites.local subdomain.

Case 3: Cookie domain value is set as .badsites.local:

HTTP/1.1 200 OK
Set-Cookie: PHPSESSID=q3a20kfes2u6fgvgsrspv0rpf0; path=/; domain=.badsites.local;
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

IE, Edge, Chrome, and Firefox will add the cookie to requests made
for attacker.badsites.local.
GET / HTTP/1.1

Accept: text/html, application/xhtml+xml, */*
Accept-Language: tr,en-US;q=0.7,en;q=0.3
Proxy-Connection: Keep-Alive
Host: attacker.badsites.local
Cookie: PHPSESSID=q3a20kfes2u6fgvgsrspv0rpf0

As seen in the cases above, cookie domain values have to be set with great care.
If the correct values aren’t given, your application might be at great risk. So
what could the badsites.local host have done better to avoid all of this
confusion and inconsistency between browsers? The answer is simple. They could
have used the www subdomain for their control panel. Doing so restricts the
cookies to www.badsites.local  and its subdomains even if you leave the domain
value empty.

In situations where you have to host websites with multiple users, do not host
the control panel or other potentially risky websites under the main domain. For
example, Github, the most popular hoster for git repositories also allows to
upload HTML and markup files in order enable developers to create accompanying
websites for their repositories. These may contain content such as product
documentation, or personal websites. The sites are created under github.com as a
repository containing these files. However, they are hosted on a completely
different domain, namely github.io. http://github.io doesn’t contain any
content. Instead it just redirects to pages.github.com. This separation of
content and content management makes it easy to avoid domain problems with
cookies.


PATH ATTRIBUTE

path is another important feature for the cookie security. Browsers check
the path value right after they check domain. Just like the domain attribute’s
value, the path value might result in critical security vulnerabilities, if not
carefully set.

The path feature is optional, and the default value is “/”, which means that the
cookie will be sent together with every request, regardless of its path.

Unlike the domain attribute, the path value is checked from left to right.

But like the domain attribute, browsers behave differently. These differences
may result in vulnerabilities in your applications.

EXAMPLE OF SETTING PATH ATTRIBUTE

We’re going to test two cases where the path value does and does not have the
“/” sign in the end. We’ll be sending requests
to http://badsites.local/victim/, http://badsites.local/victim/sub/,
and http://badsites.local/victim-fake/ from Firefox, Edge, Chrome, and Internet
Explorer browsers. The page that set the cookie is http://badsites.local/victim/

CASE 1: PATH VALUE DOESN’T HAVE “/” AT THE END (E.G. /VICTIM)

HTTP/1.1 200 OK
Set-Cookie: PHPSESSID=r9evv4bft7luq4h7c4l5q8b1o4; path=/victim;
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

IE and Edge sends the cookie in all three requests:

GET /victim/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: tr,en-US;q=0.7,en;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Proxy-Connection: Keep-Alive
Host: badsites.local
Cookie: PHPSESSID=r9evv4bft7luq4h7c4l5q8b1o4

GET /victim/sub/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: tr,en-US;q=0.7,en;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Proxy-Connection: Keep-Alive
Host: badsites.local
Cookie: PHPSESSID=r9evv4bft7luq4h7c4l5q8b1o4

GET /victim-fake/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: tr,en-US;q=0.7,en;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Proxy-Connection: Keep-Alive
Host: badsites.local
Cookie: PHPSESSID=r9evv4bft7luq4h7c4l5q8b1o4

In Chrome and Firefox, the cookie is sent only with the requests
to http://badsites.local/victim and http://badsites.local/victim/sub.

CASE 2: PATH VALUE HAS “/” AT THE END (E.G. /VICTIM/)

HTTP/1.1 200 OK
Set-Cookie: PHPSESSID=j0sbcvo5h8q8a1g6n7l4kmaqo5; path=/victim/;
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache

The Internet Explorer, Edge, Chrome, and Firefox browsers send the cookie only
with the requests
to http://badsites.local/victim and http://badsites.local/victim/sub.

Due to this difference in the browsers, the websites hosted under the sub
directories can access the session variables set for other directories.
Especially if you’re using a module like the Apache mod_userdir, you have to be
careful about the value set for the cookies.

SOLUTION

If you’re going to set a path value for the cookie, you should put a “/” at the
end of the value.


EXPIRES AND MAX-AGE

These attributes are optional and they specify the duration of storing the
cookie. If they are not specified, the browser will treat them as a session
cookie and will delete them once the browser is closed.

The purpose of the cookie is the deciding factor for the appropriate time-out
settings. Large expire or max-age values might pose security risks when using
devices that are shared among multiple users.

As mentioned above, expire and max-age can be used simultaneously. However, you
should note that browsers will prioritize the max-age value if you use both
attributes.


HTTPONLY FLAG

Cookies are not only used to control the user session, but also to help
applications customize their user preferences. Many scripts store their
customization settings as cookies, and access and modify these settings using
JavaScript.

For example, to set the user’s language preferences, you set the following:

Set-Cookie: prefLanguage=en;

Or, if you want the browser to remember that the user wants to view the videos
fullscreen, you set the following:  

Set-Cookie: fullScreen=yes;

However, the cookies that hold the session data are only crucial to the server,
since they hold the reference keys to the session data. Hence, they don’t have
to be read by JavaScript. In the presence of an XSS vulnerability, cookies read
by JavaScript may make the exploitation of such a vulnerability easier by
allowing session hijacking. Therefore, the session data cookies must have
the httpOnly value set.

For example:

Set-Cookie: PHPSESSID=a984308kdf9845; path=/; httpOnly;


SECURE FLAG

HTTP requests are transferred as plaintext between the client and the server.
Someone listening to the network using a Man in the Middle (MiTM) attack may
acquire the session data, one of the most crucial types of data for web
browsing.

You can prevent this threat by specifying the secure attribute when you create
cookies. This attribute ensures that the cookies are only sent over a secure
(HTTPS) connection.

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

But you shouldn’t just send the session cookies exclusively over HTTPS. Even if
the cookie doesn’t seem important first, it might have huge implications when it
comes to the privacy of your users. There is a Blackhat USA talk from 2016, that
explains this in detail.


SAMESITE ATTRIBUTE

When browsers are instructed to open a website, they check whether there’s a
matching cookie in their cookie database. After successfully checking the
domain, path and secure attributes, they will send the respective cookie with
the request.

This doesn’t just happen for the page you want to visit, but for all the
external JavaScript, CSS, and image files from any third-party sources.

The critical point here is that when you visit website attacker.com, all
requests made from attacker.com to example.com will contain the cookie from
example.com. By default, this doesn’t have any security implications for the
content of the cookies. Even though the attacker instructs the browser to send
example.com’s cookies to example.com, attacker.com can’t read them.



However, there is a common technique that takes advantage of the fact that
cookies are always sent to the respective website, no matter where the request
originated from, namely the Cross-Site Request Forgery (CSRF) attack.
Technically speaking, nothing would prevent an attacker from creating a form
like this:

<form method = "POST" action = "https://www.example.com/account/edit">
	<input name = "user_biography" value = "pwned by attacker.com!">
</form>
<script>document.forms[0].submit()</script>

Being able to change the user’s profile is a rather harmless side effect. It
becomes much more serious if you can change an email addresses. If an attacker
changes the email address of the user to his own email address, he can then
trigger the password reset functionality and reset the user’s password. This
allows him to take over the account.

When you visit a website, your browser sends requests on everything found on
that website, including the embedded code for social media sharing on the page
like Facebook, Google Analytics and CDN resources, for example. The cookies set
by the third-party resources on the browser are sent alongside these requests.
The name of the website that sends the request is found in the Referer header in
the request. In this way, third-parties use the cookies with the referrer data
and discover the browsing habits of the users.    

Normally, you can prevent this tracking by disabling the third-party cookies
in Firefox and Chrome. However, this might have a negative impact on your
browsing experience.

The SameSite cookie feature allows administrators to restrict to which requests
cookies are added. The SameSite parameter allows you to remove cookies from
certain requests if they weren’t issued from their own website, instead of
disabling all of them.

Setting the SameSite Cookie is pretty simple. All you have to do is to add
SameSite=Lax or SameSite=Strict parameters to your cookie.

Set-Cookie: CookieName=CookieValue; SameSite=Lax;

Set-Cookie: CookieName=CookieValue; SameSite=Strict;


STRICT AND LAX VALUES

Strict and Lax refer to different levels of security.

STRICT

If you use the Strict value, the cookies will not be sent when a third-party
issues the requests. In some cases, this option might negatively impact your
browsing experience. For example, if a website you visit by clicking on a link
has the SameSite=Strict set, the website might request you to log in again,
since the cookie won’t be sent along with the request.

LAX

If you use the lax value, this allows cookies to be sent if the third-party
issues a GET request that affects the Top Level Navigation, which means that the
request will change the address bar. Only those requests will be allowed to have
the cookie sent with the Lax value.



You can load a source with the iframe, img, or script tags. These resources will
be called with GET but they will not change the address in the address bar,
since they don’t need Top Level navigation. That’s why the cookies will not be
sent with requests made to those sources.

This table summarizes the request types and the cookies sent to them.

Request TypeExample CodeCookies sentLink<a href=”…”></a>Normal, LaxPerender<link
rel=”prerender” href=”..”/>Normal, LaxForm GET<form method=”GET”
action=”…”>Normal, LaxForm POST<form method=”POST”
action=”…”>Normaliframe<iframe src=”…”></iframe>
NormalAJAX$.get(“…”)NormalImage<img src=”…”>Normal


COOKIE PREFIXES

The most precise point in the relationship between cookie and session is that
the server looks up the file in the database that matches the cookie value and
transfers this to a session variable. If the server doesn’t find a file, it
makes a new one with the appropriate name.

The attacks are possible if developers do not take extra measures. For example,
if the cookie is regenerated each time the session is online, the Session
Fixation attack discussed above will not be possible. This is the first reason
why cookie prefixes exist.

The second reason is similar. The individualizing factors of a cookie
are domain, path, and name. The other flags, secure, httpOnly, and SameSite do
not act as a differentiating factor in the browser’s cookie jar.


EXAMPLES OF THE IMPORTANCE OF PREFIXES

For example, let’s say you set a cookie like this:

Set-Cookie: MyCookie=value; path=/;

The cookie may be overridden by these:

Set-Cookie: MyCookie=newvalue; path=/; secure;

Set-Cookie: MyCookie=newvalue; path=/; httpOnly;

A different scenario might be possible. Let’s say an attacker takes advantage of
an XSS vulnerability on your website but he can’t access your cookie set with
httpOnly. He overrides the secure session cookie using JavaScript, which results
in an overwritten cookie and would even allow the attacker to carry out a
session fixation attack.

We can break the attack down with the example below. This is your cookie:

Set-Cookie: MyCookie=value; path=/; httpOnly;

An attacker uses the code below to override the cookie:

document.cookie="MyCookie=AttackerValue; path=/;"

In the same way, if an attacker cannot access the cookie set as secure by
intercepting your network requests, she can force the user’s browser to connect
over HTTP and reach that cookie.

Here’s a dangerous scenario possible on IE or Edge browsers.

The cookie set by example.com:

Set-Cookie: MyCookie=SetByExample.com; path=/;

hacker.example.com returns the following:

Set-Cookie: MyCookie=hacked; path=/;

The cookie set by example.com, MyCookie, will have its value overridden to
‘hacked’, too.

What are the solutions to these issues? Cookie prefixes prevent cookies from
overrides through non-secure connections, subdomains, and JavaScript.


__SECURE- PREFIX

When the  __Secure– prefix is added to a cookie name, the cookie is accessible
from HTTPS connections only.

Here’s an example:

Set-Cookie: __Secure-MyCookie=value; path=/; secure;


__HOST- PREFIX

When the __Host– prefix is added to a cookie name, it performs the same job as
__Secure-. But it also ensures that only the domain that sets the cookie can
override the cookie. Subdomains cannot alter the cookie. The domain attribute
should not be set and the path should be set as ‘/’ for this prefix to work.

Set-Cookie: __Host-MyCookie=value; path=/; secure;

It is important to note that the attack above is only possible in IE and Edge
browsers. The feature, suggested by Eric Lawrence, has been revised in 2016
by Mike West, and is no longer supported by IE and Edge browsers.


EXTRA MEASURES

Here are some recommendations:

 * Use each cookie for a single task. Do not use the session cookies for other
   operations, such as password resetting. Using cookies for multiple tasks
   might result in application workflow complications, or
   vulnerabilities. Session Puzzling is an example of how dangerous it is to use
   cookies for multiple tasks.
 * Use the optimal settings for each application session and choose
   appropriate max-age or expires values, depending on the application’s type.
   For example, for a forum or a wiki site, a one hour expiration time is ideal,
   or for a banking application, five minutes would suffice. Once the time is
   up, the session variables will be removed from the server.
 * When the session is ended, you have to ensure that the variables are no
   longer valid on both the server and client side. If the session isn’t ended
   on the server side, attacks such as Cookie Replay might be possible.


CONCLUSION

In this article, we discussed how applications use cookies and session objects
to allow for secure storage of session related data on the server side.

However, it is alarming to see that from the day their specification was
released, cookies haven’t been used properly, and people still don’t set them
correctly.

Many web applications are vulnerable to attacks due to the incorrect
implementation of cookie values. Automatized tools cannot always test the
features entirely. Therefore, developers have to set cookies with the greatest
of care, and test how they behave in various browsers before releasing them into
the wild.

.

Author:

Ziyahan Albeniz

Translators:

Sven Morgenroth
Umran Yildirimkaya



Invicti Security Corp
1000 N Lamar Blvd Suite 300
Austin, TX 78703, US

© Invicti 2023

 * RESOURCES
   * Features
   * Integrations
   * Plans
   * Case Studies
   * Advisories
   * Invicti Learn
 * USE CASES
   * Penetration Testing Software
   * Website Security Scanner
   * Ethical Hacking Software
   * Web Vulnerability Scanner
   * Comparisons
   * Online Application Scanner
 * WEB SECURITY
   * The Problem with False Positives
   * Why Pay for Web Scanners
   * SQL Injection Cheat Sheet
   * Getting Started with Web Security
   * Vulnerability Index
   * Using Content Security Policy to Secure Web Applications
 * COMPANY
   * About Us
   * Contact Us
   * Support
   * Careers
   * Resources
   * Partners

© Invicti 2023
 * Legal
 * Privacy Policy
 * California Privacy Rights
 * Terms of Use
 * Accessibility
 * Sitemap

By using this website you agree with our use of cookies to improve its
performance and enhance your experience. More information in our Privacy Policy.

OK