/** * ================================================================= * Javascript code for OWASP CSRF Protector * Task it does: Fetch csrftoken from cookie, and attach it to every * POST request * Allowed GET url * -- XHR * -- Static Forms * -- URLS (GET only) * -- dynamic forms * ================================================================= */ var CSRFP_FIELD_TOKEN_NAME = 'csrfp_hidden_data_token'; var CSRFP_FIELD_URLS = 'csrfp_hidden_data_urls'; var CSRFP = { CSRFP_TOKEN: 'CSRFP-Token', /** * Array of patterns of url, for which csrftoken need to be added * In case of GET request also, provided from server * * @var {Array} */ checkForUrls: [], /** * Returns true if the get request doesn't need csrf token. * * @param {String} url to check. * @return {Boolean} true if csrftoken is not needed. */ _isValidGetRequest: function (url) { for (var i = 0; i < CSRFP.checkForUrls.length; i++) { var match = CSRFP.checkForUrls[i].exec(url); if (match !== null && match.length > 0) { return false; } } return true; }, /** * Returns auth key from cookie. * * @return {String} auth key from cookie. */ _getAuthKey: function () { var regex = new RegExp(`${CSRFP.CSRFP_TOKEN}=([^;]+)(;|$)`); var regexResult = regex.exec(document.cookie); if (regexResult === null) { return null; } return regexResult[1]; }, /** * Returns domain name of a url. * * @param {String} url - url to check. * @return {String} domain of the input url. */ _getDomain: function (url) { // TODO(mebjas): add support for other protocols that web supports. if (url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) { return document.domain; } return /http(s)?:\/\/([^\/]+)/.exec(url)[2]; }, /** * Creates hidden input element with CSRF_TOKEN in it. * * @return {HTMLInputElement} hidden input element. */ _createHiddenInputElement: function () { var inputElement = document.createElement('input'); inputElement.setAttribute('name', CSRFP.CSRFP_TOKEN); inputElement.setAttribute('class', CSRFP.CSRFP_TOKEN); inputElement.type = 'hidden'; inputElement.value = CSRFP._getAuthKey(); return inputElement; }, /** * Returns absolute url from the input relative components. * * @param {String} basePart - base part of the url. * @param {String} relativePart - relative part of the url. * @return {String} absolute url. */ _createAbsolutePath: function (basePart, relativePart) { var stack = basePart.split("/"); var parts = relativePart.split("/"); stack.pop(); for (var i = 0; i < parts.length; i++) { if (parts[i] === ".") { continue; } if (parts[i] === "..") { stack.pop(); } else { stack.push(parts[i]); } } return stack.join("/"); }, /** * Creates a function wrapper around {@param runnableFunction}, removes * CSRF Token before calling the function and then put it back. * * @param {Function} runnableFunction - function to run. * @param {Object} htmlFormObject - reference form object. * @return modified wrapped function. */ _createCsrfpWrappedFunction: function (runnableFunction, htmlFormObject) { return function (event) { // Remove CSRf token if exists if (typeof htmlFormObject[CSRFP.CSRFP_TOKEN] !== 'undefined') { var target = htmlFormObject[CSRFP.CSRFP_TOKEN]; target.parentNode.removeChild(target); } // Trigger the functions var result = runnableFunction.apply(this, [event]); // Now append the CSRFP-Token back htmlFormObject.appendChild(CSRFP._createHiddenInputElement()); return result; }; }, /** * Initialises the CSRFProtector js script. */ _init: function () { this.CSRFP_TOKEN = document.getElementById( CSRFP_FIELD_TOKEN_NAME).value; try { var csrfFieldElem = document.getElementById(CSRFP_FIELD_URLS); this.checkForUrls = JSON.parse(csrfFieldElem.value); } catch (exception) { console.error(exception); console.error('[ERROR] [CSRF Protector] unable to parse blacklisted' + ` url fields. Exception = ${exception}`); } // Convert the rules received from php library to regex objects. for (var i = 0; i < CSRFP.checkForUrls.length; i++) { this.checkForUrls[i] = this.checkForUrls[i].replace(/\*/g, '(.*)') .replace(/\//g, "\\/"); this.checkForUrls[i] = new RegExp(CSRFP.checkForUrls[i]); } } } //========================================================== // Adding tokens, wrappers on window onload //========================================================== function csrfprotector_init() { // Call the init function CSRFP._init(); // Basic FORM submit event handler to intercept the form request and attach // a CSRFP TOKEN if it's not already available. var basicSubmitInterceptor = function (event) { if (!event.target[CSRFP.CSRFP_TOKEN]) { event.target.appendChild(CSRFP._createHiddenInputElement()); } else { //modify token to latest value event.target[CSRFP.CSRFP_TOKEN].value = CSRFP._getAuthKey(); } }; //================================================================== // Adding csrftoken to request resulting from