HttpOnly.java revision 14606:bc3775e25b52
1/* 2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23/** 24 * @test 25 * @bug 7095980 8007315 26 * @modules jdk.httpserver 27 * @summary Ensure HttpURLConnection (and supporting APIs) don't expose 28 * HttpOnly cookies 29 */ 30 31import java.io.IOException; 32import java.net.CookieHandler; 33import java.net.CookieManager; 34import java.net.CookiePolicy; 35import java.net.InetAddress; 36import java.net.InetSocketAddress; 37import java.net.URI; 38import java.net.HttpURLConnection; 39import java.util.ArrayList; 40import java.util.HashMap; 41import java.util.List; 42import java.util.Map; 43import java.util.Set; 44import com.sun.net.httpserver.Headers; 45import com.sun.net.httpserver.HttpExchange; 46import com.sun.net.httpserver.HttpHandler; 47import com.sun.net.httpserver.HttpServer; 48 49/* 50 * 1) start the HTTP server 51 * 2) populate cookie store with HttpOnly cookies 52 * 3) make HTTP request that should contain HttpOnly cookies 53 * 4) check HttpOnly cookies received by server 54 * 5) server reply with Set-Cookie containing HttpOnly cookie 55 * 6) check HttpOnly cookies are not accessible from Http client 56 * 7) check that non-null (empty string) values are returned for 57 scenario where all values are stripped from original key values 58 */ 59 60public class HttpOnly { 61 62 static final String URI_PATH = "/xxyyzz/"; 63 static final int SESSION_ID = 12345; 64 65 void test(String[] args) throws Exception { 66 HttpServer server = startHttpServer(); 67 CookieHandler previousHandler = CookieHandler.getDefault(); 68 try { 69 InetSocketAddress address = server.getAddress(); 70 URI uri = new URI("http://" + InetAddress.getLocalHost().getHostAddress() 71 + ":" + address.getPort() + URI_PATH); 72 populateCookieStore(uri); 73 doClient(uri); 74 } finally { 75 CookieHandler.setDefault(previousHandler); 76 server.stop(0); 77 } 78 } 79 80 void populateCookieStore(URI uri) 81 throws IOException { 82 83 CookieManager cm = new CookieManager(null, CookiePolicy.ACCEPT_ALL); 84 CookieHandler.setDefault(cm); 85 Map<String,List<String>> header = new HashMap<>(); 86 List<String> values = new ArrayList<>(); 87 values.add("JSESSIONID=" + SESSION_ID + "; version=1; Path=" 88 + URI_PATH +"; HttpOnly"); 89 values.add("CUSTOMER=WILE_E_COYOTE; version=1; Path=" + URI_PATH); 90 header.put("Set-Cookie", values); 91 cm.put(uri, header); 92 } 93 94 void doClient(URI uri) throws Exception { 95 HttpURLConnection uc = (HttpURLConnection) uri.toURL().openConnection(); 96 int resp = uc.getResponseCode(); 97 check(resp == 200, 98 "Unexpected response code. Expected 200, got " + resp); 99 100 // TEST 1: check getRequestProperty doesn't return the HttpOnly cookie 101 // In fact, that it doesn't return any automatically set cookies. 102 String cookie = uc.getRequestProperty("Cookie"); 103 check(cookie == null, 104 "Cookie header returned from getRequestProperty, value " + cookie); 105 106 // TEST 2: check getRequestProperties doesn't return the HttpOnly cookie. 107 // In fact, that it doesn't return any automatically set cookies. 108 Map<String,List<String>> reqHeaders = uc.getRequestProperties(); 109 Set<Map.Entry<String,List<String>>> entries = reqHeaders.entrySet(); 110 for (Map.Entry<String,List<String>> entry : entries) { 111 String header = entry.getKey(); 112 check(!"Cookie".equalsIgnoreCase(header), 113 "Cookie header returned from getRequestProperties, value " + 114 entry.getValue()); 115 } 116 117 // TEST 3: check getHeaderField doesn't return Set-Cookie with HttpOnly 118 String setCookie = uc.getHeaderField("Set-Cookie"); 119 if (setCookie != null) { 120 debug("Set-Cookie:" + setCookie); 121 check(!setCookie.toLowerCase().contains("httponly"), 122 "getHeaderField returned Set-Cookie header with HttpOnly, " + 123 "value = " + setCookie); 124 } 125 126 // TEST 3.5: check getHeaderField doesn't return Set-Cookie2 with HttpOnly 127 String setCookie2 = uc.getHeaderField("Set-Cookie2"); 128 if (setCookie2 != null) { 129 debug("Set-Cookie2:" + setCookie2); 130 check(!setCookie2.toLowerCase().contains("httponly"), 131 "getHeaderField returned Set-Cookie2 header with HttpOnly, " + 132 "value = " + setCookie2); 133 } 134 135 // TEST 4: check getHeaderFields doesn't return Set-Cookie 136 // or Set-Cookie2 headers with HttpOnly 137 Map<String,List<String>> respHeaders = uc.getHeaderFields(); 138 Set<Map.Entry<String,List<String>>> respEntries = respHeaders.entrySet(); 139 for (Map.Entry<String,List<String>> entry : respEntries) { 140 String header = entry.getKey(); 141 if ("Set-Cookie".equalsIgnoreCase(header)) { 142 List<String> setCookieValues = entry.getValue(); 143 debug("Set-Cookie:" + setCookieValues); 144 for (String value : setCookieValues) 145 check(!value.toLowerCase().contains("httponly"), 146 "getHeaderFields returned Set-Cookie header with HttpOnly, " 147 + "value = " + value); 148 } 149 if ("Set-Cookie2".equalsIgnoreCase(header)) { 150 List<String> setCookieValues = entry.getValue(); 151 debug("Set-Cookie2:" + setCookieValues); 152 for (String value : setCookieValues) 153 check(!value.toLowerCase().contains("httponly"), 154 "getHeaderFields returned Set-Cookie2 header with HttpOnly, " 155 + "value = " + value); 156 } 157 } 158 159 // Now add some user set cookies into the mix. 160 uc = (HttpURLConnection) uri.toURL().openConnection(); 161 uc.addRequestProperty("Cookie", "CUSTOMER_ID=CHEGAR;"); 162 resp = uc.getResponseCode(); 163 check(resp == 200, 164 "Unexpected response code. Expected 200, got " + resp); 165 166 // TEST 5: check getRequestProperty doesn't return the HttpOnly cookie 167 cookie = uc.getRequestProperty("Cookie"); 168 check(!cookie.toLowerCase().contains("httponly"), 169 "HttpOnly cookie returned from getRequestProperty, value " + cookie); 170 171 // TEST 6: check getRequestProperties doesn't return the HttpOnly cookie. 172 reqHeaders = uc.getRequestProperties(); 173 entries = reqHeaders.entrySet(); 174 for (Map.Entry<String,List<String>> entry : entries) { 175 String header = entry.getKey(); 176 if ("Cookie".equalsIgnoreCase(header)) { 177 for (String val : entry.getValue()) 178 check(!val.toLowerCase().contains("httponly"), 179 "HttpOnly cookie returned from getRequestProperties," + 180 " value " + val); 181 } 182 } 183 184 // TEST 7 : check that header keys containing empty key values don't return null 185 int i = 1; 186 String key = ""; 187 String value = ""; 188 189 while (true) { 190 key = uc.getHeaderFieldKey(i); 191 value = uc.getHeaderField(i++); 192 if (key == null && value == null) 193 break; 194 195 if (key != null) 196 check(value != null, 197 "Encountered a null value for key value : " + key); 198 } 199 200 // TEST 7.5 similar test but use getHeaderFields 201 respHeaders = uc.getHeaderFields(); 202 respEntries = respHeaders.entrySet(); 203 for (Map.Entry<String,List<String>> entry : respEntries) { 204 String header = entry.getKey(); 205 if (header != null) { 206 List<String> listValues = entry.getValue(); 207 for (String value1 : listValues) 208 check(value1 != null, 209 "getHeaderFields returned null values for header:, " 210 + header); 211 } 212 } 213 } 214 215 // HTTP Server 216 HttpServer startHttpServer() throws IOException { 217 HttpServer httpServer = HttpServer.create(new InetSocketAddress(0), 0); 218 httpServer.createContext(URI_PATH, new SimpleHandler()); 219 httpServer.start(); 220 return httpServer; 221 } 222 223 class SimpleHandler implements HttpHandler { 224 @Override 225 public void handle(HttpExchange t) throws IOException { 226 Headers reqHeaders = t.getRequestHeaders(); 227 228 // some small sanity check 229 List<String> cookies = reqHeaders.get("Cookie"); 230 for (String cookie : cookies) { 231 if (!cookie.contains("JSESSIONID") 232 || !cookie.contains("WILE_E_COYOTE")) 233 t.sendResponseHeaders(400, -1); 234 } 235 236 // return some cookies so we can check getHeaderField(s) 237 Headers respHeaders = t.getResponseHeaders(); 238 List<String> values = new ArrayList<>(); 239 values.add("ID=JOEBLOGGS; version=1; Path=" + URI_PATH); 240 values.add("NEW_JSESSIONID=" + (SESSION_ID+1) + "; version=1; Path=" 241 + URI_PATH +"; HttpOnly"); 242 values.add("NEW_CUSTOMER=WILE_E_COYOTE2; version=1; Path=" + URI_PATH); 243 respHeaders.put("Set-Cookie", values); 244 values = new ArrayList<>(); 245 values.add("COOKIE2_CUSTOMER=WILE_E_COYOTE2; version=1; Path=" 246 + URI_PATH); 247 respHeaders.put("Set-Cookie2", values); 248 values.add("COOKIE2_JSESSIONID=" + (SESSION_ID+100) 249 + "; version=1; Path=" + URI_PATH +"; HttpOnly"); 250 respHeaders.put("Set-Cookie2", values); 251 252 t.sendResponseHeaders(200, -1); 253 t.close(); 254 } 255 } 256 257 volatile int passed = 0, failed = 0; 258 boolean debug = false; 259 void pass() {passed++;} 260 void fail() {failed++;} 261 void fail(String msg) {System.err.println(msg); fail();} 262 void unexpected(Throwable t) {failed++; t.printStackTrace();} 263 void debug(String message) { if (debug) System.out.println(message); } 264 void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} 265 public static void main(String[] args) throws Throwable { 266 Class<?> k = new Object(){}.getClass().getEnclosingClass(); 267 try {k.getMethod("instanceMain",String[].class) 268 .invoke( k.newInstance(), (Object) args);} 269 catch (Throwable e) {throw e.getCause();}} 270 public void instanceMain(String[] args) throws Throwable { 271 try {test(args);} catch (Throwable t) {unexpected(t);} 272 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 273 if (failed > 0) throw new AssertionError("Some tests failed");} 274} 275 276