Changeset 501 for trunk/lib/App.inc.php
- Timestamp:
- Nov 16, 2014 11:07:01 AM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/lib/App.inc.php
r500 r501 80 80 'ssl_enabled' => false, 81 81 82 // Use CSRF tokens. 82 // Use CSRF tokens. See notes in the getCSRFToken() method. 83 83 'csrf_token_enabled' => true, 84 // Form tokens will expire after this duration, in seconds. 85 'csrf_token_timeout' => 259200, // 259200 seconds = 3 days. 84 86 'csrf_token_name' => 'csrf_token', 85 'csrf_token_timeout' => 86400, // In seconds. This causes form tokens to be unusable after this duration. This might only cause problems when opening forms in multiple tabs left open beyond the timeout duration. But usually their session will timeout first, and they'll receive new tokens when they load the form again..86 87 87 88 // HMAC signing method … … 1018 1019 // This token can be validated upon form submission with $app->verifyCSRFToken() or $app->requireValidCSRFToken() 1019 1020 if ($this->getParam('csrf_token_enabled') && $include_csrf_token) { 1020 printf('<input type="hidden" name="%s" value="%s" />', $this->getParam('csrf_token_name'), $this-> recycleCSRFToken());1021 printf('<input type="hidden" name="%s" value="%s" />', $this->getParam('csrf_token_name'), $this->getCSRFToken()); 1021 1022 } 1022 1023 } … … 1041 1042 1042 1043 /* 1043 * Generate a csrf_token, saving it to the session and returning its value. 1044 * Generate a csrf_token if it doesn't exist or is expired, save it to the session and return its value. 1045 * Otherwise just return the current token. 1046 * Details on the synchronizer token pattern: 1044 1047 * https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern 1045 * @access public1046 * @return string The new csrf_token.1047 * @author Quinn Comendant <quinn@strangecode.com>1048 * @version 1.01049 * @since 15 Nov 2014 17:53:511050 */1051 public function generateCSRFToken()1052 {1053 return $_SESSION['_app'][$this->_ns]['csrf_tokens'][] = addSignature(time(), null, 64);1054 }1055 1056 /*1057 * Update the csrf_token to a new value if it hasn't been set yet or has expired.1058 * Save the previous csrf_token in the session to ensure continuity of currently open sessions.1059 1048 * 1060 1049 * @access public 1061 * @return string The current csrf_token1050 * @return string The new or current csrf_token 1062 1051 * @author Quinn Comendant <quinn@strangecode.com> 1063 1052 * @version 1.0 1064 1053 * @since 15 Nov 2014 17:57:17 1065 1054 */ 1066 public function recycleCSRFToken() 1067 { 1068 if (!isset($_SESSION['_app'][$this->_ns]['csrf_tokens'])) { 1069 // No token yet; generate one and return it. 1070 $_SESSION['_app'][$this->_ns]['csrf_tokens'] = array(); 1071 $return = $this->generateCSRFToken(); 1072 } 1073 if (removeSignature(end($_SESSION['_app'][$this->_ns]['csrf_tokens'])) + $this->getParam('csrf_token_timeout') < time()) { 1074 // Newest token is expired; prune array of tokens and generate new token. 1075 // We'll save the 10-most-recent tokens. This allows the user to submit up to 5 forms saved in previously opened tabs with expired tokens (loading a form will prune one token, and submitting a form will prune one token, thus 10 = 5). 1076 $_SESSION['_app'][$this->_ns]['csrf_tokens'] = array_slice($_SESSION['_app'][$this->_ns]['csrf_tokens'], -10, 10); 1077 $return = $this->generateCSRFToken(); 1055 public function getCSRFToken() 1056 { 1057 if (!isset($_SESSION['_app'][$this->_ns]['csrf_token']) || (removeSignature($_SESSION['_app'][$this->_ns]['csrf_token']) + $this->getParam('csrf_token_timeout') < time())) { 1058 // No token, or token is expired; generate one and return it. 1059 return $_SESSION['_app'][$this->_ns]['csrf_token'] = addSignature(time(), null, 64); 1078 1060 } 1079 1061 // Current token is not expired; return it. 1080 $return = end($_SESSION['_app'][$this->_ns]['csrf_tokens']); 1081 $app =& App::getInstance(); 1082 return $return; 1062 return $_SESSION['_app'][$this->_ns]['csrf_token']; 1083 1063 } 1084 1064 … … 1093 1073 * @since 15 Nov 2014 18:06:55 1094 1074 */ 1095 public function verifyCSRFToken($csrf_token) 1096 { 1097 $app =& App::getInstance(); 1075 public function verifyCSRFToken($user_submitted_csrf_token) 1076 { 1098 1077 1099 1078 if (!$this->getParam('csrf_token_enabled')) { 1100 $ app->logMsg(sprintf('%s method called, but csrf_token_enabled=false', __FUNCTION__), LOG_ERR, __FILE__, __LINE__);1101 return false;1102 } 1103 if ('' == trim($ csrf_token)) {1104 $ app->logMsg(sprintf('Empty string failed CSRF verification.', null), LOG_NOTICE, __FILE__, __LINE__);1105 return false; 1106 } 1107 if (!verifySignature($ csrf_token, null, 64)) {1108 $ app->logMsg(sprintf('Input failed CSRF verification (invalid signature in %s).', $csrf_token), LOG_WARNING, __FILE__, __LINE__);1109 return false; 1110 } 1111 $ this->recycleCSRFToken();1112 if ( !in_array($csrf_token, $_SESSION['_app'][$this->_ns]['csrf_tokens'])) {1113 $ app->logMsg(sprintf('Input failed CSRF verification (%s not in %s).', $csrf_token, getDump($_SESSION['_app'][$this->_ns]['csrf_tokens'])), LOG_WARNING, __FILE__, __LINE__);1114 return false; 1115 } 1116 // $app->logMsg(sprintf('Verified token %s is in %s', $csrf_token, getDump($_SESSION['_app'][$this->_ns]['csrf_tokens'])), LOG_DEBUG, __FILE__, __LINE__);1079 $this->logMsg(sprintf('%s method called, but csrf_token_enabled=false', __FUNCTION__), LOG_ERR, __FILE__, __LINE__); 1080 return true; 1081 } 1082 if ('' == trim($user_submitted_csrf_token)) { 1083 $this->logMsg(sprintf('Empty string failed CSRF verification.', null), LOG_NOTICE, __FILE__, __LINE__); 1084 return false; 1085 } 1086 if (!verifySignature($user_submitted_csrf_token, null, 64)) { 1087 $this->logMsg(sprintf('Input failed CSRF verification (invalid signature in %s).', $user_submitted_csrf_token), LOG_WARNING, __FILE__, __LINE__); 1088 return false; 1089 } 1090 $csrf_token = $this->getCSRFToken(); 1091 if ($user_submitted_csrf_token != $csrf_token) { 1092 $this->logMsg(sprintf('Input failed CSRF verification (%s not in %s).', $user_submitted_csrf_token, $csrf_token), LOG_WARNING, __FILE__, __LINE__); 1093 return false; 1094 } 1095 $this->logMsg(sprintf('Verified CSRF token %s is in %s', $user_submitted_csrf_token, $csrf_token), LOG_DEBUG, __FILE__, __LINE__); 1117 1096 return true; 1118 1097 } … … 1123 1102 * 1124 1103 * @access public 1125 * @param string $ csrf_token Thetoken to compare with the session token.1104 * @param string $user_submitted_csrf_token The user-submitted token to compare with the session token. 1126 1105 * @param string $message Optional message to display to the user (otherwise default message will display). Set to an empty string to display no message. 1127 1106 * @param int $type The type of message: MSG_NOTICE, … … 1134 1113 * @since 15 Nov 2014 18:10:17 1135 1114 */ 1136 public function requireValidCSRFToken($csrf_token, $message=null, $type=MSG_NOTICE, $file=null, $line=null) 1137 { 1138 $app =& App::getInstance(); 1139 1140 if (!$this->verifyCSRFToken($csrf_token)) { 1141 $message = isset($message) ? $message : _("Something went wrong; please try again."); 1142 $app->raiseMsg($message, $type, $file, $line); 1143 $app->dieBoomerangURL(); 1115 public function requireValidCSRFToken($message=null, $type=MSG_NOTICE, $file=null, $line=null) 1116 { 1117 if (!$this->verifyCSRFToken(getFormData($this->getParam('csrf_token_name')))) { 1118 $message = isset($message) ? $message : _("Sorry, the form token expired. Please try again."); 1119 $this->raiseMsg($message, $type, $file, $line); 1120 $this->dieBoomerangURL(); 1144 1121 } 1145 1122 }
Note: See TracChangeset
for help on using the changeset viewer.