JWT! JWT!Let it all out!John SJ Anderson | @genehack | Hack Salem | 14 Sep 2016
Hack Salem | 14 Sep 2016 | @genehack 1
JSON Web Tokenswant to rule your worldJohn SJ Anderson | @genehack | Hack Salem | 14 Sep 2016
Hack Salem | 14 Sep 2016 | @genehack 2
Hi, I'm John
Hack Salem | 14 Sep 2016 | @genehack 3
VP, TechInfinity
Interactive
Hack Salem | 14 Sep 2016 | @genehack 4
So, what's a JWT?
Hack Salem | 14 Sep 2016 | @genehack 5
jwt.io
Hack Salem | 14 Sep 2016 | @genehack 6
WhatDoesThatEvenMean
Hack Salem | 14 Sep 2016 | @genehack 7
Think of it as…• A lightweight alternative to cookies
• A form of access control without authentication
• Cross-site single sign on (SSO) without cross domain pain
Hack Salem | 14 Sep 2016 | @genehack 8
Made of stuff you already know• JSON Objects
• Cryptographically signed and hashed
• Transmitted during HTTP request
Hack Salem | 14 Sep 2016 | @genehack 9
JWT teardown• dot-delimited string ('.')
• 3 parts
• header
• payload
• signature
• Example: xxx.yyyyy.zzz
Hack Salem | 14 Sep 2016 | @genehack 10
JWT teardown: header• Plain ole JSON object
• Base64Url encoded
• Typically indicates token type and hashing algorithm{ "alg": "HS256", "typ": "JWT"}
Hack Salem | 14 Sep 2016 | @genehack 11
JWT teardown: payload• Also plain ole JSON object
• Contains "claims" -- really just data
• Reserved, public, private
• Also Base64Url encoded{ "name": "Hack Salem", "admin": false}
Hack Salem | 14 Sep 2016 | @genehack 12
JWT teardown: signature• Encoded header, plus
• Encoded payload, plus
• A secret, plus
• Signing algorithm from headerHMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Hack Salem | 14 Sep 2016 | @genehack 13
Putting it all together
Hack Salem | 14 Sep 2016 | @genehack 14
Putting it all together
Hack Salem | 14 Sep 2016 | @genehack 15
More advanced usage• Encrypted payloads (JWE)
• Nested JWT
(Won't get any further into these tonight…)
Hack Salem | 14 Sep 2016 | @genehack 16
Libraries for DAYS• .NET, Python, Node, Java, Javascript, Ruby, Perl, Go, PHP
• Haskell, Rust, Lua, Scala, Clojure, ObjectiveC, Swift, Delphi• Support for your favorite language/platform is probably not
an issue
Hack Salem | 14 Sep 2016 | @genehack 17
OK,you'vegotmy
attentionHack Salem | 14 Sep 2016 | @genehack 18
HowDoI
UseIt?
Hack Salem | 14 Sep 2016 | @genehack 19
Basic auth/authz usage
(stolen from jwt.io)Hack Salem | 14 Sep 2016 | @genehack 20
Things to be aware of• Payload/header NOT encrypted (in this setup)
• …don't send anything sensitive!
• Need to control expiration, re-issue, etc.
• Some APIs will send a fresh JWT to the client per-request• Sites other than issuing site can receive JWT
• …if they share the secret
Hack Salem | 14 Sep 2016 | @genehack 21
How is it actually transmitted?• Up to you! Various methods:
• As part of the URL
• In the POST body
• In the Authorization header using bearer scheme:
Authorization: Bearer <token>
Hack Salem | 14 Sep 2016 | @genehack 22
Authorization without authentication
• Scenario:
• You have an API
• You don't want to make anybody authenticate to use it
• You don't want it wide open to the Internet either
• a/k/a authz without authn
Hack Salem | 14 Sep 2016 | @genehack 23
Solution: JWT with RSA keys• Alternative to secret in previous scenario: RSA key-pair
• Can include the public key in the JWT header using JWK
• JSON Web Key, natch
• Allows API client to produce claims in a verifiable way
Hack Salem | 14 Sep 2016 | @genehack 24
To set it up:• Give authorized user a RSA key-pair
• You can even let them generate it — you just need to:
• Record the fingerprint of the public key (important later!)
Hack Salem | 14 Sep 2016 | @genehack 25
On their side:• They make JWT using the private key
• They include the public key in the header
• Include iat (issued-at) and exp (expires) claims
• Send JWT in Authorization header with API request
Hack Salem | 14 Sep 2016 | @genehack 26
On our side:• Get the public key out of the JWT header
• Validate the JWT signature using the key
• Validate that public key fingerprint is white-listed
• Signature produced with private key
• Public key is white-listed
• Therefore we know JWT is valid!
Hack Salem | 14 Sep 2016 | @genehack 27
Things to be aware of:• You still want to validate iat and exp and any other rules
• Your library should probably do that stuff for you, mostly
• Again, nothing is encrypted, so don't plan on sensitive stuff in the payload or header
Hack Salem | 14 Sep 2016 | @genehack 28
Let's see some code!• May look a bit strange
• New experimental language, Nacre
• Transpiles to Javascript
Hack Salem | 14 Sep 2016 | @genehack 29
Client side my $pri_key = Crypt::PK::RSA->new('./key.pri'); my $pub_key = Crypt::PK::RSA->new('./key.pub');
my $token = encode_jwt( alg => 'RS512', extra_headers => { jwk => $pub_key->export_key_jwk('public', 1), nonce => undef , }, key => $pri_key , payload => { iat => time() }, relative_exp => 1800, );
HTTP::Request->new( 'POST' => 'https://example.com/endpoint', ['Authorization' => "Bearer $token"], encode_json({ request => 'body' }) );
Hack Salem | 14 Sep 2016 | @genehack 30
Client side: making the token my $token = encode_jwt( alg => 'RS512', extra_headers => { jwk => $pub_key->export_key_jwk('public', 1), nonce => undef , }, key => $pri_key , payload => { iat => time() }, relative_exp => 1800, );
Hack Salem | 14 Sep 2016 | @genehack 31
Client side: adding the public key extra_headers => { jwk => $pub_key->export_key_jwk('public', 1), },
Key: find an RSA library that supports export to JWK format!
Hack Salem | 14 Sep 2016 | @genehack 32
BTW,just
kiddingaboutNacre…
Hack Salem | 14 Sep 2016 | @genehack 33
it'sPerl!(of course)
Hack Salem | 14 Sep 2016 | @genehack 34
Server side my $auth_header = request_header 'Authorization' ;
status_401 unless ( $token ) = $auth_header =~ /^Bearer (.*)$/;
# try to decode it and confirm valid sig, # and valid iat and exp claims my( $header, $payload ); try { ( $header, $payload ) = decode_jwt( token => $token , decode_header => 1 , accepted_alg => 'RS512' , verify_iat => 1 , verify_exp => 1 ); };
# no catch block, just drop the error, we're out of here in that case status_401 unless $header and $payload;
# check that expiration time is less than one hour status_401 unless $payload->{exp} - $payload->{iat} < 3600;
# check that the included public key is on the whitelist my $pk = Crypt::PK::RSA->new; $pk->import_key($header->{jwk}); my $thumbprint = $pk->export_key_jwk_thumbprint; status_401 unless config->{whitelist}{$thumbprint};
# if we get here, we're all good! ...
Hack Salem | 14 Sep 2016 | @genehack 35
Server side: get the token my $auth_header = request_header 'Authorization' ;
status_401 unless ( $token ) = $auth_header =~ /^Bearer (.*)$/;
Hack Salem | 14 Sep 2016 | @genehack 36
Server side: decode the token # try to decode it and confirm valid sig, # and valid iat and exp claims my( $header, $payload ); try { ( $header, $payload ) = decode_jwt( token => $token , decode_header => 1 , accepted_alg => 'RS512' , verify_iat => 1 , verify_exp => 1 ); };
# no catch block, just drop the error, we're out of here in that case status_401 unless $header and $payload;
Hack Salem | 14 Sep 2016 | @genehack 37
Server side: decode the token• Key in header wrong? FAILS• Not right algorithm? FAILS• Doesn't have iat and exp? FAILSALL that validation is happening inside the library, so I don't have to worry about it.
• Me? WINS
Hack Salem | 14 Sep 2016 | @genehack 38
Server side: do more checks # check that expiration time is less than one hour status_401 unless $payload->{exp} - $payload->{iat} < 3600;
# check that the included public key is on the whitelist my $pk = Crypt::PK::RSA->new; $pk->import_key($header->{jwk}); my $thumbprint = $pk->export_key_jwk_thumbprint; status_401 unless config->{whitelist}{$thumbprint};
Hack Salem | 14 Sep 2016 | @genehack 39
Server side: more checks• We specify in the API docs that tokens can only be valid for
one hour
• Have to check that ourselves
• Also need to make sure this isn't some random RSA keypair
• Need to make sure we know this public key
Hack Salem | 14 Sep 2016 | @genehack 40
Server side: THAT'S ALL FOLKS # if we get here, we're all good!
• If we know the public key in the header,
• then we know the private key was used to sign the JWT
• (or it wouldn't validate)
• and therefore the JWT is from the private key holder
• (who is, by definition, authorized!)
Hack Salem | 14 Sep 2016 | @genehack 41
IMPORTANT NOTE!This does, of course, depend on the client keeping the private key actually private…But access revocation is as simple as removing a public keyprint from the whitelist.
Hack Salem | 14 Sep 2016 | @genehack 42
Conclusions• JWTs solve some really common problems.
• JWTs solve them in a pretty elegant way.
• This is really pretty damn cool.
Hack Salem | 14 Sep 2016 | @genehack 43
Conclusions• JWTs solve some really common problems.
• JWTs solve them in a pretty elegant way.
• This is really pretty damn cool!!!• You should think about using JWTs.
Hack Salem | 14 Sep 2016 | @genehack 44
Questions?
Hack Salem | 14 Sep 2016 | @genehack 45
Thanks!
Hack Salem | 14 Sep 2016 | @genehack 46