original [Netscape spec from 1994](https://curl.haxx.se/rfc/cookie_spec.html).
In 2011, [RFC6265](https://www.ietf.org/rfc/rfc6265.txt) was finally
- published and details how cookies work within HTTP.
+ published and details how cookies work within HTTP. In 2017, an update was
+ [drafted](https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone-01)
+ to deprecate modification of 'secure' cookies from non-secure origins.
## Cookies saved to disk
5.5 auth= in URLs
5.6 Refuse "downgrade" redirects
5.7 QUIC
- 5.8 Leave secure cookies alone
6. TELNET
6.1 ditch stdin
implemented. This, to allow other projects to benefit from the work and to
thus broaden the interest and chance of others to participate.
-5.8 Leave secure cookies alone
-
- Non-secure origins (HTTP sites) should not be allowed to set or modify
- cookies with the 'secure' property:
-
- https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone-01
-
6. TELNET
bool noexpire, /* if TRUE, skip remove_expired() */
char *lineptr, /* first character of the line */
const char *domain, /* default domain */
- const char *path) /* full path used when this cookie is set,
+ const char *path, /* full path used when this cookie is set,
used to get default path for the cookie
unless set */
+ bool secure) /* TRUE if connection is over secure origin */
{
struct Cookie *clist;
struct Cookie *co;
/* this was a "<name>=" with no content, and we must allow
'secure' and 'httponly' specified this weirdly */
done = TRUE;
- if(strcasecompare("secure", name))
- co->secure = TRUE;
+ /*
+ * secure cookies are only allowed to be set when the connection is
+ * using a secure protocol, or when the cookie is being set by
+ * reading from file
+ */
+ if(strcasecompare("secure", name)) {
+ if(secure || !c->running) {
+ co->secure = TRUE;
+ }
+ else {
+ badcookie = TRUE;
+ break;
+ }
+ }
else if(strcasecompare("httponly", name))
co->httponly = TRUE;
else if(sep)
fields++; /* add a field and fall down to secure */
/* FALLTHROUGH */
case 3:
- co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
+ co->secure = FALSE;
+ if(strcasecompare(ptr, "TRUE")) {
+ if(secure || c->running)
+ co->secure = TRUE;
+ else
+ badcookie = TRUE;
+ }
break;
case 4:
if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
/* the domains were identical */
if(clist->spath && co->spath) {
- if(strcasecompare(clist->spath, co->spath)) {
- replace_old = TRUE;
+ if(clist->secure && !co->secure) {
+ size_t cllen;
+ const char *sep;
+
+ /*
+ * A non-secure cookie may not overlay an existing secure cookie.
+ * For an existing cookie "a" with path "/login", refuse a new
+ * cookie "a" with for example path "/login/en", while the path
+ * "/loginhelper" is ok.
+ */
+
+ sep = strchr(clist->spath + 1, '/');
+
+ if(sep)
+ cllen = sep - clist->spath;
+ else
+ cllen = strlen(clist->spath);
+
+ if(strncasecompare(clist->spath, co->spath, cllen)) {
+ freecookie(co);
+ return NULL;
+ }
}
+ else if(strcasecompare(clist->spath, co->spath))
+ replace_old = TRUE;
else
replace_old = FALSE;
}
while(*lineptr && ISBLANK(*lineptr))
lineptr++;
- Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL);
+ Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
}
free(line); /* free the line buffer */
remove_expired(c); /* run this once, not on every cookie */
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
struct Cookie *Curl_cookie_add(struct Curl_easy *data,
struct CookieInfo *, bool header, bool noexpiry,
char *lineptr,
- const char *domain, const char *path);
+ const char *domain, const char *path,
+ bool secure);
struct Cookie *Curl_cookie_getlist(struct CookieInfo *, const char *,
const char *, bool);
here, or else use real peer host name. */
conn->allocptr.cookiehost?
conn->allocptr.cookiehost:conn->host.name,
- data->state.up.path);
+ data->state.up.path,
+ (conn->handler->protocol&CURLPROTO_HTTPS)?
+ TRUE:FALSE);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
#endif
if(checkprefix("Set-Cookie:", argptr))
/* HTTP Header format line */
Curl_cookie_add(data, data->cookies, TRUE, FALSE, argptr + 11, NULL,
- NULL);
+ NULL, TRUE);
else
/* Netscape format line */
Curl_cookie_add(data, data->cookies, FALSE, FALSE, argptr, NULL,
- NULL);
+ NULL, TRUE);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
free(argptr);
test1540 \
test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
\
-test1560 \
+test1560 test1561 \
\
test1590 \
test1600 test1601 test1602 test1603 test1604 test1605 test1606 test1607 \
HTTP/1.1 200 OK\r
Date: Thu, 09 Nov 2010 14:49:00 GMT\r
Content-Length: 0\r
-Set-Cookie: domain=value;secure;path=/
+Set-Cookie: domain=value;path=/
\r
</data>
</reply>
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
-127.0.0.1 FALSE / TRUE 0 domain value
+127.0.0.1 FALSE / FALSE 0 domain value
</file>
</verify>
</testcase>
--- /dev/null
+<testcase>
+<info>
+<keywords>
+HTTPS
+HTTP
+HTTP GET
+cookies
+cookiejar
+HTTP replaced headers
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data1>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Set-Cookie: super=secret; domain=example.com; path=/1561; secure;
+Set-Cookie: supersuper=secret; domain=example.com; path=/1561/login/; secure;
+Content-Length: 7
+
+nomnom
+</data1>
+<data2>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Set-Cookie: super=secret; domain=example.com; path=/1561; httponly;
+Set-Cookie: super=secret; domain=example.com; path=/1561/; httponly;
+Set-Cookie: super=secret; domain=example.com; path=/15; httponly;
+Set-Cookie: public=yes; domain=example.com; path=/foo;
+Set-Cookie: supersuper=secret; domain=example.com; path=/1561/login/en;
+Set-Cookie: supersuper=secret; domain=example.com; path=/1561/login;
+Set-Cookie: secureoverhttp=yes; domain=example.com; path=/1561; secure;
+Content-Length: 7
+
+nomnom
+</data2>
+</reply>
+
+# Client-side
+<client>
+<features>
+SSL
+</features>
+<server>
+http
+https
+</server>
+<name>
+HTTP
+</name>
+<command>
+-k https://%HOSTIP:%HTTPSPORT/15610001 -L -c log/jar1561.txt -H "Host: www.example.com" http://%HOSTIP:%HTTPPORT/15610002 -L -c log/jar1561.txt -H "Host: www.example.com"
+</command>
+</client>
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /15610001 HTTP/1.1\r
+Host: www.example.com\r
+User-Agent: curl/7.62.0-DEV\r
+Accept: */*\r
+\r
+GET /15610002 HTTP/1.1\r
+Host: www.example.com\r
+User-Agent: curl/7.62.0-DEV\r
+Accept: */*\r
+\r
+</protocol>
+<file name="log/jar1561.txt" mode="text">
+# Netscape HTTP Cookie File
+# https://curl.haxx.se/docs/http-cookies.html
+# This file was generated by libcurl! Edit at your own risk.
+
+.example.com TRUE /foo FALSE 0 public yes
+.example.com TRUE /1561/login/ TRUE 0 supersuper secret
+#HttpOnly_.example.com TRUE /15 FALSE 0 super secret
+</file>
+
+</verify>
+
+</testcase>
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
-127.0.0.1 FALSE /we/want/ TRUE 0 securewithspace after
127.0.0.1 FALSE /we/want/ FALSE 0 prespace yes before
127.0.0.1 FALSE /we/want/ FALSE 0 withspaces2 before equals
127.0.0.1 FALSE /we/want/ FALSE 0 withspaces yes within and around
#HttpOnly_127.0.0.1 FALSE /silly/ FALSE 0 magic yessir
127.0.0.1 FALSE /we/want/ FALSE 2054030187 nodomain value
127.0.0.1 FALSE / FALSE 0 partmatch present
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec8 myvalue9
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec7 myvalue8
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec6 myvalue7
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec5 myvalue6
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec4 myvalue5
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec3 myvalue4
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec2 myvalue3
-#HttpOnly_127.0.0.1 FALSE /p4/ TRUE 0 httpandsec myvalue2
#HttpOnly_127.0.0.1 FALSE /p4/ FALSE 0 httponly myvalue1
#HttpOnly_127.0.0.1 FALSE /p4/ FALSE 0 httpo4 value4
#HttpOnly_127.0.0.1 FALSE /p3/ FALSE 0 httpo3 value3
#HttpOnly_127.0.0.1 FALSE /p2/ FALSE 0 httpo2 value2
#HttpOnly_127.0.0.1 FALSE /p1/ FALSE 0 httpo1 value1
-127.0.0.1 FALSE /secure9/ TRUE 0 secure very1
-127.0.0.1 FALSE /secure8/ TRUE 0 sec8value secure8
-127.0.0.1 FALSE /secure7/ TRUE 0 sec7value secure7
-127.0.0.1 FALSE /secure6/ TRUE 0 sec6value secure6
-127.0.0.1 FALSE /secure5/ TRUE 0 sec5value secure5
-127.0.0.1 FALSE /secure4/ TRUE 0 sec4value secure4
-127.0.0.1 FALSE /secure3/ TRUE 0 sec3value secure3
-127.0.0.1 FALSE /secure2/ TRUE 0 sec2value secure2
-127.0.0.1 FALSE /secure1/ TRUE 0 sec1value secure1
127.0.0.1 FALSE /overwrite FALSE 0 overwrite this2
127.0.0.1 FALSE /silly/ FALSE 0 ismatch this
</file>
# https://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
-.foo.com TRUE /moo TRUE 0 test3 maybe
.host.foo.com TRUE /we/want/ FALSE 2054030187 test2 yes
#HttpOnly_.foo.com TRUE /we/want/ FALSE 2054030187 test yes
</file>