@@ -104,16 +104,39 @@ protected function loadOAuthAccountData() {
104104 $ future ->addHeader ('Authorization ' , $ token_header );
105105 list ($ body ) = $ future ->resolvex ();
106106 try {
107- $ data = phutil_json_decode ($ body );
107+ $ data = $ this -> decodeJWT ($ body );
108108 return $ data ['result ' ];
109- } catch (PhutilJSONParserException $ ex ) {
109+ } catch (Exception $ ex ) {
110110 throw new Exception (
111111 pht (
112112 'Expected valid JSON response from MediaWiki request ' ),
113113 $ ex );
114114 }
115115 }
116116
117+ private function decodeJWT ($ jwt ) {
118+ list ($ headb64 , $ bodyb64 , $ sigb64 ) = explode ('. ' , $ jwt );
119+
120+ $ header = json_decode ($ this ->urlsafeB64Decode ($ headb64 ));
121+ $ body = json_decode ($ this ->urlsafeB64Decode ($ bodyb64 ));
122+ $ sig = $ this ->urlsafeB64Decode ($ sigb64 );
123+
124+ $ expect_sig = hash_hmac (
125+ 'sha256 ' ,
126+ "$ headb64. $ bodyb64 " ,
127+ $ this ->getConsumerSecret ()->openEnvelope (),
128+ true );
129+
130+ // MediaWiki will only use sha256 hmac (HS256) for now.
131+ // This checks that an attacker doesn't return invalid JWT signature type.
132+ if ($ header ->alg !== 'HS256 ' ||
133+ !$ this ->compareHash ($ sig , $ expect_sig )) {
134+ throw new Exception ('Invalid JWT signature. ' );
135+ }
136+
137+ return $ body ;
138+ }
139+
117140 protected function willProcessTokenRequestResponse ($ body ) {
118141 if (substr_count ($ body , 'Error: ' ) > 0 ) {
119142 throw new Exception (
0 commit comments