Hey folks I'm Nate Barbettini, product manager here at Okta and I'm here
with Aaron Parecki.
Thanks. I'm Aaron Parecki.
I'm a developer advocate at Okta and also a member of the OAuth Working Group.
So I've been seeing the OAuth working group talking about the implicit flow
talking about some of the best practices changing around that, I know
you're really involved in that so I'm kind of curious what what's going on.
Yeah sure thing.
So I've been involved in the working group and part of the part of the
group that is working on the specific guidance around the implicit flow
now and some the changes around that.
So it's something that's relatively recently been part of the
discussions in the group part of the last couple of meetings of the group
as well as discussions on the mailing list things like that.
And what what's the impetus for the discussion what kicked it off like is
there a somebody find a vulnerability in the implicit flow or like why are
we talking about this now?
Yeah yeah sure.
So there's no new vulnerability or anything like there's no significant
event that even necessarily triggered this.
It's more that the implicit flow has never really been secure from the
beginning and everybody kind of knew that.
But at the time we didn't really have any alternatives.
So you know think in like 2010 when mobile app platforms were brand new
and single page apps were just starting to become a thing right.
And --.
That's a long time ago.
It's a long time ago.
We kind of just take these things for granted now.
But at that time in that ecosystem browsers worked very differently, and
we didn't have a lot of good tools available to browsers at the time to do
things sort of properly -- like the regular OAuth way.
And because of that people still wanted to do OAuth in a browser based app
in pure JavaScript.
And so we needed something because you couldn't you couldn't do the
regular auth code flow in the browser at the time.
For anyone who's not not super deep in the OAuth world why is it that we
can't do the auth code flow in a browser or couldn't?
Without getting too far down in the weeds, the basic reason was that
cross-domain requests were not allowed in browsers for security reasons
for a very long time.
And as web development evolved and apps got more complicated
and people are using CDNs for things and app deployments are getting more
complicated, we obviously need a way to do cross-domain and requests and
the whole cross-origin resource sharing policies started popping up in
browsers and there now are good tools to enable specifically when
you want to do a cross-domain post request to allow it from only certain
places than others right.
So on my server I can say these specific places -- these origins
are allowed to make across domain request.
Otherwise keep it locked down and you can't make a request.
Exactly. Yeah.
And the whole the whole way that the regular OAuth flow works is by doing
a POST request -- you know the very last step is is the app to the
POST request over to the token endpoint with authorization code that it
has, and it gets back and access token.
That post request, if your OAuth server is on a different domain than your
app, then that is a cross-origin and request.
Right. And that can only happen if the OAuth server says it's okay.
And, prior to the sort of new way browsers are working that wasn't
even possible in browsers.
Got it.
So way back then we kind of said well if you want to do OAuth in a
browser based app, we'll kind of let you cheat around the system and you
don't have to use a client secret, you're just going to do what we call
the Implicit flow and you just use the client ID.
Yeah you just use a client ID, and more importantly it returns the access
token in the redirect from the authorization server and bypasses that
entire second step.
So it looks simpler at the surface as well because you know there's fewer steps.
It seems more more direct.
Just for curiosity why do we have that extra step.
Why why don't we just do the simpler route?
Yeah. So the simpler route of just returning the access token you know
immediately in the redirect -- the downside is that there's more ways that
can sort of go wrong.
The reason that a lot of the attacks happen and a lot of vulnerabilities
happen is because there are a bunch of different ways that that redirect
step can fail.
If you imagine that like you know the whole way that captive Wi-Fi portals
work, right? You're like "hey I'm gonna go to example dot com and then
instead you get the captive login page.
You're on boingo wireless 2 dot com or something.
Exactly. So when the access token is returned in that redirect
step, if that's intercepted it's basically just game over at that point,
because in OAuth, an access token can just be used by anybody who has.
It's called a Bearer Token.
Right. So if an attacker can steal the access token they can then make API
requests and nobody would ever know that that is happening from an attacker.
So we have this extra steps the access token doesn't come back in the in
the redirect, we get a code back, and then you have to make an additional
request -- a POST request to exchange the code for an access token.
I think what what still confuses me and some
people is that it seems like if you're not-- if you don't trust the
network on the redirect how come you trust the network on the
POST request? It seems like it's still maybe has the same problem.
Yeah. So it's a it's subtle.
As we all know with security there is no such thing as 100% secure.
Right. Everything is about mitigating risk and you're playing a numbers
game. Cryptography is not perfectly secure.
Cryptography is based on the idea that it is very hard to guess numbers.
Right?
Right.
And we use we use large enough numbers where it is practically impossible
but it is still theoretically possible for someone to crack it.
So the same thing is true here right, where you obviously can't solve
everything but you can eliminate the easy attacks.
And one of the easiest attacks is stealing that redirect because there's
more ways to steal it than there are ways to intercept
that POST request.
Right. Somebody could...
even just logging of URIs that are going by.
You could see if the access token is in the URI in the query string or
something and somebody is just logging every request that goes through
some gateway or something they could even inadvertently capture the access token.
Inadvertently for sure.
Another great example is browser extensions because that is now code
running in the browser that the app developer isn't even aware of.
Which are probably allowed to see the address bar.
Certainly
If you just click "Sure give it all permissions" when you installed it.
Absolutely be able to watch the URL and watch for stuff to appear
there. Browsers now are more featureful.
You can sign into your Google account in your Chrome browser and now your
web history is synced to Google, which now means if there's an access
token in the URL now the access gets sent up to Google.
And then maybe back down to your other computers.
Now there's multiple copies of it floating around which are even more
opportunities to steal it.
And that's all a very different threat profile than intercepting that
POST request that the browser makes.
Gotcha. So coming back to what's changing -- for a long time the implicit
flow is how we did OAuth in the browser.
So what is changing?
What's the new proposal on the table?
So the implicit flow was created in a time where browser security worked
-- browsers worked differently and under constraints that are mostly no
longer relevant.
So the other main thing that has changed is browsers used to not be able
to modify the path part of the URL in JavaScript without triggering a
page reload. If you're building a single page app you don't want the page
to reload if you're modifying the address bar.
Obviously as soon as you finish the redirect you want to remove the stuff
from the query string.
So you want JavaScript to clear that out.
Now there is a fantastic API for it, the Session History Management API.
So we have all these tools available to us now that actually enable us to
do OAuth more securely in a browser which was not true 10 years ago.
So the landscape has just slowly evolved and that is the main
motivator for this new guidance.
Which is that the Implicit flow has always been a
-- I don't wanna say hack -- but less than ideal solution.
And it's just there wasn't a good story to tell otherwise.
Just like well right now this is the best we can do.
This is the best we can do right now given the current constraints.
Ten years later, tools have gotten better, ecosystem has changed, those
constraints no longer apply.
So all we needed to do was just to reevaluate the spec and re-evaluate say
like hey does this still make sense.
And the answer now is no it doesn't make sense anymore.
So the other main thing that changed is sort of in parallel to
this, the mobile app landscape has been evolving.
A little bit of evolution of that since 2010 right.
Just a little bit.
Yeah. But interestingly also in 2010 the spec was
recommending the implicit flow for mobile apps, because it wasn't obvious
why -- well you couldn't use a secret mobile apps, we knew that from the
beginning. And if you can't use the secret then the implicit flow was a
natural way to not use the secret and do the OAuth flow.
We call those public clients right.
Public clients where you're not -- you can't guarantee that the secret be
kept safe.
Exactly. Back in 2010, the implicit flow was recommended for mobile apps
but then very quickly that started changing and the
PKCE extension was developed.
So that was something that was created specifically for mobile apps as a
better solution to doing OAuth on mobile for all these same reasons.
So PKCE kind of fits fits a little gap there which is I can use the
authorization code flow which is the two step process where I get a code
and then I have to exchange it.
But normally that requires a secret.
The way PKCE solves it is instead of using a preconfigured secret that's
shared across all installs of the app it uses a it makes up a new secret
on the fly every time.
So it's like a dynamic secret.
Yeah. So every time an app wants to initiate the OAuth flow it just makes
up a new secret, hashes it, and then does the regular OAuth flow with that
additional hash value and gets the access token in the back channel.
So the PKCE... can you call it a spec?
the PKCE extension makes it possible to securely do the authorization code
flow on a mobile device without a secret but still ensure that someone
else couldn't intercept the code or steal it from you or try to beat you
to getting the access token from it or something.
Yeah.
So that's been the best practice recommendation in mobile for a couple of
years now, right?
Several years.
Yeah. PKCE is an RFC it's a separate RFC it's an extension to OAuth 2.0.
It's been working for several years now it's been in production a bunch of
places. So it was created for mobile apps but it turns out it's useful
for any public client.
Where you can't store a secret but you still want to use the best flow
which is the authorization code flow.
Yeah exactly.
So now what we're seeing is because browsers can make cross-domain
POST requests and because they can modify the address bar they can now
actually just use PKCE as written with no changes to the spec.
No additional spec needed.
So the auth code flow plus PKCE is now a great solution for browser based apps.
So the proposal in the OAuth working group is basically just -- hey we have
this spec PKCE, it works well for mobile, the landscape has changed in
browsers, why don't we use it in browsers too, and it turns out there's
there's no reason why we can't.
Yeah. PKCE is useful in browser based apps just like it is a mobile apps.
That is essentially what these two new specs are are saying.
Security Recommendations and the Browser Based Apps spec which is
the one I'm writing.
What a lot of people are wondering is, is this something that we should go
out and just go change immediately like should I go.
PKCE-ify all of my apps tomorrow?
If you have apps that exist that are using the implicit flow, there's no
new risks to you.
You have always been in a sort of risky situation hopefully
you've addressed those risks by doing all the other protections you can
against attacks.
None of that has changed.
So as long as you are doing things properly and have built a secure app
and aren't using you know random third party JavaScripts hosted on
CDNs from questionable domains then you're probably fine.
No need to tear everything out and rip it out and start over.
Eventually yes.
Roll it into your sort of natural product lifecycle migration that you are
going to be doing anyway.
Building new apps absolutely use PKCE going forward.
Gotcha. So this isn't like a CVE found zero day need to go
replace this immediately, but it's something to think about.
Yeah there's no CVE for this.
There was no vulnerability found.
So I guess coming coming at the question from a different way too...
If I do switch to PKCE using the authorization code flow in my browser
apps, does that mean that my browser app is just 100% secure?
Like I don't have to worry about any other security?
Is it rock solid?
Yeah. Good questions.
This is one of the sort of unfortunate things about browser based apps
which is that there isn't really a secure storage API in browsers.
Which means at the end of both the implicit flow and at the end of the
auth code plus PKCE the result is the same.
You have an access token.
And there isn't a good way to store that in a browser.
There isn't a secure way to store it.
Anywhere you put it is accessible to any other JavaScript on the page
which means it's accessible to browser extensions.
And if you have third party scripts on the page that you don't know what
they're doing, they could also see that data and PKCE vs Implicit doesn't
change that.
What PKCE does solve is protecting that redirect step.
So it is more secure, but it is not perfectly secure.
Because again there is no such thing as perfect security anyway.
Until we have a secure API in browsers to store secrets
that are only accessible to the single origin or whatever it is, that will
always be the case.
So there's another thing to keep in mind here which is that this guidance
is only really applicable to the situation where you have a pure
JavaScript app served by a static web server.
So think like hosting HTML on Amazon S3.
I've got React loaded straight from my CDN or something.
Exactly. Yeah.
That server, all it does is send a file to your browser.
Right. It can't do anything else.
Even if you are using a single page app framework like React and Angular,
it is often being hosted and served from a dynamic web server.
A lot of times it might be like Spring Boot on the back end or ASP.net
or PHP powering the API and then maybe like serving the files.
Yeah exactly.
And in those cases, you do the OAuth flow on the server where the server
is the thing that exchanges the auth code for the access token.
And that lets you keep that on the server where you can securely store it
and then you just maintain a session to the browser.
And that session cookie can be locked down because there are good API for
session cookies where you can limit them to not even be visible by
JavaScript for example which means nobody can steal it.
And that is the most secure way you can do it.
Keep a single communication channel from your single page app to your
server, all the OAuth is managed at the server and that's where the access
token lives and that's the thing making API requests out to your API is
that your app needs to hit.
So to recap here a little bit.
The proposal that you're working on in the OAuth working group is
basically plugging one of the gaps that.
Something could get stolen before now, can no longer get stolen.
That's a distinct improvement in the overall security of your browser based application.
Yeah but it is worth pointing out that plugging that gap still leaves
plenty of other gaps, like browsers are really leaky browsers are really insecure.
Yeah.
What are some of the other things that people should be thinking about to
keep browser based apps secure?
It's things like, make sure you know exactly what code is running on the
page. Don't just load random JavaScript from CDNs that you don't know.
Definitely use HTTPS, like none of this is secure without HTTPS.
Use Let's Encrypt.
You can get you get a free cert now
There's no reason not to anymore.
If you are for sure using HTTPS everywhere set a long lived HSTS policy
where it forces the browser to lock onto HTTPS for 6 months or whatever it
is because you know your website's always gonna be on HTTPS.
Yep. Should people think about doing Content Security
Policy on their pages?
Yeah absolutely.
Have a good content security policy so that you can better know what code
is running on the page.
There's a whole list of the kinds of things on the OWASP that's.
Open Web Application Security Project.
Yeah exactly yeah.
OWASP
and we'll put that on the screen.
OWASP. Or maybe this way.
OWASP
And that is a fantastic resource for web app security.
If you are building a pure Javascript app with no server-side component,
and you're doing OAuth in the browser, or dealing with any sort of
credentials of any sort, definitely read all of that and do all of those things.
Well if you got to the end of this video and you're still here,
thank you for watching.
If none of this OAuth stuff makes sense to you or you're struggling with
the jargon check out my video called OAuth and OpenID Connect in Plain
English we'll put a link to it down in the description.
Also have a fun talk I gave at Oktane 19 about all the different ways
OAuth can go wrong, so to take a look at that if you're interested and
I've got a blog post which talks about how exactly to do auth code flow
and PKCE in a browser.
It does not use any frameworks, it is just in pure JavaScript to
demonstrate exactly how all these pieces work.
Very cool.
Thanks for watching.