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