1/** 2 * ================================================================= 3 * Javascript code for OWASP CSRF Protector 4 * Task it does: Fetch csrftoken from cookie, and attach it to every 5 * POST request 6 * Allowed GET url 7 * -- XHR 8 * -- Static Forms 9 * -- URLS (GET only) 10 * -- dynamic forms 11 * ================================================================= 12 */ 13 14var CSRFP_FIELD_TOKEN_NAME = 'csrfp_hidden_data_token'; 15var CSRFP_FIELD_URLS = 'csrfp_hidden_data_urls'; 16 17var CSRFP = { 18 CSRFP_TOKEN: 'csrfp_token', 19 /** 20 * Array of patterns of url, for which csrftoken need to be added 21 * In case of GET request also, provided from server 22 * 23 * @var string array 24 */ 25 checkForUrls: [], 26 /** 27 * Function to check if a certain url is allowed to perform the request 28 * With or without csrf token 29 * 30 * @param: string, url 31 * 32 * @return: boolean, true if csrftoken is not needed 33 * false if csrftoken is needed 34 */ 35 _isValidGetRequest: function(url) { 36 for (var i = 0; i < CSRFP.checkForUrls.length; i++) { 37 var match = CSRFP.checkForUrls[i].exec(CSRFP._getDomain(url)); 38 if (match !== null && match.length > 0) { 39 return false; 40 } 41 } 42 return true; 43 }, 44 /** 45 * function to get Auth key from cookie Andreturn it to requesting function 46 * 47 * @param: void 48 * 49 * @return: string, csrftoken retrieved from cookie 50 */ 51 _getAuthKey: function() { 52 var re = new RegExp(CSRFP.CSRFP_TOKEN +"=([^;]+)(;|$)"); 53 var RegExpArray = re.exec(document.cookie); 54
55 if (RegExpArray === null) { 56 return false; 57 } 58 return RegExpArray[1]; 59 }, 60 /** 61 * Function to get domain of any url 62 * 63 * @param: string, url 64 * 65 * @return: string, domain of url 66 */ 67 _getDomain: function(url) { 68 if (url.indexOf("http://") !== 0 69 && url.indexOf("https://") !== 0) 70 return document.domain; 71 return /http(s)?:\/\/([^\/]+)/.exec(url)[2]; 72 }, 73 /** 74 * Function to create and return a hidden input element 75 * For stroing the CSRFP_TOKEN 76 * 77 * @param void 78 * 79 * @return input element 80 */ 81 _getInputElt: function() { 82 var hiddenObj = document.createElement("input"); 83 hiddenObj.setAttribute('name', CSRFP.CSRFP_TOKEN); 84 hiddenObj.setAttribute('class', CSRFP.CSRFP_TOKEN); 85 hiddenObj.type = 'hidden'; 86 hiddenObj.value = CSRFP._getAuthKey(); 87 return hiddenObj; 88 }, 89 /** 90 * Returns absolute path for relative path 91 * 92 * @param base, base url 93 * @param relative, relative url 94 * 95 * @return absolute path (string) 96 */ 97 _getAbsolutePath: function(base, relative) { 98 var stack = base.split("/"); 99 var parts = relative.split("/"); 100 // remove current file name (or empty string) 101 // (omit if "base" is the current folder without trailing slash) 102 stack.pop(); 103 104 for (var i = 0; i < parts.length; i++) { 105 if (parts[i] == ".") 106 continue; 107 if (parts[i] == "..") 108 stack.pop(); 109 else 110 stack.push(parts[i]); 111 } 112 return stack.join("/"); 113 }, 114 /** 115 * Remove jcsrfp-token run fun and then put them back 116 * 117 * @param function 118 * @param reference form obj 119 * 120 * @retrun function 121 */ 122 _csrfpWrap: function(fun, obj) { 123 return function(event) { 124 // Remove CSRf token if exists 125 if (typeof obj[CSRFP.CSRFP_TOKEN] !== 'undefined') { 126 var target = obj[CSRFP.CSRFP_TOKEN]; 127 target.parentNode.removeChild(target); 128 } 129 130 // Trigger the functions 131 var result = fun.apply(this, [event]); 132 133 // Now append the csrfp_token back 134 obj.appendChild(CSRFP._getInputElt()); 135 136 return result; 137 }; 138 }, 139 /** 140 * Initialises the CSRFProtector js script 141 * 142 * @param void 143 * 144 * @return void 145 */ 146 _init: function() { 147 CSRFP.CSRFP_TOKEN = document.getElementById(CSRFP_FIELD_TOKEN_NAME).value; 148 try { 149 CSRFP.checkForUrls = JSON.parse(document.getElementById(CSRFP_FIELD_URLS).value); 150 } catch (err) { 151 console.error(err); 152 console.error('[ERROR] [CSRF Protector] unable to parse blacklisted url fields.'); 153 } 154 155 //convert these rules received from php lib to regex objects 156 for (var i = 0; i < CSRFP.checkForUrls.length; i++) { 157 CSRFP.checkForUrls[i] = CSRFP.checkForUrls[i].replace(/\*/g, '(.*)') 158 .replace(/\//g, "\\/"); 159 CSRFP.checkForUrls[i] = new RegExp(CSRFP.checkForUrls[i]); 160 } 161 162 } 163 164}; 165 166//========================================================== 167// Adding tokens, wrappers on window onload 168//========================================================== 169 170function csrfprotector_init() { 171 172 // Call the init funcion 173 CSRFP._init(); 174 175 // definition of basic FORM submit event handler to intercept the form request 176 // and attach a CSRFP TOKEN if it's not already available 177 var BasicSubmitInterceptor = function(event) { 178 if (typeof event.target[CSRFP.CSRFP_TOKEN] === 'undefined') { 179 event.target.appendChild(CSRFP._getInputElt()); 180 } else { 181 //modify token to latest value 182 event.target[CSRFP.CSRFP_TOKEN].value = CSRFP._getAuthKey(); 183 } 184 } 185 186 //================================================================== 187 // Adding csrftoken to request resulting from <form> submissions 188 // Add for each POST, while for mentioned GET request 189 // TODO - check for method 190 //================================================================== 191 // run time binding 192 193 if (typeof jQuery != 'undefined') { 194 jQuery("body").on('submit',function(event) { 195 if (event.target.tagName.toLowerCase() === 'form') { 196 BasicSubmitInterceptor(event); 197 }; 198 }); 199 } 200 201 document.querySelector('body').addEventListener('submit', function(event) { 202 if (event.target.tagName.toLowerCase() === 'form') { 203 BasicSubmitInterceptor(event); 204 }; 205 }); 206 207 // intial binding 208 // for(var i = 0; i < document.forms.length; i++) { 209 // document.forms[i].addEventListener("submit", BasicSubmitInterceptor); 210 // } 211 212 //================================================================== 213 // Adding csrftoken to request resulting from direct form.submit() call 214 // Add for each POST, while for mentioned GET request 215 // TODO - check for form method 216 //================================================================== 217 HTMLFormElement.prototype.submit_ = HTMLFormElement.prototype.submit; 218 HTMLFormElement.prototype.submit = function() { 219 // check if the FORM already contains the token element 220 if (!this.getElementsByClassName(CSRFP.CSRFP_TOKEN).length) 221 this.appendChild(CSRFP._getInputElt()); 222 this.submit_(); 223 } 224 225 226 /** 227 * Add wrapper for HTMLFormElements addEventListener so that any further 228 * addEventListens won't have trouble with CSRF token 229 * todo - check for method 230 */ 231 HTMLFormElement.prototype.addEventListener_ = HTMLFormElement.prototype.addEventListener; 232 HTMLFormElement.prototype.addEventListener = function(eventType, fun, bubble) { 233 if (eventType === 'submit') { 234 var wrapped = CSRFP._csrfpWrap(fun, this); 235 this.addEventListener_(eventType, wrapped, bubble); 236 } else { 237 this.addEventListener_(eventType, fun, bubble); 238 } 239 } 240 241 /** 242 * Add wrapper for IE's attachEvent 243 * todo - check for method 244 * todo - typeof is now obselete for IE 11, use some other method. 245 */ 246 if (typeof HTMLFormElement.prototype.attachEvent !== 'undefined') { 247 HTMLFormElement.prototype.attachEvent_ = HTMLFormElement.prototype.attachEvent; 248 HTMLFormElement.prototype.attachEvent = function(eventType, fun) { 249 if (eventType === 'onsubmit') { 250 var wrapped = CSRFP._csrfpWrap(fun, this); 251 this.attachEvent_(eventType, wrapped); 252 } else { 253 this.attachEvent_(eventType, fun); 254 } 255 } 256 } 257 258 259 //================================================================== 260 // Wrapper for XMLHttpRequest & ActiveXObject (for IE 6 & below) 261 // Set X-No-CSRF to true before sending if request method is 262 //================================================================== 263 264 /** 265 * Wrapper to XHR open method 266 * Add a property method to XMLHttpRequst class 267 * @param: all parameters to XHR open method 268 * @return: object returned by default, XHR open method 269 */ 270 function new_open(method, url, async, username, password) { 271 this.method = method; 272 this.url = url; 273 var isAbsolute = (url.indexOf("./") === -1) ? true : false;
274 if (!isAbsolute) { 275 var base = location.protocol +'//' +location.host 276 + location.pathname; 277 url = CSRFP._getAbsolutePath(base, url); 278 } 279 /*if (method.toLowerCase() === 'get' 280 && !CSRFP._isValidGetRequest(url)) { 281 //modify the url 282 if (url.indexOf('?') === -1) { 283 url += "?" +CSRFP.CSRFP_TOKEN +"=" +CSRFP._getAuthKey(); 284 } else { 285 url += "&" +CSRFP.CSRFP_TOKEN +"=" +CSRFP._getAuthKey(); 286 } 287 }*/ 288 return this.old_open(method, url, async, username, password); 289 } 290 291 /** 292 * Wrapper to XHR send method 293 * Add query paramter to XHR object 294 * 295 * @param: all parameters to XHR send method 296 * 297 * @return: object returned by default, XHR send method 298 */ 299 function new_send(data) { 300 try { 301 if (this.method.toLowerCase() === 'post' && !CSRFP._isValidGetRequest(this.url)) { 302 this.setRequestHeader(CSRFP.CSRFP_TOKEN, CSRFP._getAuthKey()); 303 } 304 return this.old_send(data); 305 } catch (e){ 306 console.log(e); 307 } 308 } 309 310 if (window.XMLHttpRequest) { 311 // Wrapping 312 XMLHttpRequest.prototype.old_send = XMLHttpRequest.prototype.send; 313 XMLHttpRequest.prototype.old_open = XMLHttpRequest.prototype.open; 314 XMLHttpRequest.prototype.open = new_open; 315 XMLHttpRequest.prototype.send = new_send; 316 } 317 if (typeof ActiveXObject !== 'undefined') { 318 ActiveXObject.prototype.old_send = ActiveXObject.prototype.send; 319 ActiveXObject.prototype.old_open = ActiveXObject.prototype.open; 320 ActiveXObject.prototype.open = new_open; 321 ActiveXObject.prototype.send = new_send; 322 } 323 //================================================================== 324 // Rewrite existing urls ( Attach CSRF token ) 325 // Rules: 326 // Rewrite those urls which matches the regex sent by Server 327 // Ignore cross origin urls & internal links (one with hashtags) 328 // Append the token to those url already containig GET query parameter(s) 329 // Add the token to those which does not contain GET query parameter(s) 330 //================================================================== 331} 332 333window.addEventListener("DOMContentLoaded", function() { 334 csrfprotector_init(); 335}, false);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.