"MQWB0100E: The CSRF header 'ibm-mq-rest-csrf-token' was omitted from the request."I thought it would be worth giving an example of why CSRF protection is needed, including an example attack. But first here is a recap on CSRF. owasp.org says that:
Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they’re currently authenticated. CSRF attacks specifically target state-changing requests, not theft of data, since the attacker has no way to see the response to the forged request. With a little help of social engineering (such as sending a link via email or chat), an attacker may trick the users of a web application into executing actions of the attacker’s choosing. If the victim is a normal user, a successful CSRF attack can force the user to perform state changing requests like transferring funds, changing their email address, and so forth. If the victim is an administrative account, CSRF can compromise the entire web application.As the MQ REST API and the MQ Console are web applications they can potentially be subject to CSRF attacks which exploit the fact that they use an HTTP cookie authenticating them to the mqweb server. If a user is logged into either the MQ Console or the REST API in a web-browser, the browser will send the cookie on each request to the REST API/MQ Console, even if that request was generated by a malicious web page that the user has been tricked into clicking on. In the MQ REST API we protect against CSRF attacks by requiring that any caller of state-changing API methods – those that are invoked using any HTTP verb other than GET – provides an HTTP header called ibm-mq-rest-csrf-token. This header doesnâ€™t need a value, the fact that it is specified is enough. The same approach is used by the MQ Console. The following steps describe how to perform a CSRF attack against the MQ REST API. I have assumed that you already have MQ 9.0.5 installed, including the mqweb component, and have copied the basic registry sample (basic_registry.xml) over the contents of mqwebuser.xml. Information on how to do that is described here. I also assume that: the mqweb server is running; you have a local queue manager called QM905 created and running; and the queue manager has a local queue called Q1 defined. IMPORTANT: If you are using the basic_registry sample then the MQ REST API can only be contacted on localhost. Donâ€™t change this otherwise you could potentially allow remote users to attempt the CSRF attack that we are about to try out! Once you have everything in place the next step is to create a new user in the registry called matt_leming, and give him a simple password of mqadmin. To do this add the following line inside the basicRegistry element in mqwebuser.xml, and save your changes:
<user name="matt_leming" password="mqadmin"/>You should have something like: Now matt_leming can log in to both the MQ Console and REST API as a user of the MQWebUser role because these roles have the ALL_AUTHENTICATED_USERS subject added to them. The MQWebUser role is described in more detail here. We are using the MQWebUser role as later on you will be driving the MQ messaging REST API, and that is only accessible to users that are a member of the MQWebUser role. Next we check that we can drive the MQ REST messaging API. You can do this in many ways, but curl is nice and simple. From a command prompt issue:
curl -i -k -X POST https://localhost:9443/ibmmq/rest/v1/messaging/qmgr/QM905/queue/Q1/message -d "Hello world" -H "Content-Type: text/*" -H "ibm-mq-rest-csrf-token: blank" -u matt_leming:mqadminThis command will put a message with a payload of “Hello world” to the Q1 queue on queue manager QM905. You should get the following response, indicating that the message has been put successfully: Note that as POST is a state-changing request it needs the ibm-mq-rest-csrf-token HTTP header to be specified. As I mentioned earlier, the value of the header doesnâ€™t matter, in this case it is the word “blank”. If you donâ€™t specify the header you will get an error message. Next go and get the message that you just put, by issuing the following command:
curl -i -k -X DELETE https://localhost:9443/ibmmq/rest/v1/messaging/qmgr/QM905/queue/Q1/message -H "ibm-mq-rest-csrf-token: blank" -u matt_leming:mqadminYou should get the message back, as follows: Again HTTP DELETE is a state changing method so we specified the ibm-mq-rest-csrf-token HTTP header on the request. Now that we have verified our configuration, letâ€™s move on to trying out the attack! As mentioned at the top of this article, CSRF is all about tricking a user to click on a link on a malicious web-page. We are going to create and use a very simple malicious web-page on your local computer, but in real attacks the web page might be in an enticing blog post, or something similar. To create the malicious web-page copy the text from the following grey box into a file called index.html on your local machine. This simple HTML defines a form containing a button which when clicked sends a message to the MQ REST messaging API, just like we were doing with curl earlier.
<html>Now load index.html into your web-browser of choice. You should see something like: If you click on the button you will get the following error from the MQ REST API. This means that we arenâ€™t authenticated to the MQ REST API. That is to be expected, we havenâ€™t provided any security credentials. So we now have a malicious web page, but for a CSRF attack to work the person who is being attacked must be authenticated with the web application (the MQ REST API in this case) that we want to attack. Actually you donâ€™t even need to be authenticated to the MQ REST API, being authenticated to the MQ Console is enough in our case, so letâ€™s do that. If you open up the MQ Console using the following URL
<title>A simple CSRF attack against the messaging REST API.</title>
<form action="https://localhost:9443/ibmmq/rest/v1/messaging/qmgr/QM905/queue/Q1/message" method="POST" enctype="text/plain">
First name: <input type="text" name="fname" value="Matt"><br>
Last name: <input type="text" name="lname" value="Leming"><br>
<input type="submit" value="Please click this button, and I will make you rich!">
https://localhost:9443/ibmmq/console/it will prompt you for a user id and password. Enter a user id of matt_leming and a password of mqadmin. Once you have done this, and the user id and password have been validated, the mqweb server will send back a cookie indicating that you are authenticated to the server. Each subsequent HTTP request to the mqweb server from your web-browser will flow this cookie. If you reload index.html and click the button you will get a different error message. This time indicating that you havenâ€™t provided the CSRF protection header, but crucially you are now logged into the REST API. At this point the CSRF attack has been prevented. There is no way that the attacker can specify the ibm-mq-rest-csrf-token HTTP header on an HTML form as they donâ€™t support headers, so by default with the MQ REST API there is no way of executing a CSRF attack. However, a CSRF attack is possible if you have switched off CSRF protection. Obviously I wouldnâ€™t normally recommend you do this. To disable CSRF protection in the MQ REST API stick the following line in the mqwebuser.xml:
<variable name="mqRestCsrfValidation" value="false"/>I.e. something like this: Now reload index.html and click the button again. This time you should get a blank screen. This means that you have just sent a message to the queue and executed, or is that experienced, a successful CSRF attack! Just as a recap, this is a CSRF attack because the malicious web-page â€“ index.html â€“ has exploited the fact that the current user (matt_leming) is logged into the MQ Console and therefore the web-browser has a cookie indicating this. When you click on the button in index.html it sends an HTTP request to the MQ REST API which runs in the same server as the MQ Console, and as a result the web-browser sends the cookie, allowing index.html to access the MQ REST API as matt_leming. To prove that a message has been sent letâ€™s take a look at the queue using the REST API: You will see that there is a message with the encoded form data (dname=Mattlname=Leming) as the payload. Letâ€™s re-enable CSRF protection again by removing the
<variable name="mqRestCsrfValidation" value="false"/>line from the mqwebuser.xml file and saving it. If you now reload index.html and click the button one last time you will again get the CSRF error message showing CSRF protection has been re-enabled. Hopefully this blog has shown why the MQ REST API has CSRF protection in place. So please donâ€™t disable it, as it is there to protect you!