[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 /** 2 * @provides javelin-behavior-refresh-csrf 3 * @requires javelin-request 4 * javelin-behavior 5 * javelin-dom 6 * javelin-router 7 * javelin-util 8 * phabricator-busy 9 */ 10 11 /** 12 * We cycle CSRF tokens every hour but accept the last 6, which means that if 13 * you leave a page open for more than 6 hours before submitting it you may hit 14 * CSRF protection. This is a super confusing workflow which potentially 15 * discards data, and we can't recover from it by re-issuing a CSRF token 16 * since that would leave us vulnerable to CSRF attacks. 17 * 18 * Our options basically boil down to: 19 * 20 * - Increase the CSRF window to something like 24 hours. This makes the CSRF 21 * implementation vaguely less secure and only mitigates the problem. 22 * - Code all forms to understand GET, POST and POST-with-invalid-CSRF. This 23 * is a huge undertaking and difficult to test properly since it is hard 24 * to remember to test the third phantom state. 25 * - Use JS to refresh the CSRF token. 26 * 27 * Since (1) mitigates rather than solving and (2) is a huge mess, this 28 * behavior implements (3) and refreshes all the CSRF tokens on the page every 29 * 55 minutes. This allows forms to remain valid indefinitely. 30 */ 31 JX.behavior('refresh-csrf', function(config) { 32 33 var current_token = config.current; 34 35 function refresh_csrf() { 36 new JX.Request('/login/refresh/', function(r) { 37 current_token = r.token; 38 var inputs = JX.DOM.scry(document.body, 'input'); 39 for (var ii = 0; ii < inputs.length; ii++) { 40 if (inputs[ii].name == config.tokenName) { 41 inputs[ii].value = r.token; 42 } 43 } 44 }) 45 .send(); 46 } 47 48 // Refresh the CSRF tokens every 55 minutes. 49 setInterval(refresh_csrf, 1000 * 60 * 55); 50 51 // Additionally, add the CSRF token as an HTTP header to every AJAX request. 52 JX.Request.listen('open', function(r) { 53 r.getTransport().setRequestHeader(config.header, current_token); 54 }); 55 56 // Does this type of routable show the "Busy" spinner? 57 var is_busy_type = function(type) { 58 switch (type) { 59 case 'workflow': 60 return true; 61 } 62 63 return false; 64 }; 65 66 // Does this type of routable show the "Loading" bar? 67 var is_bar_type = function(type) { 68 switch (type) { 69 case 'content': 70 return true; 71 } 72 73 return false; 74 }; 75 76 77 var queue = {}; 78 var count = 0; 79 var node; 80 81 // Redraw the loading bar. 82 var redraw_bar = function() { 83 // If all requests have completed, hide the bar after a moment. 84 if (!count) { 85 if (node) { 86 node.firstChild.style.width = '100%'; 87 setTimeout(JX.bind(null, JX.DOM.remove, node), 500); 88 } 89 node = null; 90 queue = {}; 91 return; 92 } 93 94 // If we don't have the bar yet, draw it. 95 if (!node) { 96 node = JX.$N('div', {className: 'routing-bar'}); 97 document.body.appendChild(node); 98 node.appendChild(JX.$N('div', {className: 'routing-progress'})); 99 } 100 101 // Update the bar progress. 102 var done = 0; 103 var total = 0; 104 for (var k in queue) { 105 total++; 106 if (queue[k]) { 107 done++; 108 } 109 } 110 111 node.firstChild.style.width = (100 * (done / total)) + '%'; 112 }; 113 114 115 // Listen for queued requests. 116 JX.Router.listen('queue', function(r) { 117 var type = r.getType(); 118 119 if (is_bar_type(type)) { 120 queue[r.getID()] = false; 121 count++; 122 redraw_bar(); 123 } 124 125 if (is_busy_type(r.getType())) { 126 JX.Busy.start(); 127 } 128 }); 129 130 131 // Listen for completed requests. 132 JX.Router.listen('done', function(r) { 133 var type = r.getType(); 134 135 if (is_bar_type(type)) { 136 queue[r.getID()] = true; 137 count--; 138 redraw_bar(); 139 } 140 141 if (is_busy_type(r.getType())) { 142 JX.Busy.done(); 143 } 144 }); 145 146 147 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |