How To Implement Input Validation For REST resources
How To Implement Input Validation For REST resources
The SaaS platform I’m working on has a RESTful interface that accepts XML payloads.
Implementing REST Resources
For a Java shop like us, it makes sense to use JAX-B to generate JavaBean classes from an XML Schema. Working with XML (and JSON) payloads using JAX-B is very easy in a JAX-RS environment like Jersey:
@Path("orders") public class OrdersResource { @POST @Consumes({ "application/xml", "application/json" }) public void place(Order order) { // Jersey marshalls the XML payload into the Order // JavaBean, allowing us to write type-safe code // using Order's getters and setters. int quantity = order.getQuantity(); // ... } }
(Note that you shouldn’t use these generic media types, but that’s a discussion for another day.)
The remainder of this post assumes JAX-B, but its main point is valid for other technologies as well. Whatever you do, please don’t use XMLDecoder
, since that is open to a host of vulnerabilities.
Securing REST Resources
Let’s suppose the order’s quantity
is used for billing, and we want to prevent people from stealing our money by entering a negative amount.
We can do that with input validation, one of the most important tools in the AppSec toolkit. Let’s look at some ways to implement it.
Input Validation With XML Schema
We could rely on XML Schema for validation, but XML Schema can only validate so much.
Validating individual properties will probably work fine, but things get hairy when we want to validate relations between properties. For maximum flexibility, we’d like to use Java to express constraints.
More importantly, schema validation is generally not a good idea in a REST service.
A major goal of REST is to decouple client and server so that they can evolve separately.
If we validate against a schema, then a new client that sends a new property would break against an old server that doesn’t understand the new property. It’s usually better to silently ignore properties you don’t understand.
JAX-B does this right, and also the other way around: properties that are not sent by an old client end up as null
. Consequently, the new server must be careful to handle null
values properly.
Input Validation With Bean Validation
If we can’t use schema validation, then what about using JSR 303 Bean Validation?
Jersey supports Bean Validation by adding the jersey-bean-validation
jar to your classpath.
There is an unofficial Maven plugin to add Bean Validation annotations to the JAX-B generated classes, but I’d rather use something better supported and that works with Gradle.
So let’s turn things around. We’ll handcraft our JavaBean and generate the XML Schema from the bean for documentation:
@XmlRootElement(name = "order") public class Order { @XmlElement @Min(1) public int quantity; }
@Path("orders") public class OrdersResource { @POST @Consumes({ "application/xml", "application/json" }) public void place(@Valid Order order) { // Jersey recognizes the @Valid annotation and // returns 400 when the JavaBean is not valid } }
Any attempt to POST
an order with a non-positive quantity will now give a 400 Bad Request
status.
Now suppose we want to allow clients to change their pending orders. We’d use PATCH
or PUT
to update individual order properties, like quantity:
@Path("orders") public class OrdersResource { @Path("{id}") @PUT @Consumes("application/x-www-form-urlencoded") public Order update(@PathParam("id") String id, @Min(1) @FormParam("quantity") int quantity) { // ... } }
We need to add the @Min
annotation here too, which is duplication. To make this DRY, we can turn quantity
into a class that is responsible for validation:
@Path("orders") public class OrdersResource { @Path("{id}") @PUT @Consumes("application/x-www-form-urlencoded") public Order update(@PathParam("id") String id, @FormParam("quantity") Quantity quantity) { // ... } }
@XmlRootElement(name = "order") public class Order { @XmlElement public Quantity quantity; }
public class Quantity { private int value; public Quantity() { } public Quantity(String value) { try { setValue(Integer.parseInt(value)); } catch (ValidationException e) { throw new IllegalArgumentException(e); } } public int getValue() { return value; } @XmlValue public void setValue(int value) throws ValidationException { if (value < 1) { throw new ValidationException( "Quantity value must be positive, but is: " + value); } this.value = value; } }
We need a public no-arg constructor for JAX-B to be able to unmarshall the payload into a JavaBean and another constructor that takes a String
for the @FormParam
to work.
setValue()
throws javax.xml.bind.ValidationException
so that JAX-B will stop unmarshalling. However, Jersey returns a 500 Internal Server Error
when it sees an exception.
We can fix that by mapping validation exceptions onto 400
status codes using an exception mapper. While we’re at it, let’s do the same for IllegalArgumentException
:
@Provider public class DefaultExceptionMapper implements ExceptionMapper<Throwable> { @Override public Response toResponse(Throwable exception) { Throwable badRequestException = getBadRequestException(exception); if (badRequestException != null) { return Response.status(Status.BAD_REQUEST) .entity(badRequestException.getMessage()) .build(); } if (exception instanceof WebApplicationException) { return ((WebApplicationException)exception) .getResponse(); } return Response.serverError() .entity(exception.getMessage()) .build(); } private Throwable getBadRequestException( Throwable exception) { if (exception instanceof ValidationException) { return exception; } Throwable cause = exception.getCause(); if (cause != null && cause != exception) { Throwable result = getBadRequestException(cause); if (result != null) { return result; } } if (exception instanceof IllegalArgumentException) { return exception; } if (exception instanceof BadRequestException) { return exception; } return null; } }
Input Validation By Domain Objects
Even though the approach outlined above will work quite well for many applications, it is fundamentally flawed.
At first sight, proponents of Domain-Driven Design (DDD) might like the idea of creating the Quantity
class.
But the Order
and Quantity
classes do not model domain concepts; they model REST representations. This distinction may be subtle, but it is important.
DDD deals with domain concepts, while REST deals with representations of those concepts. Domain concepts are discovered, but representations are designed and are subject to all kinds of trade-offs.
For instance, a collection REST resource may use paging to prevent sending too much data over the wire. Another REST resource may combine several domain concepts to make the client-server protocol less chatty.
A REST resource may even have no corresponding domain concept at all. For example, a POST
may return 202 Accepted
and point to a REST resource that represents the progress of an asynchronous transaction.
Domain objects need to capture the ubiquitous language as closely as possible, and must be free from trade-offs to make the functionality work.
When designing REST resources, on the other hand, one needs to make trade-offs to meet non-functional requirements like performance, scalability, and evolvability.
That’s why I don’t think an approach like RESTful Objects will work. (For similar reasons, I don’t believe in Naked Objects for the UI.)
Adding validation to the JavaBeans that are our resource representations means that those beans now have two reasons to change, which is a clear violation of the Single Responsibility Principle.
We get a much cleaner architecture when we use JAX-B JavaBeans only for our REST representations and create separate domain objects that handle validation.
Putting validation in domain objects is what Dan Bergh Johnsson refers to as Domain-Driven Security.
In this approach, primitive types are replaced with value objects. (Some people even argue against using any String
s at all.)
At first it may seem overkill to create a whole new class to hold a single integer, but I urge you to give it a try. You may find that getting rid of primitive obsession provides value even beyond validation.
What do you think?
How do you handle input validation in your RESTful services? What do you think of Domain-Driven Security? Please leave a comment.
Hi
I didn’t understand this part:
>>> We get a much cleaner architecture when we use JAX-B JavaBeans only for our REST representations and create separate domain objects that handle validation
What is the meaning of “REST representation” and “domain objects that handle validation”
thanks.
Can you please explain how can i validate through XML schema or using XSD in RESTful.
thanks
http://stackoverflow.com/questions/15732/whats-the-best-way-to-validate-an-xml-file-against-an-xsd-file
Please note that I argue *against* using schema validation for messages in a RESTful system.
I am using SpringServlet with Jersey, I have added jersey-bean-validation and javax.ws.rs-api in build.gradle, still @NotNull annotation is not working, am i missing some config?