MediaWikiChat 's API modules are currently marked as POST-only, but they make no use of an anti-CSRF token, as is the standard security practise. This, naturally, leaves them open to CSRF.
Proposed and lightly tested patch (please excuse the line numbers not matching; this is based on top of a patch for T189417 that is yet to be merged, and I also have a lot of other, unrelated, uncommitted changes locally -- but you get the idea anyway from this patch):
diff --git a/MediaWikiChat.js b/MediaWikiChat.js index febc2be..5570523 100644 --- a/MediaWikiChat.js +++ b/MediaWikiChat.js @@ -560,10 +560,10 @@ var MediaWikiChat = { $( '#mwchat-users #' + userE + ' .mwchat-useritem-kicklink' ).click( function() { var parent = $( this ).parent().parent(); - $.ajax( { - type: 'POST', - url: mw.config.get( 'wgScriptPath' ) + '/api.php', - data: { 'action': 'chatkick', 'id': parent.attr( 'data-id' ), 'format': 'json' } + ( new mw.Api() ).postWithToken( 'csrf', { + 'action': 'chatkick', + 'id': parent.attr( 'data-id' ), + 'format': 'json' } ).done( function() { MediaWikiChat.getNew(); } ); @@ -603,15 +603,11 @@ var MediaWikiChat = { var toid = $( this ).parents( '.mwchat-useritem' ).attr( 'data-id' ); if ( e.which == 13 ) { - $.ajax( { - type: 'POST', - url: mw.config.get( 'wgScriptPath' ) + '/api.php', - data: { - 'action': 'chatsendpm', - 'message': $( this )[0].value, - 'id': toid, - 'format': 'json' - } + ( new mw.Api() ).postWithToken( 'csrf', { + 'action': 'chatsendpm', + 'message': $( this )[0].value, + 'id': toid, + 'format': 'json' } ).done( function() { MediaWikiChat.getNew(); MediaWikiChat.restartInterval(); @@ -763,14 +759,10 @@ $( function() { parseInt( $( '#mwchat-loading' ).attr( 'data-queue' ) ) + 1 ) .animate( { opacity: $( '#mwchat-loading' ).attr( 'data-queue' ) } ); - $.ajax( { - type: 'POST', - url: mw.config.get( 'wgScriptPath' ) + '/api.php', - data: { - 'action': 'chatsend', - 'message': message, - 'format': 'json' - } + ( new mw.Api() ).postWithToken( 'csrf', { + 'action': 'chatsend', + 'message': message, + 'format': 'json' } ).done( function( msg ) { MediaWikiChat.getNewReply( msg ); $( '#mwchat-loading' ).attr( diff --git a/extension.json b/extension.json index 543b1ca..8a51fa6 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "MediaWikiChat", - "version": "2.23.0", + "version": "2.24.0", "author": [ "Adam Carter/UltrasonicNXT" ], @@ -99,7 +100,12 @@ "chat-idle-minutes", "chat-idle-hours", "chat-idle-more", "chat-today", "chat-message-from", "chat-private-message-from", "chat-mentioned-by" ], - "dependencies": [ "mediawiki.jqueryMsg", "mediawiki.user", "mediawiki.util" ] + "dependencies": [ + "mediawiki.api", + "mediawiki.jqueryMsg", + "mediawiki.user", + "mediawiki.util" + ] }, "ext.mediawikichat.site": { "class": "MediaWiki\\ResourceLoader\\WikiModule", diff --git a/includes/api/ChatKickAPI.php b/includes/api/ChatKickAPI.php index 1c3e258..2ca15d3 100644 --- a/includes/api/ChatKickAPI.php +++ b/includes/api/ChatKickAPI.php @@ -58,6 +58,16 @@ class ChatKickAPI extends ApiBase { return true; } + /** @inheritDoc */ + public function needsToken() { + return 'csrf'; + } + + /** @inheritDoc */ + public function isWriteMode() { + return true; + } + /** @inheritDoc */ public function getAllowedParams() { return [ @@ -74,9 +84,4 @@ class ChatKickAPI extends ApiBase { 'action=chatkick&id=1' => 'apihelp-chatkick-example-1' ]; } - - /** @inheritDoc */ - public function mustBePosted() { - return true; - } } diff --git a/includes/api/ChatSendAPI.php b/includes/api/ChatSendAPI.php index 61f712a..3c2219a 100644 --- a/includes/api/ChatSendAPI.php +++ b/includes/api/ChatSendAPI.php @@ -78,6 +80,15 @@ class ChatSendAPI extends ApiBase { } } + /** @inheritDoc */ + public function needsToken() { + return 'csrf'; + } + + /** @inheritDoc */ + public function isWriteMode() { + return true; + } /** @inheritDoc */ public function getAllowedParams() { return [ @@ -94,9 +105,4 @@ class ChatSendAPI extends ApiBase { 'action=chatsend&message=Hello%20World!' => 'apihelp-chatsend-example-1' ]; } - - /** @inheritDoc */ - public function mustBePosted() { - return true; - } } diff --git a/includes/api/ChatSendPMAPI.php b/includes/api/ChatSendPMAPI.php index 647fcd7..0c2beab 100644 --- a/includes/api/ChatSendPMAPI.php +++ b/includes/api/ChatSendPMAPI.php @@ -76,6 +76,16 @@ class ChatSendPMAPI extends ApiBase { } } + /** @inheritDoc */ + public function needsToken() { + return 'csrf'; + } + + /** @inheritDoc */ + public function isWriteMode() { + return true; + } + /** @inheritDoc */ public function getAllowedParams() { return [ @@ -97,9 +107,4 @@ class ChatSendPMAPI extends ApiBase { => 'apihelp-chatsendpm-example-1' ]; } - - /** @inheritDoc */ - public function mustBePosted() { - return true; - } }