Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions user/src/com/google/gwt/user/server/rpc/RPCServletUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,40 @@ public class RPCServletUtils {
*/
static final int BUFFER_SIZE = 4096;

/**
* Name of the system property that overrides the maximum accepted request body size, in
* bytes. Values that are not a positive integer are ignored in favour of the default, so a
* malformed override cannot disable the cap.
*/
public static final String MAX_REQUEST_BODY_BYTES_PROPERTY = "gwt.rpc.maxRequestBodyBytes";

/**
* Default maximum request body size accepted by {@link #readContent}, in bytes.
*/
static final int DEFAULT_MAX_REQUEST_BODY_BYTES = 1024 * 1024;

/**
* Maximum number of bytes {@link #readContent} buffers from a request body before rejecting
* it. Without a ceiling the read loop buffers an arbitrarily large attacker-supplied body
* into memory and then duplicates it as a String, exhausting the heap (CWE-400).
*/
static final int MAX_REQUEST_BODY_BYTES = readMaxRequestBodyBytes();

private static int readMaxRequestBodyBytes() {
String override = System.getProperty(MAX_REQUEST_BODY_BYTES_PROPERTY);
if (override != null) {
try {
int parsed = Integer.parseInt(override.trim());
if (parsed > 0) {
return parsed;
}
} catch (NumberFormatException e) {
// Fall through to the default rather than leaving the body unbounded.
}
}
return DEFAULT_MAX_REQUEST_BODY_BYTES;
}

private static final String ACCEPT_ENCODING = "Accept-Encoding";

private static final String ATTACHMENT = "attachment";
Expand Down Expand Up @@ -201,7 +235,8 @@ public static boolean isExpectedException(Method serviceIntfMethod,
* from or closed
* @throws ServletException if the request's content type does not
* equal the supplied <code>expectedContentType</code> or
* <code>expectedCharSet</code>
* <code>expectedCharSet</code>, or if the request body exceeds
* {@link #MAX_REQUEST_BODY_BYTES}
*/
public static String readContent(HttpServletRequest request,
String expectedContentType, String expectedCharSet)
Expand All @@ -215,17 +250,31 @@ public static String readContent(HttpServletRequest request,

/*
* Need to support 'Transfer-Encoding: chunked', so do not rely on
* presence of a 'Content-Length' request header.
* presence of a 'Content-Length' request header. When the header is present and already
* over the limit, reject before reading any of the body; the accumulated-byte check below
* is what enforces the limit for chunked requests and for an understated header.
*/
int declaredLength = request.getContentLength();
if (declaredLength > MAX_REQUEST_BODY_BYTES) {
throw new ServletException("Request body of " + declaredLength
+ " bytes exceeds the maximum of " + MAX_REQUEST_BODY_BYTES + " bytes");
}

InputStream in = request.getInputStream();
byte[] buffer = new byte[BUFFER_SIZE];
ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
try {
int totalRead = 0;
while (true) {
int byteCount = in.read(buffer);
if (byteCount == -1) {
break;
}
totalRead += byteCount;
if (totalRead > MAX_REQUEST_BODY_BYTES) {
throw new ServletException("Request body exceeds the maximum of "
+ MAX_REQUEST_BODY_BYTES + " bytes");
}
out.write(buffer, 0, byteCount);
}
return new String(out.toByteArray(), getCharset(expectedCharSet));
Expand Down