{"id":280,"date":"2018-11-13T12:53:07","date_gmt":"2018-11-13T12:53:07","guid":{"rendered":"https:\/\/int64software.com\/blog\/?p=280"},"modified":"2019-01-03T09:42:28","modified_gmt":"2019-01-03T09:42:28","slug":"hardening-website-security-part-2-user-session-cookie-security","status":"publish","type":"post","link":"https:\/\/int64software.com\/blog\/2018\/11\/13\/hardening-website-security-part-2-user-session-cookie-security\/","title":{"rendered":"Hardening Website Security \u2013 Part 2: User Session Cookie Security"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It feels like almost every week there\u2019s another news item about personal information being stolen because yet another company\u2019s website got hacked.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Most of these attacks are perpetrated through social engineering, persuading somebody to hand over some detail which allows the hacker to gain additional privileges and, eventually, access to personal information. However, a lot are still carried out due to poor security or misconfigured websites.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is the second in a series of articles which will aim to demystify some of the concepts you must get your head around if you hope to run a secure website in the 21st century.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Contents<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The series will be consist of the following topics:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li><a href=\"https:\/\/int64software.com\/blog\/2018\/11\/05\/hardening-website-security-part-1-http-security-headers\/\">HTTP Security Headers<\/a><br>o 1a. <a href=\"https:\/\/int64software.com\/blog\/2018\/11\/10\/hardening-website-security-part-1a-hsts-preloading\/\">HSTS Preloading<\/a><\/li><li>User Session Security<\/li><li><a rel=\"noreferrer noopener\" aria-label=\"HTTP Security Headers\no 1a. HSTS Preloading\u2028User Session Security\u2028Database Security\n\u2028Safely Handling User Input (Coming Soon) (opens in a new tab)\" href=\"https:\/\/int64software.com\/blog\/2018\/11\/21\/hardening-website-security-part-3-website-database-security\/\" target=\"_blank\">Database Security<\/a><br><\/li><li><a href=\"https:\/\/int64software.com\/blog\/2018\/12\/04\/hardening-website-security-part-4-safely-handling-user-input\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"HTTP Security Headers\no 1a. HSTS Preloading\u2028User Session Security\u2028Database Security\n\u2028Safely Handling User Input\n (opens in a new tab)\">Safely Handling User Input<\/a><br><\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Topics Not Covered<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I have opted not to cover server infrastructure security concerns at this time due to the huge number of possible configurations (hosting packages or VPS, operating systems, dashboard systems, firewalls, etc.) While I may cover some of these specifically in future articles, there\u2019s too much nuance in these subjects to be able to do them justice here.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Disclaimer<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Everything presented in this article is the result of years of experience, or trial and (frequent) error. Messing around with the code and\/or settings on a live site can (and likely will) lead to unexpected, possibly disastrous results. The information presented here is correct to the best of the author\u2019s knowledge, but in matters of security we strictly advise the reader to make sure they carry out additional research and understand the dangers before making any changes to their own systems or web sites. Any code presented here is done so as example only, and may be incomplete or contain errors. Readers should be careful when copying and pasting code from any website, this site included. Int64 Software Ltd, its employees and its representatives accept no liability for damage caused by the misuse, either intentional or unintentional, of the information presented in its posts and articles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Section Overview<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In this section we will be looking at how you can increase the security of user sessions on your web server, and reduce the risk of Session Hijack Attacks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dynamic websites will typically make use of Sessions in order to store data for each user to facilitate the logic behind things like user logins, or shopping carts. All session data is stored securely on the web server except for a single cookie which identifies that user and associates them with their session data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">On an unsecured website, if an attacker is able to capture this session cookie (which can be done many different ways such as packet sniffing, Cross Site Scripting attacks (XSS), or just looking over the person\u2019s shoulder), they could manually add that to their own web browser and effectively \u201chijack\u201d that user\u2019s session, gaining access to their data. We aim to look at the ways that this can be prevented by utilising a few simple settings and techniques.<\/p>\n\n\n\n<figure class=\"wp-block-image internalad\"><a href=\"https:\/\/overcee.com\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"154\" src=\"https:\/\/int64software.com\/blog\/wp-content\/uploads\/2018\/11\/overcee-clipped-graphic-1200.png\" alt=\"Overcee - Delegated Device Management System for Windows Service Desks\" class=\"wp-image-286\"\/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Session Settings<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Session Name<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The session name is the name of the cookie that is set on the client\u2019s web browser. The value of this cookie will be a unique, randomly generated string which identifies the user, but the name is important as well.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By default, the cookie name used by web server software is predictable and descriptive, such as \u201c<em>PHPSESSID<\/em>\u201d for PHP, or \u201c<em>ASP.NET_SessionId<\/em>\u201d for ASP.NET. This means that if an attacker is trying to steal this cookie\u2019s value, it is very easy for them to identify the correct cookie.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this reason, it is recommended that the session name is set to something incredibly generic so that it may be overlooked, such as just \u201cid\u201d.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">PHP<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Before calling \u201c<em>session_start();<\/em>\u201d, call the \u201c<em>session_name<\/em>\u201d method to set your session cookie name:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>session_name(\u2018id\u2019);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">ASP.NET<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Specify the \u201c<em>cookieName<\/em>\u201d attribute in your sessionState property of the web.config file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;sessionState cookieName=\u201did\u201d>&lt;\/sessionState><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Only Use Cookies<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Many web servers offer the ability to automatically attach the session ID to the end of any POST or GET requests. The reason for this is that it allows for sessions to still be used even if the user has disabled cookies in their browser.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, this presents three problems:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>An attacker can easily identify and read this value, which could lead to Session Hijack attacks.<\/li><li>It is possible an attacker could, having got this value, send a crafted URI back to the user which could carry an unintended action on the website using the user\u2019s session information. This is known as a <strong>Cross-Site Request Forgery<\/strong> attack (covered in Part 4 of this series).<\/li><li>If you make use of scripted Ajax calls, you may have to remember to manually append the session ID to each request.<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">For these reasons, it is recommended that this option is disabled.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">PHP<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Note that since PHP 5.3.0 this is disabled by default. However, if you want to ensure it is disabled then check the \u201c<em>php.ini<\/em>\u201d file for the following settings.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>session.use_only_cookies = 1\nsession.use_trans_sid = 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Alternatively, before sending any output to the client you can change this in your PHP files with the command:<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ini_set(\u2018session.use_only_cookies\u2019, true);\nini_set(\u2018session.use_trans_sid\u2019, false);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">ASP.NET<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\" style=\"text-align:left\">It is a little harder to restrict this in ASP.NET which calls it \u201c<em>cookieless<\/em>\u201d. First you need to set the sessionState to disable cookieless mode in your web.config:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;sessionState cookieless=\u201dUseCookies\u201d>&lt;\/sessionState><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The, also in your web.config file, set any login forms to not use cookieless mode:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;authentication mode=\u201dForms\u201d>\n\t&lt;forms loginUrl=\u201dlogin.aspx\u201d cookieless=\u201dUseCookies\u201d requireSSL=\u201dtrue\u201d path=\u201d\/MyApplication\u201d \/>\n&lt;\/authentication><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next, also in &#8220;<em>web.config<\/em>&#8220;, set &#8220;<em>anonymousIdentification<\/em>&#8221; to disable cookieless (Note that I haven&#8217;t included all of the other anonymousIdentification settings here for the sake of brevity):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;anonymousIdentification cookieless=\u201dUseCookies\u201d \/> <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, you need to catch requests from clients which have cookies disabled and handle them. This script will simply block the request, but you may want to redirect them instead:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>If(Request.Browser.Cookies == false)\n{\n\tResponse.Close();\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Strict Mode<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">By default in PHP, if a client sends the server a session ID that it doesn\u2019t recognise for that client, it will create a new session using that ID. This can lead to <strong>Session Fixation Attack<\/strong> where the attacker uses the ID of another user\u2019s session to hijack their session.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To combat this, Strict Mode should be enabled. By doing this, if PHP receives an uninitialized session ID, it will generate a new ID and send that back to the client.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To enable this mode, in \u201c<em>php.ini<\/em>\u201d make sure the following is set:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>session.use_strict_mode = 1<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Alternative, in your PHP files you can set this with the command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ini_set(\u2018session.user_strict_mode\u2019, true);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I was unable to find a single solid answer in my research as to whether this is a problem in ASP.NET as well, or what the fix is if it is. If anyone knows, please drop me an <a href=\"mailto:mestacey@int64software.com\">email<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Session Cookie Options<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Lifetime<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The session cookie should be given a suitably short lifetime value so that if the user idles for a time, then they must, for example, re-authenticate when returning to your website.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Many web servers do this by default, and limit it to 30 minutes or below. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Secure<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">By setting the \u201c<em>Secure<\/em>\u201d attribute on the session cookie, web browsers are instructed to only send the cookie over encrypted HTTPS channels. Failing to set this can mean that the cookie could be captured or modified through a Man-in-the-Middle attack.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Note that this is possible <strong>even if your web site enforces HTTPS-only<\/strong> communications via HSTS, redirects, or <strong>even if no HTTP connection is presented<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">HttpOnly<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Setting the \u201c<em>HttpOnly<\/em>\u201d property prevents web scripts (JavaScript, VBScript, etc) from accessing this cookie via the \u201c<em>document.cookie<\/em>\u201d DOM object, which is important to prevent a <strong>Cross Site Scripting (XSS)<\/strong> attack from stealing the session cookie ID.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Regenerating Session ID<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In order to limit the amount of time an attacker has to use a stolen session ID to hijack the session, it is necessary to limit the lifespan of a session.<br>This can be achieved in one of two ways:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>The session is timed out and removed from both the client and server, forcing the user to re-enter any stored information (by logging back in, or re-adding items to their shopping cart).<\/li><li>The session is kept active, but the session ID is regenerated with a new random value.<\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">Naturally, from a user experience viewpoint, the latter option is preferable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In addition to regularly regenerating the session ID, it should also be refreshed on a Security State Change, such as when a user logs in or out, or when they attempt to access sections of website which are deemed more secure than others (such as Administrator functions). <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Further Reading<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For more information on Session Cookie security, we highly recommend the <a href=\"https:\/\/www.owasp.org\/index.php\/Session_Management_Cheat_Sheet\">Open Web Application Security Project\u2019s (OWASP) Session Management Cheat Sheet<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s it for this article. Next time we\u2019ll be looking into making sure your Database connection and transactions are kept secure. <a href=\"https:\/\/int64software.com\/blog\/2018\/11\/21\/hardening-website-security-part-3-website-database-security\/\" target=\"_blank\" rel=\"noreferrer noopener\" aria-label=\"That's it for this article. Next time we\u2019ll be looking into making sure your Database connection and transactions are kept secure. To continue reading, click here.\n (opens in a new tab)\">To continue reading, click here.<\/a><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this section we will be looking at how you can increase the security of user sessions on your web server, and reduce the risk of Session Hijack Attacks.<\/p>\n","protected":false},"author":1,"featured_media":283,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","footnotes":""},"categories":[13,50,7],"tags":[70,9,69,68],"class_list":["post-280","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","category-security","category-tutorial","tag-cookies","tag-security","tag-sessions","tag-webdev"],"_links":{"self":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts\/280","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/comments?post=280"}],"version-history":[{"count":8,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts\/280\/revisions"}],"predecessor-version":[{"id":437,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/posts\/280\/revisions\/437"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/media\/283"}],"wp:attachment":[{"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/media?parent=280"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/categories?post=280"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/int64software.com\/blog\/wp-json\/wp\/v2\/tags?post=280"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}