Since release 1.1, CMS is been fully integrated with HDIV. HDIV (HTTP Data Integrity Validator) is an open source security framework plugins for both struts 1.x and struts 2.x. Here is a description from HDIV's official website:
We can briefly define HDIV as a Struts Security Extension (Struts 1.x & Struts 2). HDIV extends Struts behaviour by adding security functionalities, maintaining the API and Struts specification. This implies that we can use HDIV in applications developed in Struts in a transparent way to the programmer and without adding any complexity to the application development. It is possible to use HDIV in applications that don't use Struts, but in this case it is necessary to modify the application (JSP pages).
When we think about software security, it's certainly a huge topic that covers many areas. They include things such as system reliability, cryptography, fault tolerance, access controls, data integrity, confidentiality, and so on. HDIV is not intended to be a silver bullet to solve all of these security concerns.
On the other hand, for web applications, especially data driven webapp in particular, there is a set of common vulnerabilities which malicious users would exploit to attack these systems. HDIV is targeted to defend some of these common attacks. It does that by injecting data validations codes in the struts tags such that once configured, most common webapp securities concerns will be eliminated.
Supposedly, to integrate HDIV into a struts webapp, all you need to do is to simply add a few third party libraries, then setup a couple of configurations files, and everything should work magically (i.e. the webapp will be immune to sql injection attacks, cross site scripting, parameters tampering, etc once you have configured it to use HDIV). However, for CMS, integrating with HDIV turned out to be not so much of a simple configuration change task. I need to deploy quite a bit of black magic in order to make it go. So I jotted down some notes about the experience and hopefully this will be useful for other HDIV users.
The following is a list of things that I had to do in order to integrate HDIV into CMS. If you do not know what HDIV is, they will probably make little senses to you. You should to at least have a general idea about how HDIV work (read its documentation) before you continue reading, the following section does not do any repeated explanations about HDIV. This list is intended for suggesting some solutions and work-around for those who are having similar problems when integrating HDIV to their webapps. If you are one of such people, then read on...
In CMS 1.0, struts tags were used only when they were necessary. For simple html tags such as <a href="/somewhere">Go</a>, I tend to use the normal html tag instead of using struts's <s:a> tags. Also, sometimes I mix in jstl tags simply because I am more familiar with jstl. Most of the times these jstl tags are equivalent to their struts counter parts, like the <s:url> in struts and the <c:url> tag in jstl core for instance, so I used them arbitrarily. This turned out to be a big problem because currently HDIV (1.3) only supports struts tags. So all the html tags that will be inspected by HDIV will no longer work after I added HDIV to CMS. I had to change them all to use struts tags to get them working again. These tags include:
<a /> <c:url /> <button /> <input /> <c:submit /> <form /> etc...
All of these tags that used to work need to be converted to their equivalent struts tags:
<s:a /> <s:url /> <s:button /> <s:submit /> <s:textfield /> <s:form /> etc...
I was told that in future releases HDIV will also support jstl tags. When that happens, you will need to only convert all the normal html tags to their equivalent struts tags.
Converting html and jstl tags to struts tags is not difficult, but, there is a potentially bigger problem: If you have your own custom tags that are rendered to html <input />, <a />, <radio />, <form /> etc, then they might not work at all with HDIV. This is because these custom tags do not go thru HDIV when they are rendered, but will be validated by HDIV when their inputs are sent back to the sever. Since HDIV does not recognize them and does not have a record of these data, it will treat them as if they are invalid inputs that had been tampered with. In CMS, I do not have any custom tags so I don't need to worry about this issue. However, if your webapp has these custom tags, you need to check to see if there's a way around that. (I have not looked to see if there's a way to filter HDIV validation based on certain user defined criteria)
As mentioned, HIDV injects codes in struts tags and perform validation against these modified taglibs. Therefore, it only checks for these struts tags and will not check normal html tags. If your website uses javascripts that will modify the DOM model and insert dynamic html tags, it's very likely that you will run into troubles when you try to integrate HDIV to your webapp. Luckily, in CMS we used very few javascript so although there were some problems, they were easily fixed. However, I reckon that if your webapp uses javascripts heavily or it uses complicated UI javascript library such as YUI, they will give you some major headaches when you try to use them with HDIV.
Here are a couple of examples of such problems. First of, we use wForms for all the web forms. This javascript library has a feature that allows 'repeatable' fields (see demo here). Internally, this is done by adding some hidden field to maintain the states of the current form so it knows how many repeated fields to generate:
var counterField = document.getElementById(node.id + wFORMS.idSuffix_repeatCounter); if(!counterField) { if(document.all && !window.opera) { // see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp var counterFieldId = node.id + wFORMS.idSuffix_repeatCounter; if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1) // IE5 Mac counterField = document.createElement("INPUT NAME=\"" + counterFieldId + "\""); else counterField = document.createElement("<INPUT NAME=\"" + counterFieldId + "\"></INPUT>"); counterField.type ='hidden'; counterField.id = counterFieldId; counterField.value = "1"; } else { counterField = document.createElement("INPUT"); counterField.setAttribute('type','hidden'); // hidden counterField.setAttribute('value','1'); counterField.setAttribute('name', node.id + wFORMS.idSuffix_repeatCounter); counterField.setAttribute('id', node.id + wFORMS.idSuffix_repeatCounter); } // get the form element var form = node.parentNode; while(form && form.tagName.toUpperCase() != "FORM") form = form.parentNode; form.appendChild(counterField); }
Since the generated hidden <input> is not one of the tags that HDIV recognizes, HDIV will not accept their inputs and will actually throw a validation exception. This is essentially the same problem as issue 1, but you need to do your hacking within the javacript library, which makes it much harder to do especially when the javascript is from a third party.
So we need to force HDIV to also interpret these tags. In CMS, I fixed this problem by changing all the hidden <input> tag to <s:hidden>. The following snippet shows the modifications I needed to make to get them working. Notice the dynamic elements are no longer <input> but <s:hidden> instead:
if(!counterField) { // IE Specific :-( if(document.all && !window.opera) { // see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp var counterFieldId = node.id + wFORMS.idSuffix_repeatCounter; if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1) // IE5 Mac counterField = document.createElement("s:hidden NAME=\"" + counterFieldId + "\""); else counterField = document.createElement("<s:hidden NAME=\"" + counterFieldId + "\"></s:hidden>"); //counterField.type ='hidden'; counterField.id = counterFieldId; counterField.value = "1"; } else { counterField = document.createElement("s:hidden"); //counterField.setAttribute('type','hidden'); // hidden counterField.setAttribute('value','1'); counterField.setAttribute('name', node.id + wFORMS.idSuffix_repeatCounter); counterField.setAttribute('id', node.id + wFORMS.idSuffix_repeatCounter); } // get the form element var form = node.parentNode; while(form && form.tagName.toUpperCase() != "FORM") form = form.parentNode; form.appendChild(counterField); }
Another javascript library CMS uses is the starry widget library. CMS uses it for its rating feature. Similar to wForms, this javascript library also uses a hidden input to maintain states as showed below:
// lets make the hidden form value this.hidden = document.createElement("input"); this.hidden.type = "hidden"; this.hidden.name = this.name; this.element.appendChild(this.hidden);
Again, this input field will not be allowed by HDIV because it's not a recognized tag. Given the example above, you might think that you can also fix this by changing <input> to <s:hidden>. But no, that won't fix it. The previous example works because every time you click on the 'repeat' link, the page reloads itself hence the <input> elements are generated on the server. In the case with the starry widget, the hidden element is not generated on the server side, instead, it's generated on the client side when the page is loaded because it's initialized using the onload event:
<script type="text/javascript" language="JavaScript"> //<![CDATA[ window.onload = function() { new Starry('recommendationDiv', {showNull:false, startAt:1, name:'recommendation'}); } //]]> </script>
So the trick is to force this hidden element to be generated on the server side instead. To do that, I needed to comment out the javascript that generates this hidden tag. Then I added an invisible text field which has the same id as the would be generated hidden tag on the jsp page to make HDIV aware of this field. With that, HDIV will accept the input from this field:
<div id="recommendationDiv"><s:textfield value="1" id="recommendation" name="recommendation" cssStyle="display:none;" /></div>
So why use a <textfield> instead of a hidden <input> you wonder? Well, that is because HDIV assumes the value of a server generated hidden field will not change values, so it checks to make sure that its value is the same when a request comes back from the client. If a generated field needs to change value, you need to use an editable field such as an invisible text field to make HDIV allows value to be changed.
As you can see, generating a hidden input is a pretty common pattern among javascript widgets. Depending on where the hidden tag is generated, you need to make different adjustments to make HDIV take the inputs from these hidden fields. This might be trivial and it could be very difficult to do depending on the actual javascripts. The good news is that you only need to modify generated tags that are actually used for inputs to the server, the other generated tags can stay unmodified.
The following snippet shows a struts 2 logout action. It first invalidates the current user's session and then re-directs the request to the start page. This is a pretty standard logout action in a struts application. However, with HDIV, when the users click on the link to logout, instead of getting re-directed back to the start page, they will receive a 404 page not found error. This is caused by the fact that HDIV uses the http session to store the users' states, so when the logout action invalidate the session object, it also wipes away all the HDIV states. Therefore, when the action tries to re-direct the request to a different page, HDIV will think the request is trying to go to a link that is not generated by the server and hence returns a page not found error.
<action name="Logout" class="LogoutAction"> <result >Start</result> </action> public class LogoutAction extends ActionSupport implements ServletRequestAware { ... @Override public String execute() { _request.getSession().invalidate(); return SUCCESS; } }
This a huge issue that could potentially prevent you from actually using HDIV in a production environment. According to the HDIV developers, it is a bug and will be fixed in later releases. Currently CMS uses version 1.3.1 of HDIV and this problem no longer occurs. The 1.3.1 jar is not yet released, Gorka Vicente (a developer of HDIV) sent it to me through email when I found this problem. I don't know whether the changes are in the latest snapshots jar, if not, I guess you can get it by downloading CMS (look for it in the libs directory). :-)
The error log of HDIV has the format of [type of attack];[action];[parameter];[value];[userLocalIP];[IP];[userId]
Some examples logs are listed below. Now can you figure out what types of errors they were actually logging? Of course not, unless you memorize the translation table from the documentation. When I was working with HDIV, I found myself constantly need go back to the documentation to find out what the actual types of attacks were. I wish they can give a name to each of the attacks instead of using a numeric code name. In the mean time, you will probably want to print out the translation table on a paper when you work with HDIV.
2006-09-22 10:56:07,214 [http-80-Processor25] INFO - 1;action1;param1;value1;188.15.1.25;201.166.24.12;45652146M 2006-09-22 10:58:15,500 [http-80-Processor25] INFO - 7;action3;param2;value3;188.15.1.25;201.166.24.12;15235687G 2006-09-22 11:01:24,124 [http-80-Processor25] INFO - 3;action5;param1;value1;188.15.1.25;201.166.24.12;15235687G 2006-09-22 11:15:00,411 [http-80-Processor25] INFO - 2;action1;param5;value2;188.15.1.25;201.166.24.12;45652146M
I am always interested in new tools and technologies that can increase the quality of a project and can enable greater development efficiency. At the current state of programming and software development, I dare to say that only about 25% of development is actually interesting, the rest are just mundane and boring code writing and maintenance. Personally, I think HDIV is a very interesting project. Like many other great technologies, it abstracts and takes care of yet another portion of common problems that most webapps have, so you can focus your effort on solving the application specific problems. The result is quicker development time and higher quality, now who doesn't want that?
I only tried HDIV on this simple struts 2 project, so I don't want to make any recommendations on whether you should put HDIV to your websites that are in production. However, I do encourage you to try it because it offers a set of security features that every website should have.
Finally, there are a few things that I would like to see in future HDIV releases:
I posted this document on HDIV's forum and received some feedbacks from the HDIV team. It turned out that some of the things I wanted are already possible. Some of them are coming out in later releases. Here is the feedback from the HDIV team:
First, thanks for your work, I think it will be very useful for HDIV users and for us too. About your requests: 1. Jstl tags: we have already done it. It will be released this week. 2. Support for normal html tags: it's not possible. HDIV needs to know non editable data at the server side and it uses custom tags for that. 3. Add features to solve other security problems: we have thought about it, but currently we haven't any plan for that. 4. Official struts plugin: One of the Struts 2 's commiters is analyzing HDIV but we don't know if they are going to include in the official plugin list. Also one of the Struts 1.x commiters is interested on HDIV for Struts 1.4. 5. Filters to skip validations on selected tags and selected inputs: you can do that in the current release. See hdiv-config.xml options for that. You can define userStartParameters (generic start parameters for the whole web app) or parametersWithoutValidation (only for an Action). 6. Have a more human readable logging formats: We agree! We have changed it now and it will be included in the next release too. 7. Give a more flexible level of validations: you can do it now as well. See hdiv-validations.xml file. If you don't need XSS validation delete it from the list and that's all! Thanks for all, we hope these features can improve HDIV integration into your project. Regards, HDIV team