Welcome, Warren, and first yours.
Thank you.
That's not necessary.
Sorry for starting a little bit late.
This is building a security first API.
I'm Warren Parade and I'm the CTO at Authress, which is a widely used authentication authorization
API.
That means we get a lot of requests, API requests, that is.
So let's talk about that a little bit.
Today in the world, there's about one trillion requests per second.
Get on the slide.
This is public internet and from recent research published by Akamai, about 83% of these requests
are purely API related, machine clients, services, IoT devices.
From our own research, 4.9% of these requests are malicious in nature.
That means for every 20 requests your service gets, one of them is from a malicious attacker
that's attempting to compromise your service.
That's a lot.
And I think we're all in this room right now because we know we have to do something about
it.
But there's a lot that we could be doing.
So many things, in fact, that if I were to stand up here and talk through each and every
one of these, we'd be here long after the conference was over.
Luckily, I'm not going to do that.
But also, doing that may not actually have the impact that you wish it to.
That's because some of these may not be relevant to the service that you're building, functionality
that actually is in your API.
To figure that out, we actually need to do and build a threat model.
A threat model is intentionally deciding what a malicious threat actor could potentially
do to compromise your service.
This is going to be unique based off of the data that you're saving, the way you've built
your service, the infrastructure, and also your cloud provider of choice.
However, I'm going to say that some threats are ubiquitous across many, if not all, APIs
that we're building.
So what I'm hoping to do is build up a common threat model that we can utilize to actually
target solutions to these issues.
So let's start.
The first one is injection attacks.
These come in many forms, where a malicious attacker attempts to construct a custom request
into your service to cause it to execute unexpected code paths or flows.
Common example are SQL or database injection attacks.
These attempt to execute an unexpected SQL command against your database, such as drop
all tables if we're lucky.
Another type is host command injection.
So executing a privileged action against our host operating system or virtualization
layer.
And the last kind is server side request forgeries.
That's just a convoluted way of saying that your service has credentials that are used
to interact with a third party integration, and an attack may attempt to utilize those
credentials on its behalf.
So let's add all of these to our common threat model.
But we can go further.
The application security program, OWASP, has listed the top two most concerning things.
Number two is broken authentication.
We don't have any authentication on our API whatsoever, or what we have added isn't
sufficient to actually identify malicious attackers.
And this is number two.
That should be telling us something.
Number one is actually broken object level authorization.
This lack of authentication, but lack of authorization to validate whether or not the request that's
coming in actually should be allowed to execute.
Usually it's due to a lack of granular access control.
And we can go further.
These are only the one directly at our API, but we have infrastructure to consider as
well.
An malicious attacker could attempt to utilize our API indirectly to affect our infrastructure
via a DDoS attack.
Or it could attempt to inject a malicious code into one of our dependencies through
a supply chain injection attack, software or otherwise.
And lastly, they could attempt a physical intrusion into our data center if we have
something on prem or a virtualization later or a cloud provider or the cloud provider
itself.
And the attacks aren't just specific or non-specific to services in general.
You have to think about how the functionality of your service is relevant, what you're currently
building.
A malicious attacker may attempt to utilize how your resources are built, how your end
points are functioned specifically in your case.
In AuthWrist, we offer multi-tenant security capabilities that allow our tenants, our customers,
to create customized identity providers, custom user logic, even give us custom URLs.
They can attempt and have attempted to utilize that configurability to compromise adjacent
tenants at customers across our whole product.
Okay, that's I think enough for a threat model that we can all agree are problems across
the board.
Now I want to jump to actual solutions.
First up is input validation.
To deal with the threat of injection attacks, we can add input validation.
And I honestly, I feel kind of silly putting this slide up here because I feel like most
of us know about this problem.
But I feel like every single day, every week, I hear about some problem with some public
company who had an issue that could have been resolved by verifying that the request
they are getting matched their expectations.
Well, we already thought about our expectations a little bit.
And if you were listening in the last talk, we learned about the open API specification,
which documents our API in a programmatic way.
Well, we could potentially take that programmatic documentation and utilize it to verify those
same expectations on the request we're getting into our service.
Here's an example from Authris where we have group management.
This actually creates user groups.
And on the left, you can actually see the schema for this endpoint.
It's fairly bare here and uses a common component groups.
On the right, you can see the example of an open API Explorer tool automatically rendering
it.
So we can take the schema and what I want to do is build up a place where we can store
the validation of all of our endpoints.
So when we add additional endpoints to our service, they will automatically get the security
of input validation.
So let's create a security middleware.
And I've got one here in JavaScript.
But you can be using, of course, any language.
There are open API specification-based tools that work in whatever framework you're using.
And I'm loading the spec up here and passing in the method, the path, the body, the headers.
And this will get executed on every single request that we get to our service, irrelevant
of the endpoints that we have.
And just like that, hopefully we've eliminated injection attacks.
But why stop there?
Now that we're opening up the request to do validation, we can start thinking about authentication.
And authentication is required because if we don't know who the user is, we don't know
if they're a potential malicious attacker.
We have no way of identifying them across requests.
And while we could be using something like IP addresses or some fingerprinting that's
blocked by most browsers today, none of them are as effective as just using user identification
through authentication.
And like this, we can hopefully close out the broken authentication threat that we have
in our threat model.
And usually entails having a trusted identity provider.
Someone that can generate access tokens on behalf of our clients, who may be machines,
IoT devices, even end users, those tokens usually look like JWTs.
And they'll pass us those JWTs into our API through the authorization header.
From there, we'll grab some data from the identity provider that allows us to verify
those tokens.
So let's just see what JWT actually looks like.
And I'm sure someone's going to call me out and say it's pronounced jot according to
the RFC.
So I'm just going to say I absolutely know that.
But not everyone knows what a JWT is.
And so it's easy to remember by the letters rather than how to pronounce it.
So I'm just going to keep saying that a JWT has properties that can be completely configured,
but at least it contains ones that have the identity provider where the token came from.
User ID that the token represents.
Usually there are short lived token that expires.
I created this one and it's actually going to expire soon during the conference.
And signature that allows us to verify it.
So now let's extend our security middleware to close out the broken authentication.
We can just add another method in here that allows us to extract those important fields
out of the JWT and then verify them.
And maybe we're done.
Now I think we got to be careful not to fall into a trap here.
All of our endpoints need the same sort of authentication.
All the services in our system may have a similar concern, right?
We want to verify on every endpoint.
We may be tempted to delegate that security to another team in our company or a single
component where all the requests pass through.
However, fundamentally every single one of your endpoints is at stake and you're likely
on the team accountable for the security of those endpoints.
So when we add another component such as an API gateway into our system, we're really
left with two options.
The first one is to completely ignore that component, get the tokens into our service
and verify them.
The alternative is to not trust that identity provider that's giving us the tokens and somehow
trust the API gateway instead.
If you're doing that, you may think, well, our identity provider isn't necessarily providing
us what it needs to do.
Maybe think about changing it.
And like this, we can eliminate broken authentication.
But it's important to remember that identity is not security by itself.
Just because we know who the user is doesn't mean that they're actually allowed to perform
the action on our API that they're attempting to request.
To do that, we need to introduce authorization.
Admission is verifying that the machine, service, or user actually has permission to call the
endpoint that they're calling.
And to stop the broken object authorization, BOLA, for every endpoint, we need to consciously
decide what is the purpose of this endpoint for the product, what permissions make sense
to actually check, and who should have access.
The simplest thing we can do is add permissions to our JWT.
Users get blanket permissions to everything basically via a property in the JWT.
And you'll notice that there are no resources listed here.
It's just a list of permissions.
And this is the bare minimum to provide authorization in your API.
And it only works really in simple cases that where most of your data is public, users don't
really have that many roles, or the users don't really interact with each other.
However, in all but the simplest cases, it's likely not sufficient because it doesn't
provide granular enough control.
You can't specify the resources here.
And in order to stop the broken object authorization, we need to achieve granular access control.
So we need a different access control strategy.
And like that, we'll introduce resource-based.
In resource-based, individual users get assigned specific permissions to resources.
And with that, we can actually verify that they are authorized to call our endpoints,
or the specific endpoint.
Okay, let's extend our security middleware and close out the Bola threat.
And we'll do that by just adding another line of code to our security middleware.
And here I have an example from the author's SDK, but of course you can use any SDK, any
product that allows you to do this verification.
Or if you're feeling adventurous in the land of security, you can try to do it yourself,
not something I normally recommend.
And with granular access control, you can actually scope down the permissions of each
of your endpoints to only what is absolutely required.
And this is known as the principle of least privilege.
Now it should be self-evident that the more granular our permissions are, the more secure
our API is.
Because if we don't have granularity, then users likely probably have access to do too
much in our service.
And a malicious attacker that gains access to or impersonates one of our users while
having access to potentially all of their data.
The only way to prevent Bola is have a granular access control strategy and implore the principle
of least privilege.
Don't be this company that asks to delete all of my emails and all of my calendars.
It's just totally unnecessary.
Also don't be that company that lets people do this.
I should at least be able to uncheck just the delete part.
Yeah, sure, maybe I trust you to read and compose some emails.
Read just some, not all of them.
And this is actually about as far as we can get with static code in a middleware that's
running.
If we want to deal with some of the threats around our infrastructure, we need to take
it to the next level and add some monitoring and logging.
We've taken care of most of the threats at this point, but there are still some left,
right?
Now if we take a look at how the DDoS text works, taking the next step is potentially
throwing some sort of additional component into our architecture that allows us to detect
when there's a problem.
And we can do that by adding what's usually known as an API firewall or a web application
firewall.
We'll attempt to dynamically block attacks as they happen.
It can contain dynamic rules that look for suspicious activity and are executed based
off of multiple request patterns.
Some requests we'll get through and we'll end up logging them.
We have to make sure we're logging them.
And then we can process that and potentially look for patterns within our service, maybe
a couple of 200 in a particular way.
And we can use that to dynamically update our rules.
And if we identify something that the firewall isn't blocking at that moment, well, we can
actually update those rules and stop an attack as it happens.
And those updated rules will continue to live on into the future.
So you throw in some monitoring and logging and you notice something, a spike that looks
like this.
Is this a problem?
Does it look like an attack?
Who thinks this is an attack?
No one.
Everyone thinks that this is totally normal behavior.
I mean, users change things all the time.
So your customer causes this to happen in your service and you're like, whatever, it's not
a big deal.
And it may not be anything.
Realistically, spikes like this don't mean that much.
What's important is what's relevant for your product.
For us, we monitor and validate what we call the authorization ratio.
That's successful, authorized request to ones that are actually blocked, where the permissions
don't match or something fundamentally changed about how it's being used.
Even though this came up as one spike, the truth is that this was actually two individual
problems that are API firewall, caught, and our anomaly detection, which this is actually
ran to grab and pull out.
This is actually from Grafana.
Depending on the severity of the attack, the rules may completely shut out all requests
from a particular customer or a specific service client, depending on what's actually going
on.
OK.
I said a lot of things, so I would like to summarize.
The most important thing we can do is identify our threat model.
We built one here and it includes injection attacks, broken user authentication, Bola,
and DDS attacks amongst some other ones.
Then we created custom solutions specifically to deal with our threats, validating our inputs,
having user identity, granular access control, and then adding dynamic rules.
This is great when everything goes to plan, but sometimes it doesn't.
And when it does, we need to really understand what we're doing.
So let's take a look at some things not to do.
The first one is missing our threat model.
I think I've said this enough.
If we don't have a threat model, then we don't know what we're building is sufficient, useful,
or even relevant.
It's probably even harmful.
Throwing security over the wall.
It's nice to be able to utilize components from another team to help us build up a more
secure API.
But fundamentally, your APIs are your responsibility.
If something happens, who is held accountable for the lack of security there?
It isn't a replacement to use another team.
Internal services.
There's no such thing as an internal service.
There's just services that don't have any external requests yet.
Lateral attacks from other services from malicious threat actors may still end up in your API,
utilizing a proxy, a gateway, or another service that could be one of your only customers
inside your company.
At some point, your service is going to become public or have public callers, potentially.
And if you haven't built your service with security in mind, then you likely have some
of the threats that we've actually added into our threat model already today.
So treat all your callers as externals and that your API could be considered public.
Building monoliths.
I think at this point by 2024, we should know that microservices are more secure than monoliths.
Sources have dedicated boundaries of which we've implemented authentication and authorization
at those boundary layers.
If something were to happen in one of our services and it becomes compromised, there's
a bulkhead door that's closed by default.
Whereas in a monolithic system, if any component becomes compromised, then our whole system
becomes compromised.
And the last one is building it yourself.
Try to build or maintain a component that isn't your core competency.
And while it may seem like a clever way to get around a supply chain attack, in reality,
it means you're just volunteering to take full responsibility for whatever that component
is and all the vulnerabilities that show up there.
You and your team are probably not going to be able to beat out a team of experts who
manage some open source component out there that is being checked continuously.
Now the counter example argument to this is, well, what if it's a critical component?
Well, the truth is everything in our service is a critical component in some way or else
we probably wouldn't have included it.
You're not going to get very far with the resources you have attempting to build and
maintain literally everything in your stack, including making your own electricity.
Okay.
So hopefully at this point, you know exactly what you need to do to add some additional
security to your service.
I have a quick link to the presentation if anyone actually wants it.
I see too many people getting on phones.
It's going to be available online, so you don't necessarily have to, but if you really want
to.
Okay.
One more.
Five, four.
Okay.
And thank you.
Okay.
And thank you.
Okay.
Now we have some time for some questions.
Yeah.
Perfect.
Five minutes for questions.
Go ahead.
I'm not sure are you operating in the Europe company?
Our company is global.
So yes, Europe is included.
Oh, yes.
Sorry.
It's our company operating in Europe.
Yes, for sure.
It's operating in Europe.
The reason I ask is that we have some new regulations according to data protection.
Yeah, for sure.
And the IP address is considered to be one identifying characteristic for a person.
Yes.
Yeah.
How do you deal with the GDPR or whatever local regulation is for identifying information
such as IP addresses?
I mean, the sad story about IP addresses is like forget about it honestly.
Because with IPv6, which is going to be here pretty soon, you're not going to be able to
do anything remotely reasonable with IP addresses in the first place.
So stop logging them.
For us, what we actually do is edge computing for those customers.
So wherever they're coming from, most of their data stays at the edge as much as possible.
So if you do have those regulations, try to keep it there and don't send it other places.
All the logs are resident to that data center or those databases in that location.
It still allows you to perform security on the edge.
And wherever you're running your stack, whether it's on-prem data centers, hopefully not in
every single country or some cloud provider, they're usually following those regulations as well.
So as long as you keep the data as close to that region as possible, you're abiding by the law as best in your ability as you possibly can.
Go ahead.
Yeah. We do validate all incoming requests against the schema, but that doesn't necessarily mean you're safe.
So two easy examples could be a build just really, really long that leads to a DDoS.
Or they could be some SQL in there and a laser developer stripping it before it goes into the database.
So is there a way to add that kind of validation to the OpenAPI schema or would you have that as a separate set?
Can you add extra validation to the OpenAPI schema?
You absolutely can.
There are even vendor extensions.
I mean, if you want to put your whole everything in your OpenAPI schema, you can have, there are tools out there to dynamically generate the whole service if you want to.
I don't know if I'd recommend going that far, but you absolutely can.
Just to be clear, you can't block everything because you don't even know if you are blocking everything realistically, right?
Something that undiscovered zero-day that's sitting out there could still be waiting and happening.
So there is no full guarantee that you've actually even blocked everything.
Go ahead.
What are your pro tips regarding the use of refresh tokens?
My pro tips for using refresh tokens.
You probably don't need refresh tokens ever, and if you're using them and you believe this is standard or common, you probably don't fully understand the use case for them.
Unless you're dealing with a third-party technology or interface integration, you don't need refresh tokens.
Just forget they even exist.
So it was just within your own system.
There's no need for refresh tokens.
There's plenty of alternatives that are more secure.
I have a whole talk on that, I guess.
Does that answer your question?
Okay.
Anyone else?
I still have a couple minutes.
Don't be shy.
Yeah?
Go ahead.
Why?
Why what?
About the tokens.
Why?
Refresh tokens allow you to impersonate in your own service one of your users to access their resources.
So Google Drive, for instance, a refresh token allows you to access Google Drive as your user.
If you don't have integrations with third parties, then there's no reason to have refresh tokens.
There's no third-party system where you need to impersonate a user.
Your own users don't need to be impersonated.
You already can perform every action within your service that you want.
There's a lot?
Yeah, okay.
Thank you.
That's...