Mixing GET and POST request data

Are designs that mix GET and POST requests inherently flawed?

It’s common for J2EE developers to create designs that pass both GET and POST requests through a centralized processing pipe, as in the controller method below:

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet {
	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		controller(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) {
		controller(request, response);
	}

	private void controller(HttpServletRequest request, HttpServletResponse response) {
		// process the request
	}
}


I believe this design is chosen for several reasons:

  • It’s argued that having a single processor is simpler than having two or more.
  • It’s believed that creating one method that can handle multiple actions is elegant.
  • It’s believed that separating doGet and doPost does no good because the HttpServletRequest object still contains both GET and POST data.

These points are valid, and the third bullet is very important to understand. Yet if you put on a security hat, the real issue involves understanding under what conditions (how and when) these methods are invoked and determining if mixing GET/POST weakens the design. Zeller and Felton recently wrote a paper describing real-world CSRF attacks. When discussing server-side protection, they reference an interesting part of the HTTP/1.1 specification (9.1.1 Safe Methods), which says:

In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered “safe”.

So, designs that mix GET and POST requests apparently violate the specification. Does this mean you should run off and carry out a substantive re-design effort? Will this design choice always lead to security problems? When proper protection mechanisms are implemented, the answer is no. However, when a design lacks sufficient protection mechanisms, mixing GET and POST requests may make it easier for an attack, such as CSRF, to succeed.

Zeller and Felton point out that if data from GET requests is only used for retrieval purposes, an entire class (and the easiest to do) of CSRF attacks is eliminated. More specifically, attacks that are able to inject <img> tags with a malicious HREF (i.e., src) won’t succeed:

When the browser parses this line, it makes a GET request using the src attribute. If the URL parameters (i.e., accountFrom, accountTo, amount, description) are used to modify records in the DB, then we’re in trouble. If they’re only used to “get” things, then we should be fine. Here, it’s obvious that this request performs a bank transfer and is therefore modifying DB data. Game over.

Had we separated the doGet and doPost logic like the HTTP/1.1 spec says, this particular attack would not have succeeded. IMHO, separating GET/POST requests in big apps would be very difficult and costly. It may not be worth the effort. However, if you’re so inclined, one way to abide by the HTTP/1.1 spec is to:

  1. Perform all DB queries within doGet using database accounts with only read permission on tables
  2. Perform all DB queries within doPost using database accounts with read and write permission on tables (least privilege is best)

If you do this cleanly, you may even be able to use static analysis tools to enforce these requirements.

Best practices for staying out of trouble

In big apps, it’s probably not worth the effort to cleanly separate GET and POST request processing. In fact, if your design makes use of the Apache Struts framework, then you must learn to cope with it because inside a Struts Action, there’s no way to tell if the request type is GET or POST. To stay out of trouble, try to remember the following best practices:

  • Don’t trust any data that’s in the request; if you do, you’re bound to fall prey to client-side trust issues.
  • Don’t try to handle URL parameters or form fields differently; treat all request data the same.
  • Always pass request data through an input validation chokepoint (that preferably performs whitelisting or a combo of whitelist and blacklist validation).
  • Always pass request data through an output validation chokepoint (that safely encodes the data to the output context).
  • To prevent CSRF attacks, ensure that all transaction-based requests require a strong request token.

Post a Comment

Your email is never published nor shared. Required fields are marked *