Page MenuHomePhabricator

0001-SECURITY-API-Use-constant-time-comparison-for-watchl.patch

Authored By
demon
Aug 10 2015, 7:37 PM
Size
3 KB
Referenced Files
None
Subscribers
None

0001-SECURITY-API-Use-constant-time-comparison-for-watchl.patch

From ef942c1cfc2b75cdaca47d96ce3bd53e3fb3d9da Mon Sep 17 00:00:00 2001
From: Chad Horohoe <chadh@wikimedia.org>
Date: Mon, 10 Aug 2015 12:33:18 -0700
Subject: [PATCH] SECURITY: API: Use constant-time comparison for watchlist
token
Avoids a theoretical timing attack.
Includes backport of hash_equals() compat function from Iece006e
Bug: T94116
Change-Id: Ia4a2b13bd5d3cd256c6b2deada224148dc2888a6
---
includes/GlobalFunctions.php | 47 ++++++++++++++++++++++++++++++++++++++++++++
includes/api/ApiBase.php | 2 +-
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index c292892..8941b97 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -104,6 +104,53 @@ if ( !function_exists( 'gzdecode' ) ) {
return gzinflate( substr( $data, 10, -8 ) );
}
}
+
+// hash_equals function only exists in PHP >= 5.6.0
+if ( !function_exists( 'hash_equals' ) ) {
+ /**
+ * Check whether a user-provided string is equal to a fixed-length secret without
+ * revealing bytes of the secret through timing differences.
+ *
+ * This timing guarantee -- that a partial match takes the same time as a complete
+ * mismatch -- is why this function is used in some security-sensitive parts of the code.
+ * For example, it shouldn't be possible to guess an HMAC signature one byte at a time.
+ *
+ * Longer explanation: http://www.emerose.com/timing-attacks-explained
+ *
+ * @codeCoverageIgnore
+ * @param string $known_string Fixed-length secret to compare against
+ * @param string $user_string User-provided string
+ * @return bool True if the strings are the same, false otherwise
+ */
+ function hash_equals( $known_string, $user_string ) {
+ // Strict type checking as in PHP's native implementation
+ if ( !is_string( $known_string ) ) {
+ trigger_error( 'hash_equals(): Expected known_string to be a string, ' .
+ gettype( $known_string ) . ' given', E_USER_WARNING );
+
+ return false;
+ }
+
+ if ( !is_string( $user_string ) ) {
+ trigger_error( 'hash_equals(): Expected user_string to be a string, ' .
+ gettype( $user_string ) . ' given', E_USER_WARNING );
+
+ return false;
+ }
+
+ // Note that we do one thing PHP doesn't: try to avoid leaking information about
+ // relative lengths of $known_string and $user_string, and of multiple $known_strings.
+ // However, lengths may still inevitably leak through, for example, CPU cache misses.
+ $known_string_len = strlen( $known_string );
+ $user_string_len = strlen( $user_string );
+ $result = $known_string_len ^ $user_string_len;
+ for ( $i = 0; $i < $user_string_len; $i++ ) {
+ $result |= ord( $known_string[$i % $known_string_len] ) ^ ord( $user_string[$i] );
+ }
+
+ return ( $result === 0 );
+ }
+}
/// @endcond
/**
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index 7b91952..8aec787 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -2079,7 +2079,7 @@ abstract class ApiBase extends ContextSource {
$this->dieUsage( 'Specified user does not exist', 'bad_wlowner' );
}
$token = $user->getOption( 'watchlisttoken' );
- if ( $token == '' || $token != $params['token'] ) {
+ if ( $token == '' || !hash_equals( $token, $params['token'] ) ) {
$this->dieUsage(
'Incorrect watchlist token provided -- please set a correct token in Special:Preferences',
'bad_wltoken'
--
2.4.6

File Metadata

Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1225162
Default Alt Text
0001-SECURITY-API-Use-constant-time-comparison-for-watchl.patch (3 KB)

Event Timeline