JavaOne
#RESTSecurity @dblevins @tomitribe
Deconstructing REST Security
David Blevins
Tomitribe
JavaOne
#RESTSecurity @dblevins @tomitribe
“The nice thing about standards is
you have so many to choose from.”
- Andrew S. Tanenbaum
JavaOne
#RESTSecurity @dblevins @tomitribe
Focus Areas
• Beyond Basic Auth
• Theory of OAuth 2.0
• Introduc?on of JWT
• Google/Facebook style API security
• Stateless vs Stateful Architecture
• HTTP Signatures
• Amazon EC2 style API security
JavaOne
#RESTSecurity @dblevins @tomitribe
Baseline Architecture
1000 users
x 3 TPS
4 hops
3000 TPS
frontend
12000 TPS
backend
JavaOne
#RESTSecurity @dblevins @tomitribe
Basic Auth
(and its problems)
JavaOne
#RESTSecurity @dblevins @tomitribe
Basic Auth Message
POST /painter/color/object HTTP/1.1
Host: localhost:8443
Authorization: Basic c25vb3B5OnBhc3M=
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 45
{"color":{"b":255,"g":0,"name":"blue","r":0}}
JavaOne
#RESTSecurity @dblevins @tomitribe
Basic Auth
Password Sent
3000 TPS
(HTTP+SSL)
username+password
Base64
(no auth)
3000 TPS
(LDAP)
12000 TPS
(HTTP)
JavaOne
#RESTSecurity @dblevins @tomitribe
Basic Auth
Password Sent
3000 TPS
(HTTP+SSL)
username+password
Base64
username+password
Base64
15000 TPS
(LDAP)
Password Sent
12000 TPS
(HTTP)
JavaOne
#RESTSecurity @dblevins @tomitribe
Basic Auth
Password Sent
3000 TPS
(HTTP+SSL)
username+password
Base64
IP
whitelis?ng
3000 TPS
(LDAP)
12000 TPS
(HTTP)
JavaOne
#RESTSecurity @dblevins @tomitribe
“Hey, give me all
of Joe’s salary
information.”
“I don’t know
who you are,
…
but sure!”
JavaOne
#RESTSecurity @dblevins @tomitribe
Latveria Attacks
JavaOne
#RESTSecurity @dblevins @tomitribe
Basic Auth - Attacks
Valid
Password Sent
3000 TPS
(HTTP+SSL) IP
whitelis?ng
9000 TPS
(LDAP)
12000 TPS
(HTTP)
Invalid
Password Sent
6000 TPS
(HTTP+SSL)
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0
(and its problems)
JavaOne
#RESTSecurity @dblevins @tomitribe
JavaOne
#RESTSecurity @dblevins @tomitribe
JavaOne
#RESTSecurity @dblevins @tomitribe
JavaOne
#RESTSecurity @dblevins @tomitribe
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2 - Password Grant
(LDAP)
(Token Store)
POST /oauth2/token
Host: api.superbiz.io
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
grant_type=password&username=snoopy&password=woodstock
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
}
Verify
Password
Generate
Token
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/object HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 45
{"color":{"r":0,"g":0,"b":255,"name":"blue"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/palette HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 45
{"color":{"r":0,"g":255,"b":0,"name":"green"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/select HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 44
{"color":{"r":255,"g":0,"b":0,"name":"red"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/fill HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 49
{"color":{"r":0,"g":255,"b":255,"name":"yellow"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/stroke HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 49
{"color":{"r":255,"g":200,"b":255,"name":"orange"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
401
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2 - Refresh Grant
(LDAP)
(Token Store)
Verify
Password
Generate
Token
POST /oauth2/token
Host: api.superbiz.io
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"6Fe4jd7TmdE5yW2q0y6W2w",
"expires_in":3600,
"refresh_token":"hyT5rw1QNh5Ttg2hdtR54e",
}
JavaOne
#RESTSecurity @dblevins @tomitribe
Old pair
• Access Token 2YotnFZFEjr1zCsicMWpAA
• Refresh Token tGzv3JOkF0XG5Qx2TlKWIA
New pair
• Access Token 6Fe4jd7TmdE5yW2q0y6W2w
• Refresh Token hyT5rw1QNh5Ttg2hdtR54e
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/palette HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 6Fe4jd7TmdE5yW2q0y6W2w
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 46
{"color":{"r":0,"g":255,"b":0,"name":"green"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/select HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 6Fe4jd7TmdE5yW2q0y6W2w
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 44
{"color":{"r":255,"g":0,"b":0,"name":"red"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message
POST /painter/color/fill HTTP/1.1
Host: api.superbiz.io
Authorization: Bearer 6Fe4jd7TmdE5yW2q0y6W2w
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/json
Content-Length: 49
{"color":{"r":0,"g":255,"b":255,"name":"yellow"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
What have we achieved?
JavaOne
#RESTSecurity @dblevins @tomitribe
You have more passwords
(at least your devices do)
JavaOne
#RESTSecurity @dblevins @tomitribe
Term Alert
• Password Grant???
• Logging in
• Token?
• Slightly less crappy password
• Equally crappy HTTP Session ID
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2
Tokens Sent
3000 TPS
(HTTP+SSL)
IP
whitelis?ng
3000 TPS
(token checks)
Password Sent
1000/daily
(HTTP+SSL)
OAuth 2
(LDAP)
4 hops
12000 TPS
backend
JavaOne
#RESTSecurity @dblevins @tomitribe
JavaOne
#RESTSecurity @dblevins @tomitribe
“Who the heck
is
6Fe4jd7TmdE5y
W2q0y6W2w
???????”
“No idea, dude.
Ask the token
server.”
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2
Tokens Sent
3000 TPS
(HTTP+SSL)
IP
whitelis?ng
3000 TPS
(token checks)
Password Sent
1000/daily
(HTTP+SSL)
OAuth 2
(LDAP)
12000 TPS
(token checks)
8 hops
24000 TPS
backend
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2
Tokens Sent
3000 TPS
(HTTP+SSL)
IP
whitelis?ng
3000 TPS
(token checks)
Password Sent
1000/daily
(HTTP+SSL)
OAuth 2
(LDAP)
12000 TPS
(token checks)
8 hops
24000 TPS
backend
55% of all traffic
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2
Tokens Sent
3000 TPS
(HTTP+SSL)
IP
whitelis?ng
0 TPS
(token checks)
Password Sent
1000/daily
(HTTP+SSL)
OAuth 2
(LDAP)
0 TPS
(token checks)
0 hops
0 TPS
backend
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2
Pointer Pointer
State
JavaOne
#RESTSecurity @dblevins @tomitribe
Access Token
Access Pointer?
Access Primary Key?
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0
High Frequency Password
Exchange Algorithm?
JavaOne
#RESTSecurity @dblevins @tomitribe
Hashing and Signing
Symmetric and Asymmetric
JavaOne
#RESTSecurity @dblevins @tomitribe
Hashing Data
JavaOne
#RESTSecurity @dblevins @tomitribe
01010000010001000011011011101000110101001001100001010011110000
00111010101111111111111111000101111101001110111000100010000000000
111111101011100001001100100000101011111001101111111100111011000011
111011001101100100000101011110011001100001011011110101110110001
JavaOne
#RESTSecurity @dblevins @tomitribe
01010000010001000011011011101000110101001001100001010011110000
00111010101111111111111111000101111101001110111000100010000000000
111111101011100001001100100000101011111001101111111100111011000011
111011001101100100000101011110011001100001011011110101110110001
JavaOne
#RESTSecurity @dblevins @tomitribe
More Bits the Better
JavaOne
#RESTSecurity @dblevins @tomitribe
99 Bottles of Beer
mingus:~/bar 04:08:52
$ for n in {99..1}; do
echo "$n Bottles of Beer on the Wall";
done > /tmp/party-time.txt
mingus:~/bar 04:24:42
$ cat /tmp/party-time.txt
99 Bottles of Beer on the Wall
98 Bottles of Beer on the Wall
97 Bottles of Beer on the Wall
96 Bottles of Beer on the Wall
95 Bottles of Beer on the Wall
94 Bottles of Beer on the Wall
93 Bottles of Beer on the Wall
92 Bottles of Beer on the Wall
91 Bottles of Beer on the Wall
90 Bottles of Beer on the Wall
...
JavaOne
#RESTSecurity @dblevins @tomitribe
99 Bottles of Beer
mingus:~/bar 04:16:42
$ hashes all /tmp/party-time.txt
XxHash32 9237490d
XxHash64 81c4196cc7a01049
MD5 963462e4849e7c266df928b44b004190
SHA-1 b3be8c8879b31e7af4067f2769e4df37e6dd7690
SHA-256 0ef285da8792e1b78825f14ffc485a3843c66c4f481411978d0b4612be963383
SHA-512 376a589fd4b7f070119a8193e6d922df05d2f200c96927351bef66b688bffcca
3e990aa2f720494e95967bcbbfc804e86ec2eefea2f820e9a1aa2f3da5146fe7
JavaOne
#RESTSecurity @dblevins @tomitribe
99 Bottles of Water?
mingus:~/home 04:27:14
$ hashes all /tmp/party-time.txt #modified
XxHash32 2045608c
XxHash64 774507c9e384ea35
MD5 662028bba25e1e398ff8538cae9cd7c6
SHA-1 e11c127c072e09119fd7b3f35bc7ce1fd52b9b7a
SHA-256 671d8e4af11d7ee65c1d51ab29303a1afc7439402c2eb61505cfb5c698ff419c
SHA-512 fe0674683808e37bcad624d98426977c0936c15280922b2057efc33e4ca28127
8ea852d5450181fae726807e9c6e5e36a6823e7750948df89c6095ddbdaa4816
JavaOne
#RESTSecurity @dblevins @tomitribe
*9* Bottles of Beer….
mingus:~/home 04:28:00
$ hashes all /tmp/party-time.txt #modified
XxHash32 dd95a169
XxHash64 e1efa88d24f3b0a4
MD5 74ef52ef716a8036182e9622ec72bdc9
SHA-1 e1557cdf9fb9f95d0cfd2881759eee7620d41622
SHA-256 0511b68b950ce581abff7ff4e58e08c05f874c509ffd73c3379ba09e19daeb46
SHA-512 8cb1148a5e57f12e36d93519cafb882c8567985e22be8db52236d0044578784d
c3ead824531955b11fe69d9743ab11fbdb18662e54c35406f09616809367355d
JavaOne
#RESTSecurity @dblevins @tomitribe
Protecting the Hash
HMAC (Symmetric)
RSA (Asymmetric)
JavaOne
#RESTSecurity @dblevins @tomitribe
Encoding a Hash or Signature
Binary 0010100011010111110000011011000100101000110011100111010010001000
0100011011011010000000100011110100111111010100011000100011010001
1101101001010101111100010011111110100000001001100010000000010111
0000000000100101000010110011000100001001011011010111101111101101
Hex 8af5c1468a399708b12d205e7ec588c52dd547fe0232027400526846485bef5b
Base64 ivXBRoo5lwixLSBefsWIxS3VR_4CMgJ0AFJoRkhb71s
Base85 MY4eTME."/Yq7))I`7,^/_*Aj!sh!!)dK"86bOe~
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0
+
JSon Web Tokens (JWT)
JavaOne
#RESTSecurity @dblevins @tomitribe
JSon Web Token
• Pronounced “JOT”
• Fancy JSON map
• Base64 URL Encoded
• Digitally Signed (RSA-SHA256, HMAC-SHA512, etc)
• Built-in expira?on
JavaOne
#RESTSecurity @dblevins @tomitribe
Access Token Previously
• 6Fe4jd7TmdE5yW2q0y6W2w
JavaOne
#RESTSecurity @dblevins @tomitribe
Access Token Now
• eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbi
10eXBlIjoiYWNjZXNzLXRva2VuIiwidXNlcm5hbWUiOiJzb
m9vcHkiLCJhbmltYWwiOiJiZWFnbGUiLCJpc3MiOiJodHRw
czovL2RlbW8uc3VwZXJiaXouY29tL29hdXRoMi90b2tlbiI
sInNjb3BlcyI6WyJ0d2l0dGVyIiwibWFucy1iZXN0LWZyaW
VuZCJdLCJleHAiOjE0NzQyODA5NjMsImlhdCI6MTQ3NDI3O
TE2MywianRpIjoiNjY4ODFiMDY4YjI0OWFkOSJ9.DTfSdMz
IIsC0j8z3icRdYO1GaMGl6j1I_2DBjiiHW9vmDz8OAw8Jh8
DpO32fv0vICc0hb4F0QCD3KQnv8GVM73kSYaOEUwlW0k1Ta
Elxc43_Ocxm1F5IUNZvzlLJ_ksFXGDL_cuadhVDaiqmhct0
98ocefuv08TdzRxqYoEqYNo
JavaOne
#RESTSecurity @dblevins @tomitribe
Access Token Now
• header (JSON > Base64 URL Encoded)
• describes how the token signature can be checked
• payload (JSON > Base64 URL Encoded)
• Basically a map of whatever you want to put in it
• Some standard entries such as expira?on
• signature (Binary > Base64 URL Encoded
• The actual digital signature
• made exclusively by the /oauth2/token endpoint
• If RSA, can be checked by anyone
JavaOne
#RESTSecurity @dblevins @tomitribe
• { "alg": “RS256", "typ": “JWT" }
• {
"token-type": "access-token",
"username": "snoopy",
"animal": "beagle",
"iss": "https://demo.superbiz.com/oauth2/token",
"scopes": [
“twitter”, "mans-best-friend"
],
"exp": 1474280963,
"iat": 1474279163,
"jti": "66881b068b249ad9"
}
• DTfSdMzIIsC0j8z3icRdYO1GaMGl6j1I_2DBjiiHW9vmDz8OAw8Jh8DpO32fv
0vICc0hb4F0QCD3KQnv8GVM73kSYaOEUwlW0k1TaElxc43_Ocxm1F5IUNZvzl
LJ_ksFXGDL_cuadhVDaiqmhct098ocefuv08TdzRxqYoEqYNo
JavaOne
#RESTSecurity @dblevins @tomitribe
Subtle But High Impact
Architectural Change
JavaOne
#RESTSecurity @dblevins @tomitribe
What we had
(quick recap)
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Pull User Info
From IDP
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Generate an
Access Token
(pointer)
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Insert both
into DB
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Send Access Token (pointer)
to client
JavaOne
#RESTSecurity @dblevins @tomitribe
Results
Client Holds Pointer Server Holds State
JavaOne
#RESTSecurity @dblevins @tomitribe
What we can do now
(Hello JWT!)
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Pull User Info
From IDP
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Format the data
as JSON
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
RSA-SHA 256
sign JSON
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Insert only
pointer
into DB
(for revocation)
JavaOne
#RESTSecurity @dblevins @tomitribe
(LDAP)
Send Access Token (state)
to client
JavaOne
#RESTSecurity @dblevins @tomitribe
Client Holds State Server Holds Pointer
Desired
Results
JavaOne
#RESTSecurity @dblevins @tomitribe
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2 - Password Grant
(LDAP)
(Token ID Store)
POST /oauth2/token
Host: api.superbiz.io
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
grant_type=password&username=snoopy&password=woodstock
Verify
Password
Generate
Signed
Token
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ0b2tlbi10eXBlIjoiYWNjZXNzLXRva2VuIiwidXNlcm5hb
WUiOiJzbm9vcHkiLCJhbmltYWwiOiJiZWFnbGUiLCJpc3M
iOiJodHRwczovL2RlbW8uc3VwZXJiaXouY29tL29hdXRoM
i90b2tlbiIsInNjb3BlcyI6WyJ0d2l0dGVyIiwibWFucy1iZXN0
LWZyaWVuZCJdLCJleHAiOjE0NzQyODA5NjMsImlhdCI6M
TQ3NDI3OTE2MywianRpIjoiNjY4ODFiMDY4YjI0OWFkOSJ
9.DTfSdMzIIsC0j8z3icRdYO1GaMGl6j1I_2DBjiiHW9vmDz8
OAw8Jh8DpO32fv0vICc0hb4F0QCD3KQnv8GVM73kSYaO
EUwlW0k1TaElxc43_Ocxm1F5IUNZvzlLJ_ksFXGDL_cuadh
VDaiqmhct098ocefuv08TdzRxqYoEqYNo",
"expires_in":3600,
"refresh_token":"eyJhbGctGzv3JOkF0XG5Qx2TlKWIAkF0X.
eyJ0b2tlbi10eXBlIjoiYWNjZXNzLXRva2VuIiwidXNlcm5hb
WUiOiJzbm9vcHkiLCJhbmltYWwiOiJiZWFnbGUiLCJpc3M
iOiJodHRwczovL",
}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Message with JWT
POST /painter/color/paleme HTTP/1.1

Host: api.superbiz.io

Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbi10eXBlIjoiYWNjZXNzLXR
va2VuIiwidXNlcm5hbWUiOiJzbm9vcHkiLCJhbmltYWwiOiJiZWFnbGUiLCJpc3MiOiJodHRwczovL2RlbW8uc3VwZXJ
iaXouY29tL29hdXRoMi90b2tlbiIsInNjb3BlcyI6WyJ0d2l0dGVyIiwibWFucy1iZXN0LWZyaWVuZCJdLCJleHAiOjE0NzQy
ODA5NjMsImlhdCI6MTQ3NDI3OTE2MywianRpIjoiNjY4ODFiMDY4YjI0OWFkOSJ9.DTfSdMzIIsC0j8z3icRdYO1GaMGl
6j1I_2DBjiiHW9vmDz8OAw8Jh8DpO32fv0vICc0hb4F0QCD3KQnv8GVM73kSYaOEUwlW0k1TaElxc43_Ocxm1F5IUNZ
vzlLJ_ksFXGDL_cuadhVDaiqmhct098ocefuv08TdzRxqYoEqYNo
User-Agent: curl/7.43.0

Accept: */*

Content-Type: applica?on/json

Content-Length: 46



{"color":{"b":0,"g":255,"r":0,"name":"green"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2 + JWT
Tokens Sent
3000 TPS
(HTTP+SSL)
IP
whitelis?ng
0.55 TPS
(refresh token checks)
Password Sent
1000/daily
(HTTP+SSL)
OAuth 2
(LDAP)
4 hops
12000 TPS
backend
3000 TPS
(signature verifica?on)
12000 TPS
(signature verifica?on)
JavaOne
#RESTSecurity @dblevins @tomitribe
“Hey, give me all
of Joe’s salary
information.”
“Not a chance!”
JavaOne
#RESTSecurity @dblevins @tomitribe
“Hey, give me all
of Joe’s salary
information.”
“Sure thing!”
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2 + JWT
Valid
Tokens Sent
3000 TPS
(HTTP+SSL)
IP
whitelis?ng
0.55 TPS
(refresh token checks)
Password Sent
1000/daily
(HTTP+SSL)
(LDAP)
4 hops
12000 TPS
backend
9000 TPS
(signature verifica?on)
12000 TPS
(signature verifica?on)
Invalid
Tokens Sent
6000 TPS
(HTTP+SSL)
JavaOne
#RESTSecurity @dblevins @tomitribe
hmp://connect2id.com/products/nimbus-jose-jwt
Great JWT lib
JavaOne
#RESTSecurity @dblevins @tomitribe
HTTP Signatures
(Amazon EC2 style API Security)
JavaOne
#RESTSecurity @dblevins @tomitribe
HTTP Signatures
• No “secret” ever hits the wire
• Signs the message itself
• Proves iden?ty
• Prevents message tampering
• Symmetric or Asymmetric signatures
• IETF Drat
• hmps://tools.ieu.org/html/drat-cavage-hmp-signatures
• Extremely simple
• Does NOT eliminate benefits of JWT
JavaOne
#RESTSecurity @dblevins @tomitribe
Signature Message
POST /painter/color/paleme HTTP/1.1

Host: api.superbiz.io

Authoriza?on: Signature keyId=“my-key-name",
algorithm="hmac-sha256",
headers="content-length host date (request-target)”,
signature="j050ZC4iWDW40nVx2oVwBEymXzwvsgm+hKBkuw04b+w="

Date: Mon, 19 Sep 2016 16:51:35 PDT
Accept: */*

Content-Type: applica?on/json

Content-Length: 46



{"color":{"b":0,"g":255,"r":0,"name":"green"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
Signature closeup
Signature
keyId=“my-key-name",
algorithm="hmac-sha256",
headers="content-length host date (request-target)”,
signature="j050ZC4iWDW40nVx2oVwBEymXzwvsgm+hKBkuw04b+w=
JavaOne
#RESTSecurity @dblevins @tomitribe
Signature Auth
Password Sent
0 TPS
(HTTP)
Signature (no auth)
3000 TPS
(LDAP or Keystore)
12000 TPS
(HTTP)
JavaOne
#RESTSecurity @dblevins @tomitribe
Signature Auth
Password Sent
0 TPS
(HTTP)
Signature Signature
3000 TPS
(LDAP or Keystore)
12000 TPS
(HTTP)
JavaOne
#RESTSecurity @dblevins @tomitribe
“Hey, give me all
of Joe’s salary
information.”
“Hey, Larry!
Sure!”
Issue Returns
(bad)
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2.0 Proof-of-Possession
(JWT + HTTP Signatures)
JavaOne
#RESTSecurity @dblevins @tomitribe
Key Value
Iden?ty Informa?on
(JWT)
Key ID
Proof Of Iden?ty
(HTTP Signature)
JavaOne
#RESTSecurity @dblevins @tomitribe
{ "alg": “RS256", "typ": “JWT" }
{ "token-type": "access-token",
"username": "snoopy",
"iss": "hmps://demo.superbiz.com/oauth2/token",
"scopes": ["twimer”, "mans-best-friend"],
"exp": 1474280963,
"iat": 1474279163,
"j?": "66881b068b249ad9"
}
DTfSdMzIIsC0j8z3icRdYO1GaMGl6j1I_2DBjiiHW9vmDz8OAw8Jh8DpO32fv0vICc
0hb4F0QCD3KQnv8GVM73kSYaOEUwlW0k1TaElxc43_Ocxm1F5IUNZvzlLJ_ksFX
GDL_cuadhVDaiqmhct098ocefuv08TdzRxqYoEqYNo
Access Token
JavaOne
#RESTSecurity @dblevins @tomitribe
{ "alg": “RS256", "typ": “JWT" }
{ "token-type": "pop",
"cnf":{ "kid": "green-1234" }
"username": "snoopy",
"iss": "hmps://demo.superbiz.com/oauth2/token",
"scopes": ["twimer”, "mans-best-friend"],
"exp": 1474280963,
"iat": 1474279163,
"j?": "66881b068b249ad9"
}
DTfSdMzIIsC0j8z3icRdYO1GaMGl6j1I_2DBjiiHW9vmDz8OAw8Jh8DpO32fv0vICc
0hb4F0QCD3KQnv8GVM73kSYaOEUwlW0k1TaElxc43_Ocxm1F5IUNZvzlLJ_ksFX
GDL_cuadhVDaiqmhct098ocefuv08TdzRxqYoEqYNo
Access Token
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2 - Password Grant
(LDAP)
(Token ID Store)
POST /oauth2/token
Host: api.superbiz.io
User-Agent: curl/7.43.0
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
grant_type=password&username=snoopy&password=woodstock
Verify
Password
Generate
Signed
Token
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc
3MiOiJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbSIsImV4cCI6M
TMxMTI4MTk3MCwiaWF0IjoxMzExMjgwOTcwLCJjbmYiOnsia2",
"token_type":"pop",
"expires_in":3600,
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc
3MiOiJodHRwczovL2FzZGZhc2RzZGZzZXJ2ZXIuZXhhbXBsZS5
jb20iLCJleHAiOjEzMTEyODE5NzAsImlhdCI6MTMxMTI4MDk3M",
"key":"eyJrdHkiOiJvY3QiLCJ1c2UiOiJzaWciLCJraWQiOiJvcmFuZ
2UteXlqOUQwZWgiLCJrIjoiVlotMFFHTFoyUF9SUFVTVzEwQ0l1
MFdNeVhxLU5EMnBtRFl6QTBPVEtXVEhscDVpYWM1SzRWZWlS
ci1fQk9vWEo0WDJmU1R0NG5Id29fcXV0YTdqSkpLVDRQRVd5W
WFuQlNGc2kwRFc3b3dULUhFeEFHRHlKdEhVdE53NXhzczhOajZ
PeE5QdjZyUk9FLWtldmhMMndCOWNxZ2RJc2NidkRocmFzMzljd
2ZzIiwiYWxnIjoiSFMyNTYifQ"
}
Generate
HMAC
Key
(Key Store)
JavaOne
#RESTSecurity @dblevins @tomitribe
JSON Web Key (encoded)
eyJrdHkiOiJvY3QiLCJ1c2UiOiJzaWciLCJraWQiOiJvcmFuZ2UteX
lqOUQwZWgiLCJrIjoiVlotMFFHTFoyUF9SUFVTVzEwQ0l1MFd
NeVhxLU5EMnBtRFl6QTBPVEtXVEhscDVpYWM1SzRWZWlSci
1fQk9vWEo0WDJmU1R0NG5Id29fcXV0YTdqSkpLVDRQRVd5
WWFuQlNGc2kwRFc3b3dULUhFeEFHRHlKdEhVdE53NXhzczh
OajZPeE5QdjZyUk9FLWtldmhMMndCOWNxZ2RJc2NidkRocm
FzMzljd2ZzIiwiYWxnIjoiSFMyNTYifQ
JavaOne
#RESTSecurity @dblevins @tomitribe
JSON Web Key (decoded)
{ "kty": "oct",
"use": "sig",
"kid": "orange-1234",
"k": "VZ-0QGLZ2P_RPUSW10CIu0WMyXq-ND2pmDYzA0OTKW
THlp5iac5K4VeiRr-_BOoXJ4X2fSTt4nHwo_quta7j
JJKT4PEWyYanBSFsi0DW7owT-HExAGDyJtHUtNw5xs
s8Nj6OxNPv6rROE-kevhL2wB9cqgdIscbvDhras39c
wfs",
"alg": "HS256"
}
JavaOne
#RESTSecurity @dblevins @tomitribe
Signed OAuth 2.0 Message
POST /painter/color/paleme HTTP/1.1

Host: api.superbiz.io

Authoriza?on: Signature keyId=“orange-1234", algorithm="hmac-sha256",
headers="content-length host date (request-target)”,
signature="j050ZC4iWDW40nVx2oVwBEymXzwvsgm+hKBkuw04b+w="
Bearer: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbi10eXBlIjoiYWNjZXNzLXRva2VuIiwidXNlcm5h
bWUiOiJzbm9vcHkiLCJhbmltYWwiOiJiZWFnbGUiLCJpc3MiOiJodHRwczovL2RlbW8uc3VwZXJiaXouY29tL2
9hdXRoMi90b2tlbiIsInNjb3BlcyI6WyJ0d2l0dGVyIiwibWFucy1iZXN0LWZyaWVuZCJdLCJleHAiOjE0NzQyO
DA5NjMsImlhdCI6MTQ3NDI3OTE2MywianRpIjoiNjY4ODFiMDY4YjI0OWFkOSJ9.DTfSdMzIIsC0j8z3icRdY
O1GMGl6j1I_2DBjiiHW9vmDz8OAw8Jh8DpO32fv0vICc0hb4F0QCD3KQnv8GVM73kSYaOEUwlW0k1TaE
lxc43_Ocxm1F5IUNZvzlLJ_ksFXGDL_cuadhVDaiqmhct098ocefuv08TdzRxqYoEqYNo

Date: Mon, 19 Sep 2016 16:51:35 PDT
Accept: */*

Content-Type: applica?on/json

Content-Length: 46



{"color":{"b":0,"g":255,"r":0,"name":"green"}}
JavaOne
#RESTSecurity @dblevins @tomitribe
OAuth 2 + JWT + Signatures
Tokens Sent
3000 TPS
(HTTP+SSL)
0.55 TPS
(refresh token checks)
Password Sent
1000/daily
(HTTP+SSL)
OAuth 2
(LDAP)
4 hops
12000 TPS
backend
3000 TPS
(signature verifica?on)
12000 TPS
(signature verifica?on)
JavaOne
#RESTSecurity @dblevins @tomitribe
hmps://tools.ieu.org/html/drat-ieu-oauth-pop-key-distribu?on
Specification Reference
JavaOne
#RESTSecurity @dblevins @tomitribe
Observations
• HTTP Signatures the only HTTP friendly approach
• Signatures does not solve the “Iden?ty Load” problem
• OAuth 2 with JWT significantly improves IDP load
• Plain OAuth 2
• HTTP Session-like implica?ons
• OAuth 2 with JWT
• Signed cookie
• Signing key to the future
JavaOne
#RESTSecurity @dblevins @tomitribe
Thank You!

2017 JavaOne Deconstructing and Evolving REST Security