summaryrefslogtreecommitdiff
path: root/www/wiki/includes/session/Token.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/includes/session/Token.php')
-rw-r--r--www/wiki/includes/session/Token.php125
1 files changed, 125 insertions, 0 deletions
diff --git a/www/wiki/includes/session/Token.php b/www/wiki/includes/session/Token.php
new file mode 100644
index 00000000..14d239d5
--- /dev/null
+++ b/www/wiki/includes/session/Token.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * MediaWiki session token
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Session
+ */
+
+namespace MediaWiki\Session;
+
+/**
+ * Value object representing a CSRF token
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+class Token {
+ /** CSRF token suffix. Plus and terminal backslash are included to stop
+ * editing from certain broken proxies. */
+ const SUFFIX = '+\\';
+
+ private $secret = '';
+ private $salt = '';
+ private $new = false;
+
+ /**
+ * @param string $secret Token secret
+ * @param string $salt Token salt
+ * @param bool $new Whether the secret was newly-created
+ */
+ public function __construct( $secret, $salt, $new = false ) {
+ $this->secret = $secret;
+ $this->salt = $salt;
+ $this->new = $new;
+ }
+
+ /**
+ * Decode the timestamp from a token string
+ *
+ * Does not validate the token beyond the syntactic checks necessary to
+ * be able to extract the timestamp.
+ *
+ * @param string $token
+ * @return int|null
+ */
+ public static function getTimestamp( $token ) {
+ $suffixLen = strlen( self::SUFFIX );
+ $len = strlen( $token );
+ if ( $len <= 32 + $suffixLen ||
+ substr( $token, -$suffixLen ) !== self::SUFFIX ||
+ strspn( $token, '0123456789abcdef' ) + $suffixLen !== $len
+ ) {
+ return null;
+ }
+
+ return hexdec( substr( $token, 32, -$suffixLen ) );
+ }
+
+ /**
+ * Get the string representation of the token at a timestamp
+ * @param int $timestamp
+ * @return string
+ */
+ protected function toStringAtTimestamp( $timestamp ) {
+ return hash_hmac( 'md5', $timestamp . $this->salt, $this->secret, false ) .
+ dechex( $timestamp ) .
+ self::SUFFIX;
+ }
+
+ /**
+ * Get the string representation of the token
+ * @return string
+ */
+ public function toString() {
+ return $this->toStringAtTimestamp( wfTimestamp() );
+ }
+
+ public function __toString() {
+ return $this->toString();
+ }
+
+ /**
+ * Test if the token-string matches this token
+ * @param string $userToken
+ * @param int|null $maxAge Return false if $userToken is older than this many seconds
+ * @return bool
+ */
+ public function match( $userToken, $maxAge = null ) {
+ $timestamp = self::getTimestamp( $userToken );
+ if ( $timestamp === null ) {
+ return false;
+ }
+ if ( $maxAge !== null && $timestamp < wfTimestamp() - $maxAge ) {
+ // Expired token
+ return false;
+ }
+
+ $sessionToken = $this->toStringAtTimestamp( $timestamp );
+ return hash_equals( $sessionToken, $userToken );
+ }
+
+ /**
+ * Indicate whether this token was just created
+ * @return bool
+ */
+ public function wasNew() {
+ return $this->new;
+ }
+
+}