Howto improve Seam application security


In this post I’ll show some tips & tricks to make your application more secure then it is by default. See the links section at the bottom for future reading.

It’s clear that in reality your application can be hacked by some advanced geek, but anyway, let’s start.

1. Be careful with uploaded files

The most dangerous security issue with your site (not only Seam based) – is the ability to upload ANY file to the server and then access it via HTTP.  So, let’s say you have to organize image-files uploading. You can do it in the way
use <rich:upload /> component, upload them and save in some directory on the server which is under some ${context-path}.
This approach have several problems
1) it is always dangerous to allow user upload files and use them directly.
rich:upload by default allow you to upload any file (.jsp) for example and *.jsp file is uploaded it can be accessed by GET/POST request like
http://yourappdomain/$context-path/directory-path/uploaded.jsp
that jsp can then be used to allow external users to access to all your system resources (list directories, do file operations, etc.)

That problem could be solved on different levels
1) Specify <rich:upload acceptedTypes=”jpg,gif,png”/>
2) Check that uploaded file is the real-image on the server-side. it could be done with such method

public static boolean isImageFile (String fileName) {
if (!isExtensionAllowed(fileName))
    return false;
try {
    BufferedImage img = ImageIO.read(new File(fileName));
    if (img == null){
        return false;
    }
    return img.getWidth() > 0 && img.getHeight() > 0;
} catch (IOException e) {
    return false;
}
}

3) Don’t use direct access (${context-path} based). You can create some simple resource-servlet like /resource-servlet?rid=${resource-id}  You also may use content-repositories (Alfresco, JackRabit) if you need something which is more advanced and scalable. It depends on your needs, how much files you want to upload and how are you going to use it (search, etc.).

2. Store password encrypted

You never know if you will be hacked or not, but a good point will be to store encrypted password in your DB, but no a passwords in a plain text.

I was surprised that Seam contains ready-to-use solution for that issue. You can see the Identity Management
So, actually you just need to define several seam annotations like @UserPrincipal for login-name/username field and @UserPassword(hash = “md5”) or other hashing algorithm (curently “md5” and “sha” algorithms are supported).
Please be advised that you have to use identity-manager interface manage users. For example you have to create user with following call identityManager.createUser(“login”,”password”,”firstname”,”lastname”) – in that way the password will be saved in a hashed form. Don’t use entityManager.persist(user) directly.

  <security:identity-manager 
    identity-store="#{ldapIdentityStore}" 
    role-identity-store="#{jpaIdentityStore}"/>

It’s intresting to try to provide my own hashing algorithm. BTW, interesting features have been added to the latest Seam version. We should be able to specify the iteration attribute for @UserPassword and should have the way to override the attribute used together with user-password to generate hash (Salt) https://jira.jboss.org/jira/browse/JBSEAM-3762, by default attribute annotated with @UserPrincipal used as a Salt.

Here is another approach without Seam IdentityManager. You can use MD5 checksum of user-provided password and save it in a hashed form in DB.
Here is the java-function to generate MD5 from String

public static String generateMD5(String password) {
try {
    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(password.getBytes());
    StringBuilder result = new StringBuilder();
    for (byte b : md.digest())
        result.append(String.format("%02x", b));
    return result.toString();
} catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
    return null;
}
}

So, to set the password on User entity use the
user.setPassword(generateMD5(password));

Here is the JPA-query to search user by login and MD5-hashed password.

User user = entityManager.createQuery(
    "select u from User u where u.login= :login and u.password= MD5(:password)")
    .setParameter("login", login).setParameter("password", password)
    .uniqueResult();

Assuming that user-login is the unique field in the DB, you can use the search by login, in that case you may use any hashing algorithms you want, event those which are not supported by DB.

User userByLogin =  entityManager.createQuery(
    "select u from User u where u.login= :login )
    .setParameter("login", login)..uniqueResult();
if (user.getPassword().equals(PasswordHashFunction.createPasswordHash(userPassword)){
   //authentication successful
}

You can also look for other MD5 here http://rosettacode.org/wiki/MD5 . MD5 disadvantage is cryptographic weakness, SHA is better but it is still wll-known, so you may think about AES, or DES functions to encrypt your very private data. See mysql-encrypt-funcitons

3. Store data encrypted

Here is an intresting solution for the encrypting data with Hibernate – Jasypt. Hower please be advised that you can’t use that fields in the WHERE clause.
“Jasypt” + “Bouncy Castle Crypto package SHA-1” should play well toghether. it also have Seam integration

4. HTTPOnly cookies

Microsoft introduced HTTPOnly cookies which is actually just a flag on the cookie “HTTPOnly”

If the HTTPOnly flag (optional) is included in the HTTP response header, the cookie cannot be accessed through client side script (again if the browser supports this flag). As a result, even if a cross-site scripting (XSS) flaw exists, and a user accidentally accesses a link that exploits this flaw, the browser (primarily Internet Explorer) will not reveal the cookie to a third party.

If a browser does not support HTTPOnly and a website attempts to set an HTTPOnly cookie, the HTTPOnly flag will be ignored by the browser, thus creating a traditional, script accessible cookie. As a result, the cookie (typically your session cookie) becomes vulnerable to theft of modification by malicious script.

But currently JBoss/Tomcat application can’t use that feature – they are not implemented in Tomcat yet (https://issues.apache.org/bugzilla/show_bug.cgi?id=44382)
Here is an approach which allow you to introduce it, but I don’t think it is recomended way http://stackoverflow.com/questions/33412/how-do-you-configure-httponly-cookies-in-tomcat-java-webapps.

So, the solution here – is wait for next releases of Tomcat/JBoss.

5. Remove or secure default JBoss applications

Don’t forget to secure or even remove JBoss webapps like /web-console /jmx-console. See the JBoss documentation for details

6. Disable “debug” mode for production deployment

In debug mode a lot of information is disclosure about your application, as well application works slower in “debug” mode. Usually it is configured via property in the components.properties file, something like  “debug=true”, just check your components.xml file for the

<core:init debug="@debug@" jndi-pattern="@jndiPattern@"/>

SSL configuration

It is not always a requirement to have SSL configured for application to have trusted HTTPS protocol, so I’ll put it in a separate post https://achorniy.wordpress.com/2009/07/15/jboss-ssl-configuration/

Links

Blogged with the Flock Browser
Advertisements

https://www.facebook.com/achorniy

Tagged with: , , , ,
Posted in Software Development
One comment on “Howto improve Seam application security
  1. Hoan says:

    This is a very useful article. Thanks for that.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: