6980004: limit HTTP request cookie headers in HttpURLConnection
authormichaelm
Thu Sep 23 08:50:17 2010 -0700 (2 years ago)
changeset 4446e389e6349c9
parent 44265605bd0da24
child 445c4573f15b0f8
6980004: limit HTTP request cookie headers in HttpURLConnection
6961084: limit setting of some request headers in HttpURLConnection
Reviewed-by: chegar
src/share/classes/sun/net/www/MessageHeader.java
src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
--- a/src/share/classes/sun/net/www/MessageHeader.java Thu Sep 09 14:46:12 2010 -0700
+++ b/src/share/classes/sun/net/www/MessageHeader.java Thu Sep 23 08:50:17 2010 -0700
@@ -196,6 +196,10 @@ class MessageHeader {
}
public synchronized Map getHeaders(String[] excludeList) {
+ return filterAndAddHeaders(excludeList, null);
+ }
+
+ public synchronized Map filterAndAddHeaders(String[] excludeList, Map include) {
boolean skipIt = false;
Map m = new HashMap();
for (int i = nkeys; --i >= 0;) {
@@ -220,6 +224,19 @@ class MessageHeader {
} else {
// reset the flag
skipIt = false;
+ }
+ }
+
+ if (include != null) {
+ Iterator entries = include.entrySet().iterator();
+ while (entries.hasNext()) {
+ Map.Entry entry = (Map.Entry)entries.next();
+ List l = (List)m.get(entry.getKey());
+ if (l == null) {
+ l = new ArrayList();
+ m.put((String)entry.getKey(), l);
+ }
+ l.add(entry.getValue());
}
}
--- a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Thu Sep 09 14:46:12 2010 -0700
+++ b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Thu Sep 23 08:50:17 2010 -0700
@@ -51,6 +51,9 @@ import java.util.Locale;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.Iterator;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Set;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -142,6 +145,56 @@ public class HttpURLConnection extends j
*/
private static int bufSize4ES = 0;
+ /*
+ * Restrict setting of request headers through the public api
+ * consistent with JavaScript XMLHttpRequest2 with a few
+ * exceptions. Disallowed headers are silently ignored for
+ * backwards compatibility reasons rather than throwing a
+ * SecurityException. For example, some applets set the
+ * Host header since old JREs did not implement HTTP 1.1.
+ * Additionally, any header starting with Sec- is
+ * disallowed.
+ *
+ * The following headers are allowed for historical reasons:
+ *
+ * Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date,
+ * Referer, TE, User-Agent, headers beginning with Proxy-.
+ *
+ * The following headers are allowed in a limited form:
+ *
+ * Connection: close
+ *
+ * See http://www.w3.org/TR/XMLHttpRequest2.
+ */
+ private static final boolean allowRestrictedHeaders;
+
+ private static final Set<String> restrictedHeaderSet;
+
+ private static final String[] restrictedHeaders = {
+ /* Restricted by XMLHttpRequest2 */
+ //"Accept-Charset",
+ //"Accept-Encoding",
+ "Access-Control-Request-Headers",
+ "Access-Control-Request-Method",
+ "Connection", /* close is allowed */
+ "Content-Length",
+ //"Cookie",
+ //"Cookie2",
+ "Content-Transfer-Encoding",
+ //"Date",
+ "Expect",
+ "Host",
+ "Keep-Alive",
+ "Origin",
+ // "Referer",
+ // "TE",
+ "Trailer",
+ "Transfer-Encoding",
+ "Upgrade",
+ //"User-Agent",
+ "Via"
+ };
+
static {
maxRedirects = java.security.AccessController.doPrivileged(
new sun.security.action.GetIntegerAction("http.maxRedirects",
@@ -180,7 +233,17 @@ public class HttpURLConnection extends j
bufSize4ES = 4096; // use the default
}
-
+ allowRestrictedHeaders = ((Boolean)java.security.AccessController.doPrivileged(
+ new sun.security.action.GetBooleanAction(
+ "sun.net.http.allowRestrictedHeaders"))).booleanValue();
+ if (!allowRestrictedHeaders) {
+ restrictedHeaderSet = new HashSet<String>(restrictedHeaders.length);
+ for (int i=0; i < restrictedHeaders.length; i++) {
+ restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase());
+ }
+ } else {
+ restrictedHeaderSet = null;
+ }
}
static final String httpVersion = "HTTP/1.1";
@@ -193,6 +256,15 @@ public class HttpURLConnection extends j
"Proxy-Authorization",
"Authorization"
};
+
+ // also exclude system cookies when any might be set
+ private static final String[] EXCLUDE_HEADERS2= {
+ "Proxy-Authorization",
+ "Authorization",
+ "Cookie",
+ "Cookie2"
+ };
+
protected HttpClient http;
protected Handler handler;
protected Proxy instProxy;
@@ -215,6 +287,7 @@ public class HttpURLConnection extends j
/* User set Cookies */
private boolean setUserCookies = true;
private String userCookies = null;
+ private String userCookies2 = null;
/* We only have a single static authenticator for now.
* REMIND: backwards compatibility with JDK 1.1. Should be
@@ -300,6 +373,41 @@ public class HttpURLConnection extends j
prompt, scheme, url, authType);
}
});
+ }
+
+ private boolean isRestrictedHeader(String key, String value) {
+ if (allowRestrictedHeaders) {
+ return false;
+ }
+
+ key = key.toLowerCase();
+ if (restrictedHeaderSet.contains(key)) {
+ /*
+ * Exceptions to restricted headers:
+ *
+ * Allow "Connection: close".
+ */
+ if (key.equals("connection") && value.equalsIgnoreCase("close")) {
+ return false;
+ }
+ return true;
+ } else if (key.startsWith("sec-")) {
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * Checks the validity of http message header and whether the header
+ * is restricted and throws IllegalArgumentException if invalid or
+ * restricted.
+ */
+ private boolean isExternalMessageHeaderAllowed(String key, String value) {
+ checkMessageHeader(key, value);
+ if (!isRestrictedHeader(key, value)) {
+ return true;
+ }
+ return false;
}
/*
@@ -917,15 +1025,21 @@ public class HttpURLConnection extends j
// we only want to capture the user defined Cookies once, as
// they cannot be changed by user code after we are connected,
// only internally.
- if (setUserCookies) {
- int k = requests.getKey("Cookie");
- if ( k != -1)
- userCookies = requests.getValue(k);
- setUserCookies = false;
+ synchronized (this) {
+ if (setUserCookies) {
+ int k = requests.getKey("Cookie");
+ if ( k != -1)
+ userCookies = requests.getValue(k);
+ k = requests.getKey("Cookie2");
+ if ( k != -1)
+ userCookies2 = requests.getValue(k);
+ setUserCookies = false;
+ }
}
// remove old Cookie header before setting new one.
requests.remove("Cookie");
+ requests.remove("Cookie2");
URI uri = ParseUtil.toURI(url);
if (uri != null) {
@@ -966,6 +1080,13 @@ public class HttpURLConnection extends j
requests.set("Cookie", requests.getValue(k) + ";" + userCookies);
else
requests.set("Cookie", userCookies);
+ }
+ if (userCookies2 != null) {
+ int k;
+ if ((k = requests.getKey("Cookie2")) != -1)
+ requests.set("Cookie2", requests.getValue(k) + ";" + userCookies2);
+ else
+ requests.set("Cookie2", userCookies2);
}
} // end of getting cookies
@@ -2164,8 +2285,9 @@ public class HttpURLConnection extends j
if (key == null)
throw new NullPointerException ("key is null");
- checkMessageHeader(key, value);
- requests.set(key, value);
+ if (isExternalMessageHeaderAllowed(key, value)) {
+ requests.set(key, value);
+ }
}
/**
@@ -2185,8 +2307,9 @@ public class HttpURLConnection extends j
if (key == null)
throw new NullPointerException ("key is null");
- checkMessageHeader(key, value);
- requests.add(key, value);
+ if (isExternalMessageHeaderAllowed(key, value)) {
+ requests.add(key, value);
+ }
}
//
@@ -2199,14 +2322,24 @@ public class HttpURLConnection extends j
}
public String getRequestProperty (String key) {
+ if (key == null) {
+ return null;
+ }
// don't return headers containing security sensitive information
- if (key != null) {
- for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
- if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
- return null;
- }
- }
- }
+ for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
+ if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
+ return null;
+ }
+ }
+ if (!setUserCookies) {
+ if (key.equalsIgnoreCase("Cookie")) {
+ return userCookies;
+ }
+ if (key.equalsIgnoreCase("Cookie2")) {
+ return userCookies2;
+ }
+ }
+
return requests.findValue(key);
}
@@ -2222,12 +2355,29 @@ public class HttpURLConnection extends j
* @throws IllegalStateException if already connected
* @since 1.4
*/
- public Map getRequestProperties() {
+ public synchronized Map getRequestProperties() {
if (connected)
throw new IllegalStateException("Already connected");
// exclude headers containing security-sensitive info
- return requests.getHeaders(EXCLUDE_HEADERS);
+ if (setUserCookies) {
+ return requests.getHeaders(EXCLUDE_HEADERS);
+ }
+ /*
+ * The cookies in the requests message headers may have
+ * been modified. Use the saved user cookies instead.
+ */
+ Map userCookiesMap = null;
+ if (userCookies != null || userCookies2 != null) {
+ userCookiesMap = new HashMap();
+ if (userCookies != null) {
+ userCookiesMap.put("Cookie", userCookies);
+ }
+ if (userCookies2 != null) {
+ userCookiesMap.put("Cookie2", userCookies2);
+ }
+ }
+ return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
}
public void setConnectTimeout(int timeout) {