J2EE Application Security
Digital Certificate based SSL for network security and Authentication/Authorization based on the security-constraints tag in deployment descriptors for Access Control is quite standard for J2EE Application.However that is not the end of protecting your J2EE Application.If yours is a Web Application, there are a number of application vulnerabilities which a attacker can make use of and get into your application.
1) Cross Site Scripting (XSS) - Assume you a Web page where user can enter some text data and another page where these use entered data is displayed (Some thing like 'add comment' and 'moderate comments' pages). An attacker may enter a script instead of text data in the input text area (eg: '[script]echo 'this is a sample attack'[/script]). When the somebody else (say administrator) opens the second page instead of the text being displayed the script shall be executed. This is called XSS.The solution for this kind of attach is:a) Validate all user inputs - in this case we should not allow patterns like [script]b) Encode all output content (not formatting metadata). If output is encoded even if [script] is present in the text, that will be displayed as [script] instead of executing.For practical purpose you need to validate input as well as encode output content.
2) SQL Injection - Assume you have a authentication servlet. You store the username password in database use the following SQL for authentication. "select 1 from user_table where username = '" + username + "' and password = '" + password + "'"; An attacker may enter the following text as username and passwords
Username = "abc' OR 1 = 1; --
Password = xyz
NOw the SQL becomes "select 1 from user_table where username = 'abc' OR 1 = 1; -- password = 'zyz'; which is all ways trueSolution for this kind of attack is Using PreparedStatement instead of Statement.
3) Cross Site Request Forgery (CSRF) - Assume you are using FireFox and in one tab you have opened your web application. Now get a email (from attacker) with some links to some fancy pictures. Actually the link is to your application with query string indicating to do some operation. Since you are already logged in to the application it will not ask for authentication again. It will silently execute the operation. Solution for this kind of attack is to use a unique string (other than session id) to identify each session/request (It identifier could be per session or per request; per session is sufficient for most purposes). You need to attach this identifier to each form and link in your application. Validate all request for a valid identifier.
Vulnerabilities in Swing clients
1) SQL injection is equally valid in Thick clients. Solution is same - using Prepared statements
2) Un protected JNDI objects - There is no standard way for protecting JNDI objects in J2EE. If an attacker can get hold of a DataSource or JMS Queue or topic he could access the application. or invoke SQL queries directly. Solution is to protect all JNDI resources in App server vendor specific way
3) Unprotected EJBs - Some times In swing client you first log in and then navigate other screens to do operations hence even if the subsequent EJBs are not protected you may not notice it. However a attacker can write own EJB client which can directly invoke the un protected EJB Solution is to protect all the EJBs
Session Fixation
Somebody could already start a session (may be by accessing a non-secure/less previleged resource) and trick you to login to the application and use it. Then you are vulnerable. He could have saved the sessionid before handing over to you and use it from another machine. If you close the browser after your usage (without loggint out - which many people do), he could use it infinitely long. If you logout after use he could use only as long as you are logged in.
You could prevent this by calling session.invalidate before authentication, in the authentication method
If you are BASIC authentication, you are at risk unless the web server handles it correctly (most web servers don't handle it). Ideally authentication framework in the web server should have called session.invalidate() before authentication.
Don't use BASIC authentication in your application.
Sample application to demonstrate the vulnerability
1) Use some war files for practice
2) Configure a WebLogic or Tomcat server
3) Create a user called "user1" in weblogic
4) Create a user called "user1" and role called "user1" in Tomcat
5) Deploy the application in WebLogic or tomcat
6) Access test2.jsp (http://localhost:7001/SimpleWebApp1/test2.jsp)
7) Note the cookie (use LiveHttpHeaders with firefox)
8) Access test1.jsp (http://localhost:7001/SimpleWebApp1/test1.jsp). You will be asked to log in, log in.
9) Now try access the test1.jsp using the TestSessionFixation.java provide inside the war. Usage: java rejeev.sessionfixation.TestSessionFixation . You will be able to see test1.jsp without login.
Oracle has supposed to fix this issue in WebLogic Server 10.3. However I have observed that latest version of WebLogic Server (10.3) has this issue. I think they have mistaken this issue as an issue in thier admin console (admin cosole being a web application that is also vulnerable). However fundemental vulnerability is in the authentication framework.
Character encoding in Web DevelopmentIf you have static HTML page, use the following tag in <head> section.
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
If you have a dynamic page (JSP or Servlet), use the following code
response.setCharacterEncoding("UTF-8");
If this method is called, web server will set the Content-Type HTTP header accordingly.
Content-Type: text/html; charset=UTF-8
Assume you have a page which accepts Russian characters.
Then according the standards you need to specify accept-charset="UTF-8" in form tag
<form accept-charset="UTF-8" >
However Internet explorer does not supports accept-charset. Nevertheless it is better to specify this attribute, howeve that is not sufficient.
However most of the browsers use the same encoding used for rendering the page (specified by Content-Type HTTP header), for encoding the forms submitted from the page. Hence specify the encoding for the page which contains the form (as mentioned above). This will force the browser to use the same encoding for form submission.
Browsers are supposed to send the Content-Type HTTP header along with HTTP reqeust for the form submission. however most of the browsers including IE and Firefox don't do so. Hence server side there is no way to acertain the encoding used by client.
Best work around is to use Content-Type (as mentined above) for the web pages containing forms and hence force the browser to use specific encoding scheme. Then specify the encoding at server while processing the reqeust by specifying
if(request.getCharacterEncoding() == null) request.setCharacterEncoding("UTF-8");
However there is another catch. If you want to submit a form with some other encoding scheme (for whateve reason - typically this can happen when you want to submit form to another website). Then it will be difficult from IE.
On the whole the best practice is to use always
response.setCharacterEncoding("UTF-8"); -- in JSP/Servlet
if(request.getCharacterEncoding() == null) request.setCharacterEncoding("UTF-8"); -- in JSP/Servlet
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> -- in static pages
<form accept-charset="UTF-8" > -- all forms
Allways use "UTF-8" as the encoding which is much better scheme than all other available schemes.Session Tracking with SSL in WebLogicWhen we are using SSL, there are two session ids and they are put in two separate cookies. _wl_authcookie_ is a secure cookie.
1) JSESSIONID
2) _wl_authcookie_
All SSL requests should contain both cookies otherwise the request will not be considered as authenticated. Non SSL (HTTP) requests needs to contain only the JSESSIONID cookie.
The sad thing is that WebLogic's implementation of URLConnection's 'getHeaderField("Set-Cookie")' returns only one cookie (whichever comes first). If you want to get both cookies you need to use 'getHeaderFields()' and look for the entry with key "Set-Cookie". To set cookie we need to use URLConnection.setRequestProperty("cookies") and we need to pass both cookies with semi colon (;) as separator.
If cookies are disabled then URL rewrite shall be used for session tracking. However you don't need to pass both cookies for SSL; only the JSESSIONID cookie will do. With SSL, URL rewrite will work only if either of the following is configured
1) EnableCookie=false in weblogic.xml
2) AuthCookieEnabled=false in config.xml
Spring Security
All projects I have worked on included login. And roles. They were built using database tables or third party applications like LDAP or service based authentication. However the new projects starting now are going ahead with Spring Security. As I have no idea about it, I decided to give a go at understanding Spring Security.
I decided to first do a small simple web application and enable the security for it. Not spring based, simple Servlet style security.
Consider my web.xml for a simple application:
<display-name>SimpleWeb</display-name>
<servlet>
<servlet-name>all</servlet-name>
<jsp-file>//WEB-INF//index.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>all</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<security-constraint>
<display-name>CompleteSecurityConstraint</display-name>
<web-resource-collection>
<web-resource-name>AllResources</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>Only authenticated users must proceed from here</description>
<role-name>USER</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>USER</role-name>
</security-role>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
As seen all urls to my new web site SimpleWeb have been redirected to the file index.jsp. I have added a security constraint to this url pattern too. The constraint requires that an authentication is performed for all urls who try to navigate to the site. Only users with role USER can visit the site. But where is the role configuration?
The security mechanism provided by Servlets is container dependent. As I am using a Tomcat servlet container, I shall provide the authentication details in tomcat. This is done via the tomcat-users.xml (located in the TOMCAT_HOME/conf folder)
<tomcat-users>
<role rolename="USER" />
<user username="robin" password="robin" roles="USER" />
</tomcat-users>
Here I have defined the role of type USER. I have also specified the credentials needed to authorize a client who claims to posess this role. If I run the application and simply try to navigate to, say http://localhost:8080/SimpleWeb/
Then I get a popup asking for credentials.
The popup was shown as the auth-method specified was BASIC. Only valid credentials will take me to index.jsp or allow my request to complete successfully.
I decided to try the same with Spring Security. I used the same application, but rather than use container
managed authentication and authorization, let Spring Security to do the tasks.
The first step was to update my web.xml file.
<display-name>SimpleWeb</display-name>
<servlet>
<servlet-name>all</servlet-name>
<jsp-file>/WEB-INF/index.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>all</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
</param-value>
</context-param>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
As seen all the security information has been stripped out. SimpleWeb is - as the name indicates - a simple web application. As there is no dispatcher servlet here, I need to ensure that my spring beans are loaded to the web context. So I had to provide the listener based configuration.
Any beans defined will be placed in spring-scurity.xml. As stated earlier, security from Spring comes through filters. So I decided to add my first filter:
<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>
As per documentation:
This provides a hook into the Spring Security web infrastructure. DelegatingFilterProxy is
a Spring Framework class which delegates to a filter implementation which is defined as
a Spring bean in your application context.
Accordingly I decided to add the beans:
<beans:beans xmlns="http://www.springframework.org/schema/security"
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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
</beans:beans>
This bean is equivalent to all the security code we had written in web.xml. It states that all URLs must be available only to users with role "USER" (In the role ROLE_USER, ROLE_ is only a marker used within Spring.)
Spring security also frees me from having to work with Tomcat for role management. Instead I added another bean:
<authentication-manager>
<authentication-provider>
<user-service>
<user name="robin" password="robin" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
The authentication-provider element here includes the credential and role information for my users. This will be used by the authentication manager to process authentication requests.
The jars needed for the project are:
If I run this code now, any attempt to access a resource will redirect me tohttp://localhost:8080/SimpleWeb/spring_security_login;jsessionid=01DD842C74C95A6380550F7FA2A5BD37
This is because the auto-config attribute when set to true uses form login. On submitting the form,
1. Spring processed it,
2. validated the credentials and on success,
3. redirected me to the correct page
If I want to use basic authentication as I did in the simple application then a slight tweaking is needed in the spring configuration:
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
<http-basic/>
</http>
The http-basic element will ensure that BASIC authentication is used. Thus with Spring security it is possible to even secure non spring applications.
Spring Security And Authorization
In a Java Web application, where authorization is involved, roles come into the picture. If a request is authorized to access a particular resource, then only will the Servlet Container allow it to work with the resource. How is the authorization decision made ?
Every existing user is assigned with one or many roles. This role assignment is closely associated with the authentication process used.For e.g. if I am using Tomcat managed authentication, then the details will be present in my tomcat-users.xml file:
<tomcat-users>
<role rolename="tomcat" />
<role rolename="ACCOUNT_HOLDER" />
<role rolename="Admin" />
<user username="tomcat" password="tomcat" roles="tomcat" />
<user username="robin" password="robin" roles="ACCOUNT_HOLDER,Admin" />
</tomcat-users>
When a request is received with user credentials, Tomcat will
1. Authenticate the user.
2. Note the roles assigned to it.
When the request now tries to refer to a secured resource, Tomcat checks if the user's role authorizes him to access the resource. If yes, then the resource is made available to him. For e.g.:
<security-constraint>
<display-name>CompleteSecurityConstraint</display-name>
<web-resource-collection>
<web-resource-name>AllResources</web-resource-name>
<url-pattern>/account/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>Only authenticated users must proceed from here</description>
<role-name>ACCOUNT_HOLDER</role-name>
</auth-constraint>
</security-constraint>
The above fragment is from web.xml. Only users with role ACCOUNT_HOLDER can access urls of the form /account/*
Can we check a user's role programmatically? Consider my servlet code:
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// if (user in role account) {
// execute the update method
// } else {
// return a read only view
// }
}
For such scenarios, the Servlet API provides role details within the request
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
Principal principal = request.getUserPrincipal();
PrintWriter printWriter = response.getWriter();
printWriter.write("<html><head></head><body>");
// if user in role account execute the update method
if (request.isUserInRole("ACCOUNT_HOLDER")) {
// do logic
printWriter.write("Allowed to update account details<br/><br/>");
} else {
printWriter.write("Account read only view !! <br/><br/>");
}
printWriter.write("<br/>Principal.getName() " + principal.getName()
+ " <br/> Principal Class " + principal.getClass()
+ " <br/> Principal " + principal + "</body></html>");
}
The request includes a isUserInRole method that checks against a role name. Running the above code for user robin would display the message on screen:
Allowed to update account details
Principal.getName() robin
Principal Class class org.apache.catalina.users.MemoryUser
Principal User username="robin", roles="ACCOUNT_HOLDER,Admin"
As seen the Principal represents an authorized user within the system. The user is associated with the system via a request. Hence the request holds the information on the principal. How does Spring manage this information ?
The most fundamental object is SecurityContextHolder. From the spring docs:
This is where we store details of the present security context of the application, which includes details of the principal currently using the application. By default the SecurityContextHolder uses a ThreadLocal to store these details, which means that the security context is always available to methods in the same thread of execution, even if the security context is not explicitly passed around as an argument to those methods.
public void processRequest(HttpServletRequest request,
HttpServletResponse response) throws IOException {
PrintWriter printWriter = response.getWriter();
printWriter.write("<html><head></head><body>");
String username = "-";
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
}
if (request instanceof SecurityContextHolderAwareRequestWrapper) {
SecurityContextHolderAwareRequestWrapper requestWrapper =
(SecurityContextHolderAwareRequestWrapper) request;
if (requestWrapper.isUserInRole("ACCOUNT_HOLDER")) {
// do logic
printWriter.write("Allowed to update account details <br/><br/>");
} else {
printWriter.write("Account read only view !! <br/><br/>");
}
}
printWriter.write("<br/>Principal.getName() " + username
+ " <br/> Principal Class " + principal.getClass()
+ " <br/> Principal " + principal + "</body></html>");
}
The code pretty much does the same thing as the earlier servlet. For users we used the authentication-provider element to list out the user credentials and roles.
The security information has been obtained in a different manner.
1. As the documentation suggested we retrieve the SecurityContext for the current thread.
2. To obtain the principal object we retrieved the Authentication instance.
3. The authorization information is actually retrieved form the ServletRequest wrapper.
If I run the code the screen would display:
Allowed to update account details
Principal.getName() robin
Principal Class class org.springframework.security.core.userdetails.User
Principal org.springframework.security.core.userdetails.User@67a69aa:
Username: robin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true;
credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ACCOUNT_HOLDER
As can be seen, Spring's Authroization method and classes seem to resemble the Container managed security. But Spring is not using Container managed security. It is actually using a combination of filters, wrapper classes and information saved in session to manage security.