Silent Bugs Overview0:00
Some of the most scary types of bugs are silence. They don't have errors. They certainly don't blare at you saying, this is wrong. No, there, there's nothing that goes on. And then the most dangerous kinds of bugs are the silent bugs that do actual harm. Something like, you know, a security hole, that's never a good thing. So here's the situation. Our back office team has a set of reports
Unauthorized Refund Reports0:26
So here's the situation. Our back office team has a set of reports and there are some refunds that shouldn't be there. There are naturally some refunds that an admin user has approved, but there are some that admin doesn't look like has approved. So we can see here that if we look at any order, we have the option to update the status and refund. It is one of those. But we can also take a look at the order page that the user sees.
It is one of those. But we can also take a look at the order page that the user sees. We can see that it's processing. We see all of the products that they've purchased, the total, and then there's a button to issue that refund. So this is something that only an admin should be able to do. A regular user can't just issue their own refund and we could take a look at that. If we sign in as our normal user,
and we could take a look at that. If we sign in as our normal user, then we will be able to view our orders. And if we scroll on down, let's just pick one. Uh, this one right here. If we take a look at this order, we can view the details. And here we can see that we no longer have the button to request a refund. We can download the receipts, but there is no refund. So this is exactly what we would expect,
We can download the receipts, but there is no refund. So this is exactly what we would expect, at least from a UI perspective. Now, an error like this can be caused by really anything. The best case scenario is there's some kind of processing issue on the admin side. The worst case scenario would be an actual security hole. Let's hope for the former. And if it's the latter, then we will just get affixed. So there's many different ways that we could approach,
Investigate Database in Tinker2:04
And if it's the latter, then we will just get affixed. So there's many different ways that we could approach, you know, investigating this type of bug. I think first of all, we need to take a look at the database and we can do that with Tinker because we can use our models. So we are going to fetch the orders or the status is refunded and we want, you know, a reasonable amount of time since it was last updated.
and we want, you know, a reasonable amount of time since it was last updated. Now, according to the back office people, it has been, uh, relatively recent that they've seen these unauthorized refunds. But we also want to include the user information. We want the id, the order number, the total, blah, blah, blah, blah, blah. And then we're gonna format that information so that we can read it a little bit better.
And then we're gonna format that information so that we can read it a little bit better. And if we take a look here, then yes, we have a dump. We can see where there are some refunds by the admin user that is to be expected because they are an admin. But here we can also see that Eloise, well, I'm just gonna call her Eloise because I'm not gonna try to pronounce her last name. Eloise has issued some refunds and I'm pretty sure Eloise is not an admin,
Eloise has issued some refunds and I'm pretty sure Eloise is not an admin, but we could find that out very easily because here we are in Tinker, all we want to do is find a user where the name is Eloise, whatever that last name is, and we will get to the first. And here we can see that this is ID of Tin, the name is Eloise. And then sure enough, this user is not an admin. So it starts to look like it's a security issue.
Audit Routes for Authorization3:37
And then sure enough, this user is not an admin. So it starts to look like it's a security issue. You know, we could take a look at the routes themselves, we can find the code. Here it is, nevermind the comment there. But here we can see. Yes, absolutely there is no other middleware being used. Now, yes, an authorized not authorized, an authenticated user can only issue a refund. So thankfully a user has to be authenticated,
an authenticated user can only issue a refund. So thankfully a user has to be authenticated, but it looks like that there's no authorization going on. Whereas if we take a look at, you know, some of the admin stuff, you know, here's our admin routes, we can see that that is protected with the admin middleware. But of course this is just one of the places where we can set and check for authorization. You know, there could be, you know, a policy applied inside of the controller or something like that.
Reproduce Exploit in Browser4:30
You know, there could be, you know, a policy applied inside of the controller or something like that. So one of the things that we could do is just go back to the browser and we can just try to replicate this, you know, bug ourselves. And this would kind of be a definitive thing, so that if we can actually, you know, submit our own refunds, then we could save ourselves a lot of money. Well, we wouldn't save ourselves money because we would be losing money,
Well, we wouldn't save ourselves money because we would be losing money, but you know, the customers would be saving themselves some money. So we went to fetch orders and since we have this 24 62, that's just the order that we are going to try to issue a refund for. And we want this to be a post request. So we need to include that, but that's not enough. We also need the headers
So we need to include that, but that's not enough. We also need the headers because we've got to include the X-C-S-R-F token header. That's gonna come from query selector. We want the meta tag that has the name of CSRF dash token. We want the content of that. And we also want the accept header, and we'll just say application.
And we also want the accept header, and we'll just say application. I always spell that wrong, Jason. So that is going to make a request. Then we want to do something with the response, such as call the Jason method so that then we can write that out to the console. And I don't think that that's actually, uh, going to write something out because we're gonna get an issue because what we're getting back isn't Jason.
to write something out because we're gonna get an issue because what we're getting back isn't Jason. But the important thing is to see what we have here. If we refresh, sure enough, our order is refunded. So we've just verified that security hole there. There's no other policy in place for protecting the refund endpoint. So yeah, worst case scenario, users can issue their own refunds. Now, for the sake of completeness,
users can issue their own refunds. Now, for the sake of completeness, let's take a look at the order controller. We'll start with the show method. Even though this isn't the method that we really need to look at, we're still gonna look here because there are some checks that need to be done here, such as is the orders user ID equal to the currently signed in user's id, because we don't want, you know, any user
to the currently signed in user's id, because we don't want, you know, any user to look at any order. We want the user to only be able to see their own orders, basic stuff. But we can see that that is done here. And of course, if the user's an admin, then you know, we let them see it. But if we take a look at the refund, well, what do we do? We log and then we check if the order can be refunded
But if we take a look at the refund, well, what do we do? We log and then we check if the order can be refunded or rather, if it can't be refunded, otherwise we return there. But then there's nothing else, there's no other checking, there's just an order refund. So really this is, uh, a double failure because if we look at the routes, we know that our refund route is not protected with the admin middleware.
that our refund route is not protected with the admin middleware. So we have that, that's the first failure. The second failure is that there's no policy or anything like that implemented to ensure that the user is authorized to refund. And if any one of those things were in place, then you know, this would've been averted. So, you know, of course the, the solution to this is really twofold.
Fix with Layered Security7:58
So, you know, of course the, the solution to this is really twofold. The first thing to do is to simply add the middleware that we need to protect this route. So we could call middleware, we want the admin middleware, and then voila. Now only admins can issue refunds. But you know, security is a layered thing. There's no such thing as absolute security. Anything can be broken, anything can be hacked.
There's no such thing as absolute security. Anything can be broken, anything can be hacked. But if we add layers to our security, then we make our thing more secure. And the, there's really three layers to it. The first layer is the UI layer. We show or we hide the things that we don't want, just, you know, normal users to see. So we have that tackled. A normal user cannot see the refund button. Perfect.
So we have that tackled. A normal user cannot see the refund button. Perfect. So that's the first thing. The second thing is applying the appropriate middleware to the route, which we didn't do, but now we have. So that's gonna be the second layer. The third layer is writing a policy. And that might seem redundant, especially since we have now protected the refund route with the admin middleware.
especially since we have now protected the refund route with the admin middleware. But remember, security is a layered thing. Yes, it takes extra time to create a policy to implement it, to register it, and to use it. But if for whatever reason someone actually made it past the admin middleware, the policy would be in place to ensure that the user had the ability to issue a refund. And if they didn't, then you've saved yourself there. So always think in terms of layers when it comes
And if they didn't, then you've saved yourself there. So always think in terms of layers when it comes to security, because it's not enough to just hide buttons. You have to enforce rules at every level.
