• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# JWT support in lws
2
3lws supports the common usage scenarios of JWS (signed) JWT generation,
4parsing and transferring in and out as http cookies.  Care is taken to provide
5helpers that implement the current security best practices for cookie handling
6and JWT validation.  All of the common algorithms like ES512 are supported
7along with JWK generation and handling apis.
8
9The build options needed are `-DLWS_WITH_JOSE=1` `-DLWS_WITH_GENCRYPTO=1`.
10
11Underlying JOSE primitives are exposed as apis, some JWT specific primitives
12and finally a JWT-via http cookie level creation apis each building on top of
13what was underneath.
14
15The higher level APIs are provided additionally because they have the most
16opportunity for implementation pitfalls like not validating alg carefully, or
17not using the latest cookie security options; the provided APIs handle that
18centrally for you.  If your needs vary from what the higher level apis are
19doing, you can cut-and-paste out those implementations and create your own
20using the public lower level apis.
21
22## LWS JWT fields
23
24Lws JWT uses mainly well-known fields
25
26Field|Std|Meaning
27---|---|---
28iss|yes|Issuer, typically the domain like "warmcat.com"
29aud|yes|Audience, typically a url path like "https://warmcat.com/sai"
30iat|yes|Unix-time "Issued At"
31nbf|yes|Unix-time "Not Before"
32exp|yes|Unix-time "Expired"
33sub|yes|Subject, eg, a username or user email
34csrf|no|A random 16-char hex token generated with the JWT for use in links specific to the JWT bearer
35ext|no|Application-specific JSON sub-object with whatever fields you need, eg, `"authorization": 1`
36
37## Approach for JWT as session token
38
39Once JWTs are produced, they are autonomous bearer tokens, if they are not kept
40secret between the browser and the site, they will be accepted as evidence for
41having rights to the session from anyone.
42
43Requiring https, and various other cookie hardening techniques make it more
44difficult for them to leak, but it is still necessary to strictly constrain the
45token's validity time, usually to a few tens of minutes or how long it takes a
46user to login and get stuff done on the site in one session.
47
48## CSRF mitigation
49
50Cross Site Request Forgery (CSRF) is a hacking scenario where an authorized
51user with a valid token is tricked into clicking on an external link that
52performs some action with side-effects on the site he has active auth on.  For
53example, he has a cookie that's logged into his bank, and the link posts a form
54to the bank site transferring money to the attacker.
55
56Lws JWT mitigates this possibility by putting a random secret in the generated
57JWT; when the authorized user presents his JWT to generate the page, generated
58links that require auth to perform their actions include the CSRF string from
59that user's current JWT.
60
61When the user clicks those links intentionally, the CSRF string in the link
62matches the CSRF string in the currently valid JWT that was also provided to
63the server along with the click, and all is well.
64
65An attacker does not know the random, ephemeral JWT CSRF secret to include in
66forged links, so the attacker-controlled action gets rejected at the server as
67having used an invalid link.
68
69The checking and link manipulation need to be implemented in user code / JS...
70lws JWT provides the random CSRF secret in the JWT and makes it visible to the
71server when the incoming JWT is processed.
72
73## Need for client tracking of short JWT validity times
74
75Many links or references on pages do not require CSRF strings, only those that
76perform actions with side-effects like deletion or money transfer should need
77protecting this way.
78
79Due to CSRF mitigation, generated pages containing the protected links
80effectively have an expiry time linked to that of the JWT, since only the bearer
81of the JWT used to generate the links on the page can use them; once that
82expires actually nobody can use them and the page contents, which may anyway
83be showing content that only authenticated users can see must be invalidated and
84re-fetched.  Even if the contents are visible without authentication, additional
85UI elements like delete buttons that should only be shown when authenticated
86will wrongly still be shown
87
88For that reason, the client should be informed by the server along with the
89authentication status, the expiry time of it.  The client should then by itself
90make arrangements to refresh the page when this time is passed,
91either showing an unauthenticated version of the same page if it exists, or by
92redirecting to the site homepage if showing any of the contents required
93authentication.  The user can then log back in using his credientials typically
94stored in the browser's password store and receive a new short-term JWT with a
95new random csrf token along with a new page using the new csrf token in its
96links.
97
98## Considerations for long-lived connections
99
100Once established as authorized, websocket links may be very long-lived and hold
101their authorization state at the server.  Although the browser monitoring the
102JWT reloading the page on auth expiry should mitigate this, an attacker can
103choose to just not do that and have an immortally useful websocket link.
104
105At least for actions on the long-lived connection, it should not only confirm
106the JWT authorized it but that the current time is still before the "exp" time
107in the JWT, this is made available as `expiry_unix_time` in the args struct
108after successful validation.
109
110Ideally the server should close long-lived connections according to their auth
111expiry time.
112
113## JWT lower level APIs
114
115The related apis are in `./include/libwebsockets/lws-jws.h`
116
117### Validation of JWT
118
119```
120int
121lws_jwt_signed_validate(struct lws_context *ctx, struct lws_jwk *jwk,
122			const char *alg_list, const char *com, size_t len,
123			char *temp, int tl, char *out, size_t *out_len);
124```
125
126### Composing and signing JWT
127
128```
129int
130lws_jwt_sign_compact(struct lws_context *ctx, struct lws_jwk *jwk,
131		     const char *alg, char *out, size_t *out_len, char *temp,
132		     int tl, const char *format, ...);
133```
134
135## JWT creation and cookie get / set API
136
137Both the validation and signing apis use the same struct to contain their
138aguments.
139
140```
141struct lws_jwt_sign_set_cookie {
142	struct lws_jwk			*jwk;
143	/**< entry: required signing key */
144	const char			*alg;
145	/**< entry: required signing alg, eg, "ES512" */
146	const char 			*iss;
147	/**< entry: issuer name to use */
148	const char			*aud;
149	/**< entry: audience */
150	const char			*cookie_name;
151	/**< entry: the name of the cookie */
152	char				sub[33];
153	/**< sign-entry, validate-exit: subject */
154	const char			*extra_json;
155	/**< sign-entry, validate-exit:
156	 * optional "ext" JSON object contents for the JWT */
157	size_t				extra_json_len;
158	/**< validate-exit:
159	 * length of optional "ext" JSON object contents for the JWT */
160	const char			*csrf_in;
161	/**< validate-entry:
162	 * NULL, or an external CSRF token to check against what is in the JWT */
163	unsigned long			expiry_unix_time;
164	/**< sign-entry: seconds the JWT and cookie may live,
165	 * validate-exit: expiry unix time */
166};
167
168int
169lws_jwt_sign_token_set_http_cookie(struct lws *wsi,
170				   const struct lws_jwt_sign_set_cookie *i,
171				   uint8_t **p, uint8_t *end);
172int
173lws_jwt_get_http_cookie_validate_jwt(struct lws *wsi,
174				     struct lws_jwt_sign_set_cookie *i,
175				     char *out, size_t *out_len);
176```
177