0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-15 04:30:25 +00:00

Update the cookies guide chapter

This commit is contained in:
Loïc Hoguin 2016-09-01 17:22:53 +02:00
parent 88dbc30391
commit 5495b57174

View file

@ -4,112 +4,94 @@
Cookies are a mechanism allowing applications to maintain Cookies are a mechanism allowing applications to maintain
state on top of the stateless HTTP protocol. state on top of the stateless HTTP protocol.
Cowboy provides facilities for handling cookies. It is highly Cookies are a name/value store where the names and values are
recommended to use them instead of writing your own, as the stored in plain text. They expire either after a delay
implementation of cookies can vary greatly between clients. or when the browser closes. They can be configured on a
specific domain name or path, and restricted to secure
resources (sent or downloaded over HTTPS), or restricted
to the server (disallowing access from client-side scripts).
Cookie names are de facto case sensitive.
Cookies are stored client-side and sent with every subsequent Cookies are stored client-side and sent with every subsequent
request that matches the domain and path for which they were request that matches the domain and path for which they were
stored, including requests for static files. For this reason stored, until they expire. This can create a non-negligible
they can incur a cost which must be taken in consideration. cost.
Also consider that, regardless of the options used, cookies Cookies should not be considered secure. They are stored on
are not to be trusted. They may be read and modified by any the user's computer in plain text, and can be read by any
program on the user's computer, but also by proxies. You program. They can also be read by proxies when using clear
should always validate cookie values before using them. Do connections. Always validate the value before using it,
not store any sensitive information in cookies either. and never store any sensitive information inside it.
When explicitly setting the domain, the cookie will be sent Cookies set by the server are only available in requests
for the domain and all subdomains from that domain. Otherwise following the client reception of the response containing
the current domain will be used. The same is true for the them.
path.
When the server sets cookies, they will only be available Cookies may be sent repeatedly. This is often useful to
for requests that are sent after the client receives the update the expiration time and avoid losing a cookie.
response.
Cookies are sent in HTTP headers, therefore they must have
text values. It is your responsibility to encode any other
data type. Also note that cookie names are de facto case
sensitive.
Cookies can be set for the client session (which generally
means until the browser is closed), or it can be set for
a number of seconds. Once it expires, or when the server
says the cookie must exist for up to 0 seconds, the cookie
is deleted by the client. To avoid this while the user
is browsing your site, you should set the cookie for
every request, essentially resetting the expiration time.
Cookies can be restricted to secure channels. This typically
means that such a cookie will only be sent over HTTPS,
and that it will only be available by client-side scripts
that run from HTTPS webpages.
Finally, cookies can be restricted to HTTP and HTTPS requests,
essentially disabling their access from client-side scripts.
=== Setting cookies === Setting cookies
By default, cookies you set are defined for the session. // @todo So I am not particularly happy about set_resp_cookie/4
// having Opts as a *third* argument, instead of the *last* like
// all other functions that come with an Opts argument. We will
// probably need to change this before 2.0.
By default cookies are defined for the duration of the session:
[source,erlang] [source,erlang]
SessionID = generate_session_id(), SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [], Req). Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, Req0).
You can also make them expire at a specific point in the They can also be set for a duration in seconds:
future.
[source,erlang] [source,erlang]
---- ----
SessionID = generate_session_id(), SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
{max_age, 3600} #{max_age => 3600}, Req0).
], Req).
---- ----
You can delete cookies that have already been set. The value To delete cookies, set `max_age` to 0:
is ignored.
[source,erlang]
----
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, <<>>, [
{max_age, 0}
], Req).
----
You can restrict them to a specific domain and path.
For example, the following cookie will be set for the domain
`my.example.org` and all its subdomains, but only on the path
`/account` and all its subdirectories.
[source,erlang]
----
Req2 = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>, [
{domain, "my.example.org"},
{path, "/account"}
], Req).
----
You can restrict the cookie to secure channels, typically HTTPS.
[source,erlang] [source,erlang]
---- ----
SessionID = generate_session_id(), SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
{secure, true} #{max_age => 0}, Req0).
], Req).
---- ----
You can restrict the cookie to client-server communication To restrict cookies to a specific domain and path, the options
only. Such a cookie will not be available to client-side scripts. of the same name can be used:
[source,erlang]
----
Req = cowboy_req:set_resp_cookie(<<"inaccount">>, <<"1">>,
#{domain => "my.example.org", path => "/account"}, Req0).
----
Cookies will be sent with requests to this domain and all
its subdomains, and to resources on this path or deeper
in the path hierarchy.
To restrict cookies to secure channels (typically resources
available over HTTPS):
[source,erlang] [source,erlang]
---- ----
SessionID = generate_session_id(), SessionID = generate_session_id(),
Req2 = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID, [ Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
{http_only, true} #{secure => true}, Req0).
], Req). ----
To prevent client-side scripts from accessing a cookie:
[source,erlang]
----
SessionID = generate_session_id(),
Req = cowboy_req:set_resp_cookie(<<"sessionid">>, SessionID,
#{http_only => true}, Req0).
---- ----
Cookies may also be set client-side, for example using Cookies may also be set client-side, for example using
@ -117,23 +99,23 @@ Javascript.
=== Reading cookies === Reading cookies
As we said, the client sends cookies with every request. The client only ever sends back the cookie name and value.
But unlike the server, the client only sends the cookie All other options that can be set are never sent back.
name and value.
Cowboy provides two different ways to read cookies. You Cowboy provides two functions for reading cookies. Both
can either parse them as a list of key/value pairs, or involve parsing the cookie header(s) and so should not
match them into a map, optionally applying constraints be called repeatedly.
to the values or providing a default if they are missing.
You can parse the cookies and then use standard library You can get all cookies as a key/value list:
functions to access individual values.
[source,erlang] [source,erlang]
Cookies = cowboy_req:parse_cookies(Req), Cookies = cowboy_req:parse_cookies(Req),
{_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies). {_, Lang} = lists:keyfind(<<"lang">>, 1, Cookies).
You can match the cookies into a map. Or you can perform a match against cookies and retrieve
only the ones you need, while at the same time doing
any required post processing using xref:constraints[constraints].
This function returns a map:
[source,erlang] [source,erlang]
#{id := ID, lang := Lang} = cowboy_req:match_cookies([id, lang], Req). #{id := ID, lang := Lang} = cowboy_req:match_cookies([id, lang], Req).
@ -141,8 +123,7 @@ You can match the cookies into a map.
You can use constraints to validate the values while matching You can use constraints to validate the values while matching
them. The following snippet will crash if the `id` cookie is them. The following snippet will crash if the `id` cookie is
not an integer number or if the `lang` cookie is empty. Additionally not an integer number or if the `lang` cookie is empty. Additionally
the `id` cookie value will be converted to an integer term, saving the `id` cookie value will be converted to an integer term:
you a conversion step.
[source,erlang] [source,erlang]
CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req). CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req).
@ -150,14 +131,12 @@ CookiesMap = cowboy_req:match_cookies([{id, int}, {lang, nonempty}], Req).
Note that if two cookies share the same name, then the map value Note that if two cookies share the same name, then the map value
will be a list of the two cookie values. will be a list of the two cookie values.
Read more about xref:constraints[constraints].
A default value can be provided. The default will be used A default value can be provided. The default will be used
if the `lang` cookie is not found. It will not be used if if the `lang` cookie is not found. It will not be used if
the cookie is found but has an empty value. the cookie is found but has an empty value:
[source,erlang] [source,erlang]
#{lang := Lang} = cowboy_req:match_cookies([{lang, [], <<"en-US">>}], Req). #{lang := Lang} = cowboy_req:match_cookies([{lang, [], <<"en-US">>}], Req).
If no default is provided and the value is missing, the If no default is provided and the value is missing, an
query string is deemed invalid and the process will crash. exception is thrown.