blog.smart-java.nl
Ordina J-Technologies – Java Blog

Archief ‘Security’ categorie




Cross Site Request Forgery: Implementation patterns

Door: Jan-Kees van Andel, 7 September 2009

In the previous blog, I’ve shown you a tip of the CSRF iceberg and a simple prevention mechanism. In this blog, I’ll elaborate a bit more on the implementation of a successful CSRF prevention mechanism.

Ad-hoc token per form implementation

As stated, the solution is simple, just include a hidden field in every form, store the same value in the session and compare the two when the form is submitted.

Although working, this fix has some disadvantages.

  1. First, security is not turned-on by default. Every developer needs to be aware of the concept of CSRF and may not forget it when adding new forms. This also applies to developers that have to maintain the website in the future. They may not even know CSRF in the first place and just remove ‘that silly token’ from the form.
  2. Second, it’s probably not the most elegant solution from a Separation of Concerns perspective. Now, every form needs to be aware of security and you need to perform a check in your side controller logic.

So let’s elaborate on some more mature solutions.

What are the variables?

Like always in computer science, there is no single, one-size-fits-all, perfect solution. It always depends on the context. Well, let’s give it a bit of context!

  • Token freshness
    How often is a new token generated? In other words, what’s the time-to-live of a token?
  • Token quality
    How random should the token be? What algorithm should you use?
  • Token storage
    You’ll need to store the token in two places to compare them. First, the HTML where your forms and links reside. And somewhere else…
  • Back buttons and bookmarking
    How to deal with users pressing the back button and seeing a cached web page which they submit? How to deal with bookmarks? Also, you can have Single Sign On, paving the way for deep linking into your system without re-authentication.
  • Ajax
    When using Ajax, many server side requests may occur before a full page refresh happens (if any). What to do with token management?
  • Punishment
    What to do with hack attempts? Do you log an error and short-circuit the server side processing? Or do you log the user off?
  • Logging
    How do you log the event? What kind of information do you include in the logging?

As you can see, there are many variables and all of them may impact your implementation. Most of them even influences each other. In the next section, I’ll discuss all of them in isolation.

Token freshness

This is a major one. A choice needs to be made here. Do you generate one token when the user logs in or do you re-generate a new token each time an action is made?

While re-generating a new token with every request may be very secure, it’s not the most practical solution in reality. In practice, it doesn’t even add that much security, because the Same Origin Policy takes care of the integrity of the token. I also wouldn’t worry about brute force attacks for the token, since the user will probably be logged off long before the attacker guesses right. But when we throw back buttons, Ajax, double-clicking users, etc. into the mix, changing the token during a session has serious disadvantages.

So, unless you have really really really weak token (which will never happen unless you pick an integer between 0 and 1), I would generate the token once (@login) and reuse that value.

Token quality

First of all, we’re not talking about high end encryption stuff here. The only use of the token is turning the URL into a “random” URL. So simple tokens will do.

Important to note is that the attacker will probably never see any token, unless he logs into the system (the token is only used for secure pages). So we need something between an incremental counter and some heavy duty 1024 bit encryption.

I would say, keep it simple. Just use a Random generator or a simple AES key, if it makes your security department happy. When using something like AES, remember to encode it using Base64. It only needs to be one way encryption, since you can compare the encrypted values.

This is a simple AES token generator which also encodes the token using Base64 so it can be used in the HTML.

import java.util.Random;
 
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
 
import org.apache.commons.codec.binary.Base64;
 
public class IntegrityTokenGenerator {
 
    public static void main(String[] args) {
        IntegrityTokenGenerator integrityTokenGenerator = new IntegrityTokenGenerator();
        System.out.println(integrityTokenGenerator.generateToken());
        System.out.println(integrityTokenGenerator.generateToken());
        System.out.println(integrityTokenGenerator.generateToken());
    }
 
    private static final String ENC_TYPE = "AES";
 
    private final SecretKey key;
    private final Cipher cipher;
    private final Random random;
 
    public IntegrityTokenGenerator() {
        try {
            KeyGenerator keyGen = KeyGenerator.getInstance(ENC_TYPE);
            key = keyGen.generateKey();
 
            cipher = Cipher.getInstance(ENC_TYPE);
            cipher.init(Cipher.ENCRYPT_MODE, key);
 
            random = new Random();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Error generating integrity token");
        }
    }
 
    public String generateToken() {
        try {
            String tokenString = "" + System.nanoTime() + "" + random.nextInt();
            byte[] tokenBytes = tokenString.getBytes("UTF-8");
            byte[] encoded = cipher.doFinal(tokenBytes);
            return new String(Base64.encodeBase64(encoded));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("Error generating integrity token");
        }
    }
}

I used AES in this example, but you can use anything, like Blowfish. Performance is not an issue, because of the small token length. I’m talking about less than milliseconds. Also, encryption strength is not very important, because you only need a simple token. Remember to base64 encode binary tokens into plain text.

Also be sure to use a random value each time when you generate a token. This way, the token sequence becomes less predictable. To make it a bit more secure, include some personal data in the token, like a customer ID. The combination of data make the key even less likely to guess. Don’t put any confidential data in the token. It doesn’t add any security and only increases the risk of information leakage.

And make sure not to throw too specific exceptions. Just log them and throw a general exception. Details may never end up in the wrong hands!

One more note about the snippet. A big advantage of this approach is that you don’t have any key management. Keys may leak and key management puts another burden on the system administrators. This implementation generates a key once (when the class is instantiated) and reuses it. Java takes care of the details.

Token storage

There are many ways to keep track of the current token for the currently logged in user, but the HttpSession and Cookies are the most common. Cookies decrease the amount of state your server must manage, but for some reason, developers think cookies are unsafe.

When used for storing CSRF prevention tokens, cookies can be safe. Why? Because an attacker cannot see the contents of the cookies. It’s the browser that appends the cookies to an HTTP request, but the attacker cannot see them. A mechanism like HttpOnly makes cookies more secure, because it prevents scripts to access them. But storing security tokens on the client is inherently unsafe and is probably the prime reason not to use cookies to store the tokens. You CAN do it, but you must be very careful AND rely on the browser to do the right thing. For example, Firefox ignores HttpOnly, causing cookies to be quite unsafe.

But why not store the token in an HttpSession attribute? Generate it once, put it in the session and then compare this token with the submitted token. It has a little bit of overhead, but that will probably be nothing compared with the rest of the session data.

I prefer the HttpSession over cookies by a mile length. With session attributes, I don’t have to think about token confidentiality and with cookies I do. Easy choice.

Back buttons and bookmarking

For some reason, clients want us to build systems in web browsers that are not specifically designed for such a purpose. But it’s a fact of life, so we need to deal with it. Browsers have back/forward buttons, refresh buttons and bookmarks and allow users to navigate freely by using the address bar. If you want your users to be able to use these features, here are some notes.

Only check the CSRF prevention token on state changing actions. Users often don’t bookmark actions, but they bookmark pages. When you only check the CSRF token when the form is submitted, the user can use a bookmark to navigate to the form. The server then renders a token into the form. The user submits the form and it works. You just need to give the server a chance to render a form before the user triggers an action. Using post-redirect-get makes it even more robust, because it prevents users to bookmark the POST request.

Also, don’t re-generate tokens with each request. When you want to support back buttons, you’ll have to be aware of the possibility that the rendered page may be a cached (stale) page which contains an outdated token. So, use the same token for the duration of the session.

You might also want to consider storing the token per user in a persistent store, so that when the same user navigates to the site (after a time of inactivity, maybe days or weeks) he gets the same token, thus fixing possible bookmarking problems.

If you implement your own home brewn CSRF prevention mechanism you can redirect the user to a proper page when the integrity token is absent or invalid. This way, the user doesn’t see some strange error but a proper page instead.

Ajax

When securing your website, you also need to secure your Ajax actions. After all, an Ajax request is just the same (from the server perspective) as another request. So you need to include CSRF tokens in the Ajax requests as well.

Luckily, with most JavaScript libraries, this is easy, since they provide a generic abstraction for Ajax requests and often provide a way to append generic parameters to a request.

The implementation is really easy, just render the CSRF token to a script variable and append it to all requests, like shown below.

var globalIntegrityToken = '<%=(String)session.getAttribute("integrityToken")%>';

Yeah I know that scriptlets are ugly, but I don’t care in this example. You can use EL or whatever you like.

Appending the token to the URL is easy, for example using JQuery:

$.ajax({
  type: 'POST',
  url: '/changeEmail.do',
  data: {
    newEmail: $('emailaddress').val(),
    integrityToken: globalIntegrityToken
  }
});

Again, reusing the same token for a longer period of time makes your job a lot easier. Otherwise, if you re-generate the token per request, you need a way to update all tokens that exist in the DOM when the request is completed. This has performance impact on the client and makes the code a lot more complex. You’ll have to make sure you update all tokens, which include:

  • The global variable, which is easy to update in a generic way
  • All hidden fields that contain a token. If you can easily find the fields, this should also be easy
  • Any tokens in querystrings. This may be a bigger challenge and you may end up using regular expressions to parse and replace the querystrings

So, when you reuse the same token for the duration of the session (or longer) Ajax will probably not be difficult.

Punishment

What do you do when you see someone fiddling with the CSRF prevention token? First of all, don’t proceed with the action the user invokes. But do you log the user off?

If you can, I would suggest making a switch to turn the logoff mechanism on and off. Turning it off makes it easier to test, but you need to turn it on in production. If you’re afraid that you (or the sysadmin) forgets to turn the mechanism on, you can skip this switch.

Just be sure your prevention mechanism doesn’t give false positives, because users will be very annoyed if they are logged off often.

Logging the user off has some other benefit. If there is a hacker messing around with your website, users will get logged off very often and complaints will come in often at your help desk. This way, you have the opportunity to detect hackers. If you let the user proceed working without notification, you will never know that something happened. Unless your sysadmins proactively scan the log files, but unfortunately I don’t see sysadmins do these kind of things very often… :-(

Logging

How do you log the event? I would suggest creating a separate security log file so that sysadmins can react to hack attempts. It’s also useful as a proof in a lawsuit. I would log as much as possible. You can think about:

  • Date/time of course and also the timezone.
  • The entire HTTP request, with cookies, referrer, user agent, etc.
  • The source IP address. However it may be spoofed or a proxy, at least you can see that there is a difference with the “real” user’s IP.
  • The invoked action, along with it’s parameters.
  • Any server side state, specific to your system.

It might be a good idea to also log the first two items when the user authenticates so you can compare the two values.

To summarize

In most cases, you’ll want to use a static CSRF prevention token for the duration of the session. It makes Ajax implementations easier and gives browser features like back buttons and bookmarks a more natural behavior. When you do, implementing a working CSRF prevention mechanism may not be a big issue anymore.

The other variables don’t have this much impact, but be sure to pick a good combination, because some variable combinations may harm each other.




Cross Site Request Forgery: An introduction

Door: Jan-Kees van Andel, 31 August 2009

What is it?

Cross site request forgery (CSRF) is a type of attack, made to a web application by another web application, running in another tab or window in the same browser on the same computer. The server cannot see the difference between legitimate and malicious requests, since all HTTP requests look the same. Thus, the server (obviously) assumes a legitimate request. This gives the hacker the opportunity to impersonate the user and execute commands without the user knowing it. Or more abstract, CSRF may violate integrity.

The attack is caused by three things.

  1. First, the fact that most websites completely rely on a session cookie to represent the user.
  2. Second, because browsers allow websites to invoke certain cross domain requests.
  3. And third, the browser appends all cookies that belong to a domain to every request to that domain.

Example

For example, you have a social networking site where you can add friends to your network. Let’s say everyone has a link on his/her personal page that can be used to add that person to your “friends” list.

The link may look like this:

<a href="/addfriend.do?userid=12345">Add friend</a>

The application uses the HttpSession (or JAAS Subject or whatever. The attack is not specific to Java) to determine the currently logged in user and its userId. The userId of the new friend is taken from the querystring. Both are combined and the relation is created. This mechanism trusts the integrity of the session, which is not a bad thing on itself.

So far so good…

Attacking the example website

But the above example has one major flaw. It assumes that a session safely represents the user and bases security checks on this assumption. For some reason, people think that the session is “safe”, just because it exists on the server. But there is one big issue with sessions. The server determines which session belongs to a certain user by a sessionId, which is stored in a cookie in the user’s browser (URL rewriting is also possible, but not common). From the server point of view, this cookie IS the user. So, when someone accesses the cookie value, this person is automatically authenticated. But that’s not the issue here. That would be a case of session hijacking (or session fixation). You can migitate the risk of session hijacking using common security mechanisms, like SSL, and by ensuring that only proper input is accepted. But we’re talking about CSRF here.

CSRF is possible, because all cookies that belong to a certain domain are sent with each request to that domain, regardless from what domain the request is invoked, even when that web page is running in a different tab or window. SSL doesn’t help you here. If a call is made using HTTPS, the cookies are still appended to the request and a valid HTTPS request is executed.

Note: The details of cookie sharing differ per browser. For example, in Internet Explorer, when you use CTRL+N to open a new window, cookies (and thus sessions) are shared between the window and its “parent”, but when a new process is created (e.g. from the Start Menu or Quick Launch), the two act as two separate browsers and cookies are not shared. This distinction is often not visible to (or known by) the end user. But there are more issues, like the annoying IE8-I-share-cookies-between-tabs-”feature”.

What can an attacker do with this knowledge? Consider the example above. If the social networking site permits HTML content on your personal page, the attacker can first create an account and then put the following HTML snippet on his personal page.

<img src="/addfriend.do?userid=[ATTACKER_USER_ID]" />

The browser sees this image tag and tries to load the image, effectively invoking the following HTTP request to the specified URL. It doesn’t matter that the URL doesn’t point to a real image, the processing on the server is being done anyway. The request is invoked from the victim’s browser, so the victim’s cookies are appended to the request.

Now, everyone who visits the attacker’s personal page becomes a friend of the attacker.

With a bit more effort, the attacker can do almost anything with the entire social networking site. SAMY is an example of an attack on MySpace which used XSS and CSRF techniques to do its thing.

Almost all major websites (like GMail) have had CSRF leaks and many major websites still have.

How about POST?

The previous example uses an image to trigger an HTTP GET request. So you might think that POST is safer. Well, opposed to what many people claim, POST is not much safer than GET (from a networking perspective they are the same). Where we use an image to trigger a GET request, we can just as easily use a (hidden) form to trigger a POST request, as shown in the next snippet:

<form id="myForm" action="/addFriend.do" method="post">
  <input type="hidden" name="userid" value="[ATTACKER_USER_ID]" />
</form>
<script type="text/javascript">
  document.getElementById("myForm").submit();
</script>

Cross Site?

The risk of CSRF may be small or big. In the above example, the attack concentrated on the same website.

This is a case of a “Stored CSRF Vulnerability” (from www.isecpartners.com/documents/XSRF_Paper.pdf), which means that the attacker uses a security hole in the application itself to invoke the HTTP methods. The attacker uses techniques like XSS as an “enabler” for their CSRF attack. This kind of attack is completely the responsibility of the website. They just need to prevent the user to enter any HTML.

But CSRF also works cross domain. In that case, we talk about “Reflected CSRF” (also from www.isecpartners.com/documents/XSRF_Paper.pdf). In this case, your website itself may be perfectly safe (meaning it doesn’t accept HTML snippets like in the example) but an attacker may still invoke HTTP requests to your site using a different site, like a forum, blog, email, IM or whatever. Effectively this means your website can be the victim of the leak in another website. Luckily, this method often fails, since the user must be logged in on the targeted site and visit the malicious other site at the same time. How often this happens depends on the type of website. I’m sure most people don’t visit many dangerous websites when doing electronic banking, but it’s not uncommon to do this when logged in on webmail, especially since these kind of websites often provide features like Single Sign On or long sessions.

But doesn’t the browser prevent this behavior?!?!?! Nope, as I’ll indicate in the following section.

Same Origin Policy

First implemented in Netscape Navigator 2.0, the Same Origin Policy is one of the main security mechanisms in modern browsers. Basically, it allows scripts that are located on the same domain to interact, while preventing interaction between scripts that are on different domains. With interaction, I mean accessing properties, invoking methods and other scripting mechanisms.

The Same Origin Policy is invoked when one of the following actions happens.

  • Requesting URLs with XMLHttpRequest (XHR)
  • Accessing frames and iframes
  • Accessing documents
  • Accessing cookies
  • Accessing browser windows

The list above is not complete, but contains the major actions that are validated against the Same Origin Policy.

This means that for example, it’s not allowed to invoke cross domain XHR requests. That’s good. It adds a serious layer of security and in most cases, you won’t need cross domain XHR requests.

But the Same Origin Policy doesn’t apply to everything. For example, it’s still possible to reference images, scripts and style sheets from different domains. It’s also possible to submit forms across domains.

I’m talking about domains a lot, let’s see what I mean with a domain, using the following listing. It shows how some example URL’s relate to the following web page: http://www.domain.com/pages/homepage.html. It also shows whether the Same Origin Policy allows document retrieval, and if not, why.

Other URL Allowed Reason
http://www.domain.com/otherDirectory/page.html Allowed Different directory is allowed
https://www.domain.com/page.html Not allowed Different protocol
http://sub.domain.com/page.html Not allowed Different subdomain
http://www.domain.com:8080/page.html Not allowed Different port

So a different domain can also mean: different protocol, different port or different subdomain.

Also, be warned that there are several ways around the Same Origin Policy. For example using the Flash cross-domain.xml file. Flickr was vulnerable to this, as written here.

To summarize, the Same Origin Policy provides quite a lot of safety, but not enough to completely prevent CSRF attacks. It does provide a helping hand, as I will show in the last part.

For a more detailed discussion of the Same Origin Policy, see:
https://developer.mozilla.org/En/Same_origin_policy_for_JavaScript and
http://taossa.com/index.php/2007/02/08/same-origin-policy/ and
http://taossa.com/index.php/2007/02/17/same-origin-proposal/

HTTPOnly cookie flag

As some may know, HTTPOnly is a cookie flag that is respected by most major browsers. Some browsers only added HTTPOnly support recently, like Firefox, which only has HTTPOnly support since version 3. Some browsers, like IE, already supported the flag for ages.

It is included in the Set-Cookie Http response header, as follows:

Set-Cookie: =[; =]
[; expires=][; domain=]
[; path=][; secure][; HTTPOnly]

It is appended to the Set-Cookie value in just the same way as the Secure flag, which prevents the cookie to be sent across insecure connections (non-HTTPS).

Basically, marking a cookie as HTTPOnly, prevents any scripts to access it. HTTPOnly cookies are only accessible by the browser to include it in the requests made to the server. It is not accessible by any JavaScript (the document.cookie property).

For more information, see the OWASP HTTPOnly documentation.

Btw. The Java Servlet specification only added HttpOnly support in version 3.0, so it’s not final at the time of writing!

Fixes

The solution is actually quite simple. Just use unguessable URLs in your website. You can achieve this by including a random token in the querystring/POSTdata of “important” requests. It’s not necessary to include it in requests that don’t change any state on the server, only state modifying requests need to have the token.

Why not include it in simple GET requests? Simple, because they don’t change anything and the hacker has no way to read the information that has been sent back to the browser, because of the Same Origin Policy. As indicated before, the Same Origin Policy prevents any scripts to read content from a remote page. Using XHR is also not possible, since the Same Origin Policy prevents cross domain requests using XHR. So, thanks to the Same Origin Policy, confidentiality is ensured. But to ensure integrity, some steps need to be taken by the developer.

For integrity, you need to implement a mechanism like the following: When you render an HTML form, be sure to include a hidden field with a random integrity token. You’ll also need to put this token into the session. When the form is submitted, you’ll need to compare the submitted token with the token in the session. If they are not the same, trigger an error or log the user off. Whatever you do, don’t let the request continue executing!

Also, don’t forget to include an integrity token in state-changing GET requests and Ajax calls and check appropriately!

There are a lot of variables in a successful CSRF prevention mechanism. In the next blog, I’ll give you some guidelines so you can pick the right ones for your website.




Retrofit your webapp with generic XSS protection

Door: Jan-Kees van Andel, 25 June 2009

On my current project (online e-banking application for a medium/large scale bank), we needed to add Cross Site Scripting (XSS) protection afterwards. Well, actually, we had a working XSS protection mechanism in place, but the security auditors pointed out the implementation had flaws. But we’ll get to that later.

Cross Site Scripting?

First, let me explain what Cross Site Scripting (XSS) is. XSS is a hack where malicious code (JavaScript/HTML) is injected into a trusted site. With trusted, I mean the user trusts the site, which is usually the case with e-banking websites. At least, I hope so. ;)

A simple example is a web site which contains a search box, like the following.

<form action="/search.do" method="GET">
  <input type="text" name="searchString" value="Enter something here" />
  <input type="submit" value="Search" />
</form>

When the user submits the form, a request is made to the following URL: /search.do?searchString=[THE_USER_INPUT]

The application responds with a page, containing the results, including a summary of the search criteria, and also the search form, prefilled with the search string the user entered, like here.

<!-- Some HTML -->
 
<form action="search.jsp" method="GET">
  <input type="text" name="searchString" value="${param.searchString}" />
  <input type="submit" value="Search" />
</form>
 
<table id="results">
  <!-- The search results -->
</table>
 
<!-- The rest of the site -->

So far, so good, right?

No, wrong!!!

Suppose a hacker enters the following value into the searchString box:

"/><script>alert("hello")</script><p id="

You’ll see an alert box with the message: “Hello”. You have injected a script into your web page.

But why would anyone hack his own browser session? That’s not the danger here. The danger is that a hacker can create an URL, like

http://www.yoursite.com/search.do?searchString="/><script>alert("hello")</script><p id="

and send it to other people.

When they click on it, the script gets executed in their browser.

Of course, an alert window is harmless, but what if I inject some DOM scripting to create a form where the user must enter his or her credentials for the attacked site? Or an image to automatically trigger a “Button click” (this is actually a case of Cross Site Request Forgery)? With such a leak, I can do almost anything on your page, without you knowing. :)

Let’s see what happens when the login page of your bank is vulnerable to XSS. Phishing becomes easy as stepping on kittens!

As you’re probably now aware of, XSS is a great “enabler” for all kind of other hacks. And the fun part is, hackers can spot XSS flaws easily and often using automatic tools. Not scared yet? In that case, I hope you’re not working on the website where I perform my electronic payments! :) You should be scared for these kind of leaks.

Countermeasures

In theory, it is very easy to protect your website against XSS. Just escape all variables, according to the escaping rules that apply for the markup/script language you are about to put the variables in. Apache Commons Lang has a utility for this purpose: the class StringEscapeUtils with its method escapeHtml and some others. escapeHtml is probably the method you’ll need most, since most variables are outputted in HTML tags.

Why is this fix enough? Well, just test it out with the example above. We modify the JSP to escape the input before rendering it to the client, as shown below.

<%
  String param = request.getParameter("searchString");
  if (param == null) param = "";
  String escaped = org.apache.commons.lang.StringEscapeUtils.escapeHtml(param);
%>
<form action="search.jsp" method="GET">
  <input type="text" name="searchString" value="<%=escaped%>"/>
  <input type="submit" value="Search"/>
</form>

The difference is that I added an escaping routine here. Nothing fancy, except that the “obvious” happens. The user input is shown on the screen in exactly the same way the user has entered it.

You see, no validation or black-/whitelists are needed, which is great, since you don’t want to bother users with security.

Some caveats, which become quite annoying later on.

  • Timing is essential with regards to escaping. You cannot do it on your input parameters, since you’ll then end up with HTML encoded texts in your backend systems (that was the flaw I was talking about in the introduction). You also don’t want to be bothered with HTML encoding in your Java business logic, so input filtering is not the way to go. You definitely need to filter on the output, just before you render your page back to the client.
  • You may not escape a variable twice, since the user ends up with HTML codes in his/her UI. When escaping > twice, it first becomes > and after the second pass, it becomes &gt;, which is not only wrong with regards to usability, it also opens up some interesting new security holes.
  • When you persist user input unencoded, which you should, you also need to escape all variables that come from there before rendering the web page. Essentially, you just need to escape every variable that is inserted into the web page, since it may (directly or indirectly) be manipulated by a malicious user.

Implementation

So, we need a way to escape all user input just before it is rendered. What are the options?

  • Custom JSP function
    Probably the easiest way to escape special characters is by implementing a JSP custom function, like the JSF “outputText” component:
    <h:outputText escape="true" value="<script>" />

    . This one is easy to implement. It’s just a burden to edit all 200 JSP files (and the same amount of JSP Tag files) in our project and add the function at all places where a variable is used. The second issue is that you also need to be sure you don’t escape a variable twice, since that results in strange output and, ironically, new security holes. This is especially the case with tag attributes. Do you escape in the tag or in the calling code? You’ll need a strategy there. Finally, and probably most importantly, this solution will probably become a maintenance nightmare. JSPs are cluttered with these custom functions and future maintainers may never forget one when making changes. Something more generic would be nice.

  • ELResolver?
    ELResolvers are added to JSP with version 2.1. We use Tomcat 6, so that’s great. However, the ELResolver mechanism differs greatly from, for example, the JSF VariableResolver mechanism. VariableResolvers are implemented using decoration, which makes them very useful to intercept all variable access and modify the responses. ELResolvers are implemented as a CoR and the implementation makes it impossible to create a custom filtering mechanism for all variables. It is also not the right place for this kind of logic. Why? Imagine a JSP Tag file. In the calling JSP file, you use EL to pass an argument to the tag and in the JSP Tag file, you use EL to put the argument in the HTML. With an ELResolver solution, variables get escaped twice. Not good.
  • Tomcat hooks, AOP, Javassist?
    There are other ways to hook into the JSP/Servlet lifecycle. Containers provide vendor specific hooks to hook into the lifecycle, but unfortunately, no Tomcat hooks to help us here. But we’re not defeated yet! We can use AOP or a bytecode library to enhance some core Tomcat classes. Of course, we’re entering the black magic room here, but if it works… Unfortunately, these are also no real options. We can for example intercept all calls that go into the EL runtime by proxying all EL calls, but this has almost all of the issues as a custom ELResolver. Darn!
  • Servlet Filter?
    But what’s wrong with a plain old Servlet Filter which escapes HTML special tags afterwards? The issue with a Servlet Filter is that it can’t possibly see the difference between legitimate and injected markup. The Filter Either escapes all markup (including your own) or nothing (which renders it useless). After the JSP has been executed, you’ll end up with HTML. If there is an XSS attack, you are simply too late when you use a Servlet Filter. So, it has to do with timing. Let’s step back.
  • Object graph XSS filtering!
    There are two issues with most solutions presented.

    • Timing: The solution is too early or too late, making it useless or wrong.
    • no. executions: The solution has the risk of escaping the variable more than once, making it wrong and unsafe.

    So, what’s the best point in the request processing lifecycle to do the escaping? It turns out that just before the JSP is executed is the ideal moment. It’s ideal, because you’re not too early, so you won’t notice anything in your Java code. It’s also ideal, since you have full control over execution, making it easier to execute once and only once.

Object graph XSS filtering

The graph walker sounds like a solution, however, it’s a difficult one to implement.

The reason is the flexibility of our MVC framework. It’s allowed to pass arbitrary object graphs to the JSP. This is an issue, since you can’t just iterate over all attributes and escape them. For example, some attributes may be collections, maps or domain objects, containing possibly unsafe strings.

So we need an object walker. What features should it have?

  • Cycle detection, to prevent infinite loops.
  • Ability to modify arbitrary objects, either using reflection (POJO’s) or API (Collections).

Implementation sources

A hack and slash implementation of this solution is attached. What do you guys think of it? Does it look like a workable solution?

Ps. I’m aware of the fact that I haven’t implemented any pattern or followed any best practice. It’s just a simple PoC.

Click here to download the ObjectWalker sources




Request in LoginModule

Door: Jan-Kees van Andel, 28 July 2008

Je zult het misschien weleens gewild hebben, vanuit je JAAS LoginModule toegang hebben tot je HttpServletRequest. Helaas biedt JAAS hier geen ondersteuning voor.
Je JAAS LoginModule wordt namelijk door JAAS aangeroepen en je krijgt alleen een Subject, een CallbackHandler implementatie en nog een zootje parameters mee. Maar dus geen servlet objecten.

JACC

Voor deze beperking heeft Sun een oplossing (bestaal al sinds 2003), namelijk JACC. JACC staat voor Java Authorization Contract for Containers en is een specificatie die eigenlijk voor Container leveranciers bedoeld is. Echter, je kunt het als applicatie ontwikkelaar ook gewoon gebruiken.

JACC heeft namelijk de klasse PolicyContext en in deze klasse zit een methode getContext(String).

getContext accepteert de volgende Strings:

  • javax.security.auth.Subject.container
    Geeft het huidige Subject terug.
  • javax.xml.soap.SOAPMessage
    Geeft de SOAPMessage terug bij een EJB Web Service Endpoint (alleen EJB).
  • javax.servlet.http.HttpServletRequest
    Geeft het request terug.
  • javax.ejb.EnterpriseBean
    Idem voor de EJB.
  • javax.ejb.arguments
    Geeft een array terug met de parameters waarmee de EJB aangeroepen wordt. (alleen EJB)

In de spec staat niets over Java EE 5 of EJB 3.0, maar aangezien aan de onderliggende architectuur niets veranderd is, zal het ook op een Java EE 5 container wel werken.

Het HttpServletRequest

Hieronder staat een LoginModule die de HttpSession nodig heeft.

import javax.security.jacc.PolicyContext;
import javax.security.jacc.PolicyContextException;

// Other imports skipped

public class CustomLoginModule implements LoginModule {

    private Subject subject;
    private CallbackHandler callbackHandler;
    private Object object;

    public void initialize(Subject subject,
                           CallbackHandler callbackHandler,
                           Map sharedState,
                           Map options) {

        this.subject = subject;
        this.callbackHandler = callbackHandler;

        // Get an object from the HttpSession.

        try {
            HttpServletRequest req = (HttpServletRequest) PolicyContext.getContext("javax.servlet.http.HttpServletRequest");
            object = req.getSession().getAttribute("someAttribute");
        } catch (PolicyContextException e) {
            LOGGER.error("Unable to retrieve object from user session:", e);
            object = null;
        }
    }

    public boolean login() throws LoginException {
        // Do some login stuff and use the object from the session
    }

    public boolean commit() throws LoginException {}

    public boolean abort() throws LoginException {}

    public boolean logout() throws LoginException {}
}

Zoals je ziet, kun je via PolicyContext.getContext gemakkelijk aan het request komen. En als je een request hebt, is de sessie niet ver meer.

Met deze klasse wordt het dus stukken gemakkelijker om context specifieke zaken te gebruiken bij het inloggen.




Spring Security

Door: Jethro Bakker, 4 July 2008

Het is inmiddels al even geleden dat Acegi Security gerebrand is naar Spring Security. Acegi stond bekend om de erg lastige configuratie. Een gebruiker heeft zelfs gezegd dat: “every time I use Acegi a fairy dies..”

Je moest bijvoorbeeld handmatig een heel aantal Servlet Filters configureren en een werkende configuratie bestond uit minimaal een A4tje.

Dan Diephouse zegt hierover:

“The fact that I need to write *180* lines of XML to configure Acegi for a simple simple user authentication in front of a webapp means that something is eriously wrong. There should be default profiles which configure things out of the box and provide minimal switches so users can’t screw things up.”

Met de komst van Spring 2.0 is er de mogelijkheid om custom namespaces te gebruiken in je XML configuratie. Spring Security maakt hier, sinds versie 2.0, ook gebruik van. De configuratie kan hierdoor bestaan uit een paar regels ipv een A4tje. De referentie handleiding is een goed punt om te starten.

Om te beginnen moet je een DelegatingFilterProxy definiëren in je web.xml:

<filter>
 
  <filter-name>springSecurityFilterChain</filter-name>
 
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
 
</filter><filter-mapping>
 
  <filter-name>springSecurityFilterChain</filter-name>
 
  <url-pattern>/*</url-pattern>
 
</filter-mapping>

In je Spring configuratie voeg je een extra namespace toe:

<beans xmlns="http://www.springframework.org/schema/security">
 
</beans>  xmlns:beans="http://www.springframework.org/schema/beans"
 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
 
http://www.springframework.org/schema/security
 
http://www.springframework.org/schema/security/spring-security-2.0.xsd"
 
    ...

Met behulp van een paar regels heb je een basis configuratie (form based):

<http auto-config="true">
 
    <intercept-url pattern="/**" access="ROLE_USER"></intercept-url>
 
</http><authentication-provider>
 
    <user-service>
 
      <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN">   </user>
 
      <user name="bob" password="bobspassword" authorities="ROLE_USER"></user>
 
    </user-service>
 
</authentication-provider>

Op de achtergrond worden alle filters die in Acegi gebruikt werden gestart.

Om bovenstaande configuratie werkend te krijgen moest ik eerst upgraden naar de nieuwste versie van WAS 6. Er zit een enorme bug in zowel 6.0 als 6.1: http://www-1.ibm.com/support/docview.wss?rs=180&uid=swg1PK27620 Na het installeren van de laatste fixpack werkte alles.

Case
Op mijn huidige project is het de bedoeling dat onze applicatie geïntegreerd wordt in een Siebel omgeving. De gebruiker logt in op Siebel en kan vervolgens naar onze Java applicatie gaan. De gebruiker hoeft maar 1x in te loggen. Onze applicatie krijgt een Cookie binnen met o.a. de gebruikersnaam. Op basis van de gebruikersnaam roept onze applicatie een externe service aan om de rollen van de gebruiker op te halen. Hoe zetten we Spring Security in om dit te regelen? Het voorbeeld hierboven beschreven is in ieder geval niet toereikend.

De referentiehandleiding is weer een goed startpunt het heeft namelijk een hoofdstuk over pre authenticatie scenario’s. Dat zijn scenario’s waarin een ander systeem –dat aangeroepen wordt voor je eigen applicatie- verantwoordelijk is voor de authenticatie. Onze eigen applicatie is alleen verantwoordelijk voor de autorisatie. We moeten een eigen implementatie maken AbstractPreAuthenticatedProcessingFilter en van de UserDetailsService.

Het filter haalt o.a. de gebruikersnaam uit het request cookie.

protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
 
        Cookie[] cookies = request.getCookies();
 
        if (cookies != null) {
 
            for (int i = 0; i &lt; cookies.length; i++) {
 
                Cookie cookie = cookies[i];
 
                //TODO haal user principal uit cookie
 
            }
 
        }
 
        return "DUMMY_USER";
 
}

De service ziet er zo uit:

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
 
        //TODO check username against external system
 
        GrantedAuthority[] authorities = new GrantedAuthority[1];
 
        authorities[0] = new GrantedAuthorityImpl("ROLE_ALL");
 
        //authorities[0] = new GrantedAuthorityImpl("ROLE_NOACCESS");
 
        //authorities[0] = new GrantedAuthorityImpl("ROLE_RESTRICTED");
 
        return new User("DUMMY_USER", "DUMMY_PASSWORD", true, true, true, true, authorities);
 
}

En in de Spring configuratie doe je het volgende:

<security:http auto-config="true">
 
  <security:intercept-url pattern="/**" access="ROLE_ALL, ROLE_RESTRICTED">
 
</security:intercept-url><security:authentication-manager alias="authenticationManager"></security:authentication-manager>
 
<security:authentication-provider user-service-ref="userDetailsService"></security:authentication-provider>
 
<bean id="userDetailsService" class="MyUserDetailsService"></bean>
 
<bean id="cookieFilter" class="CookiePreAuthenticatedProcessingFilter">
 
  <security:custom-filter position="PRE_AUTH_FILTER"></security:custom-filter></bean>
<property name="authenticationManager" ref="authenticationManager"></property>
</security:http><bean id="preauthAuthProvider" class="org..preauth.PreAuthenticatedAuthenticationProvider">
 
  <security:custom-authentication-provider></security:custom-authentication-provider></bean>
<property name="preAuthenticatedUserDetailsService">
    <bean id="userDetailsServiceWrapper" class="..UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="userDetailsService"></property>
    </bean>
 
  </property>

Het is mij erg meegevallen om werkende configuratie te maken en aan te passen aan de specifieke eisen binnen ons project. De configuratie van security is in ieder geval met de release van Spring Security 2.0 een stuk eenvoudiger geworden!




JSF bean security met CGLIB

Door: Jan-Kees van Andel, 14 April 2008

Ik krijg vaak de vraag hoe je je JSF applicatie gemakkelijk kunt beveiligen. Dit is niet altijd even gemakkelijk om een goed antwoord op te geven. In principe bouwt JSF voort op de Servlet en JSP specs, waardoor je in je JSF code gebruik kunt maken van JAAS functionaliteiten, zoals de methoden isUserInRole en getUserPrincipal op het request object. Deze kun je in je managed beans uitlezen via de ExternalContext. FacesContext.getCurrentInstance().getExternalContext(); ExternalContext is een wrapper voor external context implementaties, waardoor je jouw JSF applicatie zowel in een Servlet als in een Portlet omgeving kunt draaien, zonder allemaal if’s in je code.

Het is echter niet echt interessant werk om alle managed bean action methods te voorzien van een stuk code waarin de rollen gecheckt worden. Dat gaan we nu iets anders doen.

Note: Voor real life kun je beter naar een production-ready framework zoals Acegi kijken, zeker in een organisatie waar al een dergelijke tool in gebruik is. Dit stukje is puur bedoeld als quick start en om aan te geven wat je allemaal zelf met annotations en reflection kunt bouwen.

We beginnen met deze pagina, waarop een stel commandLinks liggen die elk een andere methode aanroepen:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
	</head>
<body>
<f:view>
	<h:form>
		<h:commandLink value="Public action" action="#{secureBean.publicAction} /">
		<h:commandLink value="Secure action (admin only)" action="#{secureBean.adminAction} /">
		<h:commandLink value="Secure action (admin and manager)" action="#{secureBean.combiAction} /">
		<h:commandLink value="Public action (empty annotation)" action="#{secureBean.public2Action} /">
	</h:form>
</f:view>
</body>
</html>

Achter deze pagina hangt de volgende Managed Bean class:

package nl.ordina.jsf.security.bean;

import java.security.Principal;
import javax.faces.context.FacesContext;
import nl.ordina.jsf.security.annotation.Allow;
import nl.ordina.jsf.security.annotation.AllowAll;

public class SecureManagedBean {

	public String publicAction() {
		System.out.println("I'm in publicAction");
		return null;
	}

	public String adminAction() {
		System.out.println("I'm in adminAction");
		return null;
	}

	public String combiAction() {
		System.out.println("I'm in combiAction");
		return null;
	}

	public String public2Action() {
		System.out.println("I'm in public2Action");
		return null;
	}
}

Zoals je ziet, is hier niets secure aan. Iedereen kan deze methoden aanroepen. Daar gaan we nu iets aan doen.

We gaan met CGLIB proxies om de managed beans hangen die de toegang regelen. Dit doen ze op basis van annotations die op de klasse/methode liggen.

De configuratie

Eerst moeten we zorgen dat we uberhaupt proxies kunnen maken. Hiervoor hebben we CGLIB nodig. Ja, ik weet het, in de JDK zitten tegenwoordig ook proxy functionaliteiten, maar die kunnen alleen proxies maken van klassen die een bepaalde interface implementeren. CGLIB heeft deze beperking niet.

Let wel op met CGLIB, zeker als je ook Hibernate en Spring tegelijk gebruikt. Dit kan voor irritante classloading problemen zorgen. Bovendien, als je toch al interfaces implementeert voor je managed beans (heb ik nog niet gezien in de praktijk), kun je gewoon de JDK Proxy klassen gebruiken. Tevens performt die in nieuwe Java versies (5 en hoger) een stuk beter dan CGLIB.

Maar goed, ik heb cglib-nodep-2.2_beta1 gebruikt. nodep betekent dat de ASM klassen meegeleverd worden in de JAR. Zo heb je altijd de goede twee versies bij elkaar en geen last van annoying runtime fouten.

Daarna passen we de faces-config.xml aan zodat de Managed Beans geproxied worden.

	...
	<application>
		<variable-resolver>nl.ordina.jsf.security.resolver.SecurityBeanResolver</variable-resolver>
	</application>
	...

Dit doen we met een VariableResolver. Dit is een object dat aangeroepen wordt als de JSF runtime een expressie (#{myBean.methodName}) resolved. We bouwen met behulp van het Decorator pattern voort op de standaard VariableResolver, zodat het teruggegeven geproxied wordt.

package nl.ordina.jsf.security.resolver;

import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.VariableResolver;
import net.sf.cglib.proxy.Enhancer;

public class SecurityBeanResolver extends VariableResolver {

	private final VariableResolver decorated;

	public SecurityBeanResolver(VariableResolver decorated) {
		this.decorated = decorated;
	}

	@Override
	public Object resolveVariable(FacesContext fc, String expression)
			throws EvaluationException {
		Object bean = decorated.resolveVariable(fc, expression);
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(bean.getClass()); // 1 The API to implement
		enhancer.setCallback(new SecurityCallback()); // 2 This object will be called when you invoke methods on the proxy
		return enhancer.create();
	}
}

JSF zorgt ervoor dat de constructor met de juiste parameter aangeroepen wordt, zodat we die kunnen gebruiken. Als we eenmaal het object hebben, kunnen we hem met CGLIB enhancen. De meeste regels zijn vrij zelfbeschrijvend.

  1. setSuperclass wordt gebruikt om aan te geven welke methoden geproxied moeten worden. Hier kunnen we dus gewoon de bean klasse aan meegeven.
  2. setCallback wordt aangeroepen door CGLIB als jij methoden van de proxy aanroept. Hierin komt dus de security logica.

De Managed Bean new and improved

Nu passen we de Managed Bean aan zodat we in de SecurityCallback kunnen zien wie erin mogen.

package nl.ordina.jsf.security.bean;

import java.security.Principal;
import javax.faces.context.FacesContext;
import nl.ordina.jsf.security.annotation.Allow;
import nl.ordina.jsf.security.annotation.AllowAll;

@AllowAll
public class SecureManagedBean {

	public String publicAction() {
		System.out.println("I'm in publicAction");
		return null;
	}

	@Allow(roles = "admin")
	public String adminAction() {
		System.out.println("I'm in adminAction");
		return null;
	}

	@Allow(roles = { "admin", "manager" })
	public String combiAction() {
		System.out.println("I'm in combiAction");
		return null;
	}

	@Allow
	public String public2Action() {
		System.out.println("I'm in public2Action");
		return null;
	}
}

Zoals je kunt zien, hebben we een aantal annotations toegevoegd aan de klasse. In dit geval zijn het zelfgemaakte annotations, maar je kunt ook gewoon de annotations uit het javax.annotation.security package gebruiken. Het zijn twee annotations, namelijk AllowAll en Allow.

  • Met AllowAll heeft iedereen toegang. In dit voorbeeld is het zinloos, aangezien je zonder annotation ook al toegang hebt.
  • Allow heeft een parameter “roles” waarmee je kunt opgeven wie erin mogen. Geen parameter meegeven betekent dat iedereen erin mag.

Dit zijn de annotations.
Allow.java

package nl.ordina.jsf.security.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.METHOD;

@Target(value = { TYPE, METHOD })
@Retention(value = RUNTIME)
public @interface Allow {
	String[] roles() default "[uninitialized]";
}

De “[uninitialized]” is een workaround voor een beperking van Java. Je kunt namelijk geen null als default waarde gebruiken, want dan gaat de compiler zeiken. Je mag er redelijkerwijs vanuitgaan dat niemand deze naam als rol gebruikt, anders maak je er nog iets gekkers van.

AllowAll.java

package nl.ordina.jsf.security.annotation;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Target(value = { TYPE, METHOD })
@Retention(value = RUNTIME)
public @interface AllowAll {
}

De meest interessante onderdelen van deze annotations zijn de annotations erboven.

  • Met Target geef je aan waarbij je deze annotation kunt plaatsen. Ik heb gekozen om alleen klassen en methoden toe te staan, maar er zijn nog een stuk of 5 mogelijkheden, zoals properties.
  • Door Retention op RUNTIME te zetten, kun je de annotatie uitlezen tijdens runtime. Met CLASS en SOURCE is dit niet mogelijk.

De SecurityCallback

Nu maken we de code die daadwerkelijk de beveiliging regelt, namelijk de CGLIB Callback. Ik implementeer net.sf.cglib.proxy.MethodInterceptor. Deze is speciaal gemaakt voor het proxien van method calls.

package nl.ordina.jsf.security.resolver;

import java.lang.reflect.Method;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import nl.ordina.jsf.security.annotation.Allow;
import nl.ordina.jsf.security.annotation.AllowAll;

public class SecurityCallback implements MethodInterceptor {

	@Override
	public Object intercept(Object object, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		FacesContext fc = FacesContext.getCurrentInstance();
		ExternalContext extCtx = fc.getExternalContext();

		// We exclude calls to get*** and set***, because these
		// methods are automatically called by the JSF runtime
		if (!isGetterOrSetter(method)) {
			boolean allowAtBean = allowAtBeanLevel(object, extCtx);

			boolean allowAtMethod = allowAtMethodLevel(method, extCtx);

			// Simple check, a production-ready implementation probably needs
			// more sophisticated rules, considering superclasses, precedence, etc
			if (!allowAtBean || !allowAtMethod) {

				// In this case, I just redirect to an error page,
				// but you can also prefer an exception. For
				// example to leverage an existing exception handler
				fc.responseComplete();
				extCtx.redirect("403.jsp");
				return null; // Prevent method invocation
			}
		}

		// Invoke the method (use this syntax to prevent infinite loops)
		return proxy.invokeSuper(object, args);
	}

	private boolean allowAtBeanLevel(Object object, ExternalContext extCtx) {
		AllowAll allowAll = object.getClass().getAnnotation(AllowAll.class);
		if (allowAll == null) {
			Allow allow = object.getClass().getAnnotation(Allow.class);
			if (allow != null) {
				String[] roles = allow.roles();
				return checkRoles(extCtx, roles);
			}
		}
		return true;
	}

	private boolean allowAtMethodLevel(Method method, ExternalContext extCtx) {
		AllowAll allowAll = method.getAnnotation(AllowAll.class);
		if (allowAll == null) {
			Allow methodAllow = method.getAnnotation(Allow.class);
			if (methodAllow != null) {
				String[] roles = methodAllow.roles();
				return checkRoles(extCtx, roles);
			}
		}
		return true;
	}

	private boolean checkRoles(ExternalContext extCtx, String[] roles) {
		// Uninitialized is the default since annotations don't like null defaults
		if (roles == null || (roles.length == 1 && roles[0].equals("[uninitialized]"))) {
			return true;
		}
		for (String role : roles) {
			if (extCtx.isUserInRole(role)) {
				return true;
			}
		}
		return false;
	}

	private boolean isGetterOrSetter(Method method) {
		String name = method.getName();
		return name.startsWith("get") || name.startsWith("set");
	}
}

Het is een vrij eenvoudige security check, maar je kunt dit natuurlijk uitbreiden met extra opties, bijvoorbeeld een Deny annotation.

Ten slotte

  • Als je Spring en/of Hibernate gebruikt, dan krijg je CGLIB en ASM cadeau. Gebruik dan ook die versie, anders kom je snel in de problemen met NoClassDefErrors en zo. Ik weet niet waarom, maar schijnbaar is het erg moeilijk om deze tools backwards compatible te bouwen.
  • Bedenk dat dit nog geen complete, waterdichte, secure implementatie is. Deze code dient alleen voor autorisatie. Om je applicatie helemaal te beveiligen, heb je minimaal nog authenticatie en beveiliging van de verbinding nodig (zoals SSL). Ook zaken als (SQL) injection of Cross Site Scripting vang je hiermee nog niet af.
  • In een webapplicatie zijn altijd twee soorten autorisatie, namelijk de acties die je kunt doen en de pagina’s die je kunt zien. De eerste kun je met deze techniek dichttimmeren. Voor de tweede kun je je URL’s afschermen in de web.xml.
  • Let op dat een VariableResolver door JSF voor elke expression aangeroepen wordt. Dus ook als je de session opvraagt. In real life zul je dus nog een check willen doen of het object wat je opvraagt wel een managed bean is. Dit kun je doen door bijvoorbeeld een annotation ManagedBean te maken en daarop te checken of een conventie te hanteren dat managed beans in een bepaald package staan. Helaas biedt JSF geen implementatieonafhankelijke manier om te checken of een klasse een managed bean is.
  • Ook is deze code nog niet echt efficiënt. Per keer dat een bean opgehaald wordt, wordt een nieuwe proxy gemaakt en worden steeds weer de methodes en annotations met reflection doorgelopen. Je zou hiervoor gewoon een static Map met beans en properties kunnen bijhouden. Maar goed, eerst ff testen of de code echt traag is. Het kan meevallen.

Ten slotte, ga gewoon eens een keertje met CGLIB (of JDK Proxy) spelen. Het is niet voor niks dat vrijwel elk bekend framework het gebruikt, het is gewoon een hele handige library.