Showing posts with label Struts 2. Show all posts
Showing posts with label Struts 2. Show all posts

Monday, May 4, 2009

OWASP ISWG: Struts 2/WebWork Gap Analysis

Arshan Dabirsiaghi recently published "A Gap Analysis of Application Security in Struts2/WebWork" for the OWASP Intrinsic Security Working Group. The paper evaluates the security controls/features that are either built into Struts 2 or can be added by extending the framework.

I had the opportunity to contribute research and code to this paper. The appendix section contains several code examples showing how one might:
  • Create an authentication interceptor
  • Create a roles interceptor (Enforced page-level access controls based on a user's privilege level)
  • Create a caching headers interceptor
  • Prevent CSRF vulnerabilities using the built in tokenSession Interceptor
  • Implement a custom error handler
  • Create an interceptor that enforces SSL
  • Regenerate session IDs when users cross an authentication boundary
The paper can be found here:
http://www.owasp.org/index.php/Image:A_Gap_Analysis_of_Application_Security_in_Struts2.pdf

The code repository containing updated struts 2 modules can be found below:

http://code.google.com/p/struts2securityaddons/

Additionally, you can see discussion of these modules in my earlier blog posts:

Friday, December 19, 2008

Page-Level Access Controls in Struts 2 - Part 2

Background

Applications should ensure users are properly authenticated and have sufficient permissions to access pages before content is displayed. Page-level access controls are one security control that enforces this behavior.

Part 1 and 2 of this article describes one of many ways to implement a Struts 2 AuthenticationInterceptor and a RolesInterceptor to verify users have authenticated successfully and belong to an approved role before allowing access to pages. The key features targeted by both interceptors include a default deny policy and a centralized location for defining access control rules.

Struts 2 RolesInterceptor

In Part 2 of the page-level access controls article, a RolesInterceptor has been added to the struts.xml file. The "roleActions" parameter, passed to the interceptor, contains a list of actions allowed for each role. The "*" role indicates that any role can access the action.

Struts.xml

(Click the image above to view the XML)

Similar to the AuthenticationInterceptor, the RolesInterceptor verifies all users are allowed access to a particular page or validates that the user's "role" session variable matches one of the roles allowed for the action requested.

RolesInterceptor
(Click the image above to view the code)

In the ProcessSimpleLogin action, the "role" session variable has been added to include the name of the role that the user belongs to.

ProcessSimpleLogin

(Click the image above to view the code)

Thursday, November 20, 2008

Page-Level Access Controls in Struts 2 - Part 1

Background

Applications should ensure users are properly authenticated and have sufficient permissions to access pages before content is displayed. Page-level access controls are one security control that enforces this behavior.

Part 1 and 2 of this article describes one of many ways to implement a Struts 2 AuthenticationInterceptor and a RolesInterceptor to verify users have authenticated successfully and belong to an approved role before allowing access to pages. The key features targeted by both interceptors include a default deny policy and a centralized location for defining access control rules.

Struts 2 AuthenticationInterceptor

In the struts.xml file below, the AuthenticationInterceptor is defined and included in the defaultSecurityStackWithAuthentication. The "excludeActions" parameter provided to the interceptor lists the actions that do not require users to be authenticated. In this case, the "Login" and "ProcessSimpleLogin" actions do not require authentication, however the "Internal" page does require authentication.

Struts.xml

(Click the image above to view the XML)

When the AuthenticationInterceptor is called, the interceptor verifies the requested action is in the exclude list or the user has the "authenticated" session variable set to "True."

AuthenticationInterceptor

(Click the image above to view the code)

Finally, to allow users access to authenticated pages, the ProcessSimpleLogin action verifies the submitted credentials and then sets the "authenticated" session variable to "True."

ProcessSimpleLogin

(Click the image above to view the code)

The code repository containing updated struts 2 modules can be found below:

http://code.google.com/p/struts2securityaddons/

Additionally, you can see discussion of these modules in my earlier blog posts:

Monday, November 10, 2008

CSRF Prevention in Struts 2

Background

Cross-site request forgery, one of the OWASP Top 10 vulnerabilities for 2007, is an attack in which a malicious user causes a victim's browser to make a request without the user's consent. This attack is generally propagated by a third party site.

The example below shows how a CSRF attack might affect a web application that allows customers to request rental movies to be mailed to their house.

A customer logs into her movie rental web site and selects some movies to add to her queue. Next, instead of logging out of the application, she types in the address for her favorite news site, reads a few online comics, and does some research on used cars.

Unfortunately, before the customer began her research, an attacker had discovered a persistent cross-site scripting vulnerability on one of the used car sites. The attacker had also exploited this vulnerability to include a simple image tag containing a URL similar to the one below:

<img src="http://www.fakemovierentalsite.com/addMovieToQueueBeginning?movieId=12345" />

When this image tag loaded in the customer's browser, a request was sent to the movie rental application to add an embarrassing movie to the beginning of the customer's queue.

One strategy to address CSRF attacks is to require and validate one-time values included in requests to sensitive functionality. For more information, please view the OWASP explanation found here.

Struts 2 tokenSessionInterceptor

The tokenSessionInterceptor, provided by struts 2, allows developers to add CSRF protection quite easily. In the example below, the tokenSessionInterceptor was added to the interceptor stack. A parameter has been passed to the interceptor to ensure it will not be triggered on each request.

In the ProcessSimpleLogin action, the tokenSessionInterceptor is referenced again. In this case, a parameter is passed to the interceptor to ensure it verifies a valid token has been sent for this action only.


(Click the image above to view the XML)

In order to include the proper token within the page, the <s:token /> tag is included as shown below.

(Click the image above to view the code)

This strategy ensures the ProcessSimpleLogin action executes only if a one-time token has been associated with the user's session, the request includes this token, and the token has been used only one time.

The code repository containing updated struts 2 modules can be found below:

http://code.google.com/p/struts2securityaddons/

Additionally, you can see discussion of these modules in my earlier blog posts:

Sunday, November 2, 2008

Custom Error Pages in Struts 2

Background

When attackers target a particular application, they typically spend some time gathering information about the application's components, framework, and architecture. One way attackers may gather this type of information is through error messages.

Error messages often disclose SQL queries, code fragments, file names, or other sensitive information. An example is shown below.

Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3 xx.xx.xx.xx 10/18/08 17:49:20 clientid=&id= http://www.google.com/search?q=

[Microsoft][ODBC SQL Server Driver][SQL Server]Line 1: Incorrect syntax near '='.

SQL = "SELECT * FROM logins WHERE clientid ="

Data Source = "AFE2003"

The error occurred while processing an element with a general identifier of (CFQUERY), occupying document position (40:2) to (40:47) in the template file E:\inetpub\i\somesite\showinv\Application.cfm.

The disclosure of this information can be avoided using custom error pages. Applications, frameworks, or servers can be configured to redirect users to a custom error page that does not disclose stack traces, debugging information, or verbose error messages.

Struts 2 Custom Error Pages

Struts 2 includes an Exception Interceptor in its default stack. Developers can utilize this interceptor to catch errors and redirect users to a page containing a generic error message. One example is shown below.

Custom Error Page

(Click the image above to view the code)
Struts.xml


(Click the image above to see the XML)
Result



The code repository containing updated struts 2 modules can be found below:

http://code.google.com/p/struts2securityaddons/

Additionally, you can see discussion of these modules in my earlier blog posts:

Sunday, October 19, 2008

SSL/TLS in Struts 2

Background

SSL/TLS provides an encrypted communication channel that a client and server can use to exchange messages without an attacker eavesdropping or manipulating data in transit. This is an important security control to include within a web application to ensure attackers cannot steal user's authentication session cookies or observe user's credentials as they are transmitted to the server.

Web applications and their environments should ensure users can only access sensitive applications over an encrypted communication channel (https for example). Firewalls, application servers, or applications themselves can enforce this behavior.

Requiring SSL/TLS in Struts 2


One way to ensure users connect using SSL or TLS within struts is to create an interceptor to verify this connection. An example has been provided below.


SSLRequired.jsp

(Click the image to view the code)

RequireSSLInterceptor

(Click the image to view the code)

Struts.xml

Saturday, October 18, 2008

HTTP Caching Headers in Struts 2

Background

In many cases, the users' web browsing experience can be made more efficient by allowing web browsers to cache pages, images, scripts, and other content. This allows the web browser to retrieve content from the local disk rather than requesting data from the server every time. The end result is a quicker, more responsive user interface.

While this strategy is great for applications that handle public, non-sensitive information, it may not be appropriate for banking, investment, health care, or other similar applications. In general, applications that contain sensitive or confidential information should have controls that reduce the likelihood of information being disclosed to unauthorized individuals.

One way web applications can reduce the likelihood of browsers disclosing sensitive data through caching is to include the following HTTP headers within the server's response.

Cache-control: no-cache, no-store
Pragma: no-cache
Expires: -1
For more information, please see the OWASP AppSec FAQ.


Struts Interceptor Implementation

One way these headers can be included within a Struts 2 application is to create a custom interceptor. An example has been provided below.

CachingHeadersInterceptor Code

(Click the image above to view the code)

Struts.xml

(Click the image above to view the XML)

Results
Server: Apache-Coyote/1.1
Cache-Control: no-cache, no-store
Pragma: no-cache
Expires: -1
Content-Type: text/html
Content-Length: 157
Date: Sat, 18 Oct 2008 18:37:35 GMT

200 OK

The code repository containing updated struts 2 modules can be found below:

http://code.google.com/p/struts2securityaddons/

Additionally, you can see discussion of these modules in my earlier blog posts:

Thursday, September 4, 2008

JSESSIONID Regeneration in Struts 2

Background

Whenever a user crosses an authentication boundary, the user's session ID should be regenerated. This concept applies to a user logging into an application, logging out, or when a user reauthenticates due to a risk-based authentication process. The regeneration of session IDs is an important practice that helps eliminate session fixation vulnerabilities and may limit the impact of session theft vulnerabilities prior to authentication.

For more information on Session Fixation vulnerabilities and Session ID regeneration practices, please see the OWASP pages below:

http://www.owasp.org/index.php/Session_Fixation
http://www.owasp.org/index.php/Session_Management#Regeneration_of_Session_Tokens

Session ID Regeneration in Traditional Java Web Applications

In a J2EE application, the user's JSESSIONID cookie should be regenerated and the previous session should be removed or deleted from the server. Example code below shows how this might be accomplished in a traditional Java web application.
public class ExampleLoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if( //authentication was successful ) {
request.getSession().invalidate();
HttpSession session = request.getSession(true);
session.setAttribute("AUTHENTICATED", new Boolean(true));
response.sendRedirect("PageRequiringAuthentication.jsp");
//Additional Code Would Normally Follow
Session ID Regeneration in Struts 2 Applications

In Struts 2 applications, developers typically don't directly interact with the HttpServletRequest, HTTPServletResponse, or HttpSession objects. With consideration of these factors, the solution discussed above for a traditional Java web application may not be appropriate for Struts 2.

I did a little research and through trial an error I came up with a Struts 2 specific solution for regenerating JSESSIONIDs. This solution utilizes the SessionAware interface. Please excuse the unrealistic authentication code...
package nickcoblentzblog.actions.sessions;

import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.dispatcher.SessionMap;

public class Login extends ActionSupport implements SessionAware  {
private String userid;
private String password;
private Map session;

public String execute() {
if(userid.equals("admin") && password.equals("admin"))  {

/* Session ID Regeneration: Try #4 */
((SessionMap)this.session).invalidate();
this.session = ActionContext.getContext().getSession();
/* End Try #4 */

session.put("AUTHENTICATED", new Boolean(true));


return SUCCESS;
}
else
return ERROR;
}
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}

public void setSession(Map session) {
this.session = session;
}
}
To test this code, I followed the following procedure.

1. Cleared all browser cookies
2. Visited the Login JSP page
3. Used the Web Developer Toolbar to view my initial JSESSIONID
4. Logged into the application successfully
5. Used the Web Developer Toolbar to view my final JSESSIONID

The initial JSESSIONID value was:
AA4996C5E24BB8221BB27B23EA599F34

The final JSESSIONID value was:
325ED18851B93EBA542D2AE7926E7F8E

Based on these tests this solution appears to work successfully.

In case anyone is curious, here are a couple other ideas I toyed with:
/* Try # 1:
this.request.getSession().invalidate();
this.request.getSession(true);
*/

/* Try #2:
HTTPUtilities esapiHTTPUtilities = ESAPI.httpUtilities();
esapiHTTPUtilities.setCurrentHTTP(request, response);
try {
esapiHTTPUtilities.changeSessionIdentifier();
}
catch(Exception e) {
e.printStackTrace();
}
*/

/* Try #3:
((SessionMap)ActionContext.getContext().getSession()).invalidate();
*/


The code repository containing updated struts 2 modules can be found below:

http://code.google.com/p/struts2securityaddons/

Additionally, you can see discussion of these modules in my earlier blog posts: