#165 Manage active sessions. Security feature

18/04/2024
security devise authentication
Transcript
Hey guys, let's talk a bit about security of our applications. So that exists, this open world application security project of us that defines different tenants and methodologies for securing your applications and thinking about the world of rails, I know Craig mna, who is an OSCP certified penetration tester. Now, what does this mean? It means, uh, uh, offensive security certified professional. So a person that you can hire to try to hack into your, uh, software. And, uh, if he, uh, successfully hacks into your software, he will provide a report of, uh, vote can be improved in, uh, your software. And if you did not manage to successfully hack, uh, your software, he will, uh, uh, well provide you with an uh, OSP certification. So he'll say that your application is kind of quite bulletproof, and this can, uh, uh, from hacks and this can help you sell to corporate clients. So the process of, uh, doing this kind of, uh, uh, hacking that, uh, you are hired to do is called penetration testing. And, uh, we are going to have a look at a few things that, uh, are commonly mentioned by penetration testers in, uh, uh, ribbon rail applications. So one thing is, uh, access control. So broken access control. So, uh, basically given I use a list of all devices that he's logged in with, and, uh, the possibility to log out of a device remotely. So, for example, Facebook has this feature. I go to my account center and I go to where you are logged in. I click on it, I see that I'm logged in from two devices. If I click on one of the devices, I will see where I'm logged in and I will have the possibility to log out of this device. Let's see how we can add this kind of feature in the Ruben Rails application here, I actually have already built, so here I'm logged in from this file browser here. I'm logged in from the edge browser. I go to the slash logins, and here I see that I'm logged in from a Safari browser. And from the current session that is from the Edge browser, I go to logins. Here, I see that, uh, I'm logged in from Safari. Now if I click on disconnect, I will refresh the other tab. And here you see, I have been remotely disconnected. So this way you can log out of any device that you, uh, possibly forgot to log out. Uh, from now I log in, once again, I refresh. You see I'm logged into two devices. Now I will log out on my own and I'm logged in with just one device. So this is, uh, sometimes considered that an important security feature that you should have, uh, in your application. And let's see how we can build this. Uh, I'm gonna start with an basic application where I have just device installed. I will run realty b roll back. Now, uh, I'll go to the main branch, and here I have, uh, this, uh, basic application. Lemme restart the server. Okay, so here is my basic application that has, uh, only device and couple of pages, the homepage and the admin page. And within the admin page requires a user to be sent in. So, uh, let's try adding this kind of, uh, logins or cations or devices, uh, manager to our application. So first of all, we are going to start with adding a login. Stable, let's say rail generate, uh, scaffold login. So a login is going to belong to a user. So user references, it's going to have a device id, uh, it is going to have, uh, uh, an IP address and user agent, okay, let's say rails to be my. Now I will go to the user model and say that a user has many logins. And, uh, now how are we going to, uh, create a login when, uh, a user sends in, remove a login when a user sends out and ensure that login exists. When the user is trying to navigate the application, let's go to our application controller and just write it down. They're going to need to have something like dev, uh, create login. Then, uh, dev, uh, destroy login. So this is going to happen when user signs in, Uh, this is when user signs out and they're going to have another thing to check if, uh, that exists a login. So, uh, uh, dev require login, okay? So, uh, require login is going to be a before action. So before action require login, if there is a current user and what is required login going to do, it's going to check if, uh, the user is logged in from this device. So, uh, let's, uh, let's start by creating this create login action. When a user signs in, we are going to create a login in the login database. Uh, they're going to say, uh, uh, current login equals current user logins dot create device ID is going to be something. Then they're going to have, uh, a IP address is going to be something and user agent, we are gonna study this. Now where can we get them? Let's, uh, see what we have in our request. Whenever we make a request, we are going to have the IP address, user agent, and we're going to generate a device. id. Let's, uh, put a debugger here and, uh, let's try to invoke this. Create login. Uh, uh, when they sign in. Now, the easiest way to invoke it can be using the device after sign in the Path four. So dev after sign in path for resource, it'll be, let's say route path, but they're also going to invoke great login. So, uh, let's go back and try to log in, uh, what accounts they have. Okay, let me, uh, register. So I'm clicking sign up and I hit the binding. Okay, I had the bin Dev running, not just rail server. So, uh, it's not going to work. I'm going to just try, have to try to restart running the rail server. Okay? Now, once again, okay, email has been taken. Let's log in with this email login. And, uh, yeah, I have this undefined method require login. I forgotten. I, let's try on scan. Login, okay, now I hit the bind in. So I'm inside this Create login. What do we have? I have IP address that I can get from request request remote ip, and I can get user agent from request, user agent. And let's generate a unique, uh, ID for the device. So let's say device ID can be something like, uh, digest, SHA, uh, 2, 5, 6, HX Digest. And we are going to generate it based on the, the, uh, request user agent and request remote ip. So let's say, uh, request dot user Agent concatenated with request dot remote ip. Now, I'm not doing it as a secure HX because a secure HX would always be different, but this is going to be always the same, uh, for the same, uh, user agent and remote ip. So this is going to be the device id. Let's uh, paste here. So we are going to, uh, create a login and we are gonna store the device ID in our session. We're going to say session device ID equals device id. Okay, now let's, uh, and start the server once again. So, uh, I'm going to log in, uh, and define method, IP address. Yeah, because I should say, uh, request remote IP and, uh, request user Agent. Okay, let's try again. Uh, login. I'm logged in and let's see if, uh, login has actually been created. I will go to slash logins and you see EL has been, uh, created. Okay, so I managed to create EL login and, uh, let's, uh, require Elgan to be present, to be logged in. Uh, let's, uh, uh, say we're going to find a login. So I will say, uh, uh, current login, uh, equals, uh, current user dot logins dot find, uh, uh, find by device, uh, id, the device ID that we have stored in the session. So there should be like a device ID stored in the session from, uh, our previous login. So it'll be session device id. And, uh, if there is a current login, everything is okay, but if current login is, uh, nil, then they're going to send the user out. So sign out that is a device method, uh, current user, and we'll say redirect, uh, to, uh, new user session path alert, uh, device, not, uh, recognized. Okay, let's, uh, try navigating our application a bit. Now, uh, let's say I will, uh, log in with this, uh, device. I click on login and you see I get this device not recognized. Why am I getting this? Because, uh, login happens before, after Sendin path is, uh, triggered. So, uh, I have no chance to actually, uh, create a login. I get logged out before this, after Sendin path is triggered. So one way to work around this would be adding a return here so that this, uh, require login that is not, uh, trigger on the, the sign in the action on the device sign in. So I will say return if, uh, controller path, uh, equals devise sessions. And uh, action name equals great. So, uh, when logging in, we're not going to require login. This kind of makes sense. Let's see if this works. Now I'm going to try to log in and I manage to log in. Let's see how many logins I have. I have two logins. I will, uh, try to log out of, uh, this, uh, safari device, uh, destroy login. I refresh. And you see I have been logged out, so it kind of works. Uh, yeah, looks nice. Uh, now, uh, let's try login in onesca. I'm logged in again, I have two logins. I will log out on my own and I still have to logins, so I need to manually destroy the login. Then I'm, uh, uh, signing out. So, uh, I'm going to say, uh, current user logins, uh, find by device ID session. Device id. Uh, if we find it, we're going to destroy it and we're going to, uh, set the, the session device ID to nil. So I'm going to say session delete, uh, device id. Now, how can I trigger this? Destroy login. I cannot do it in the after send out path because, uh, I will already not have a current user. Then I hit the after send out path for device. So I'm going to actually have to hijack the device sessions controller for this. And, uh, to do it, I'm going to generate the device sessions controller. I'm going to say, uh, rails generate, uh, device controllers. I'm going to name specific within users, and, uh, I'm going to generate only sessions. I'm going to update my route. So instead of just having device for users, I'm going to use our custom sessions controller. Let's open the custom sessions controller. And here I have, uh, the new create and destroy actions that I can override and inside destroy. Uh, before running the actual destroy actions, I'm going to, uh, run, destroy login like this. Let's, uh, see if it works. Now I'm going to log in. Once again, I get device not recognized. Why do I get this? Because, uh, now I'm not using device sessions. I'm using, uh, uh, users slash sessions. So, uh, instead of this, I'm going to have users slash sessions. Let's try once again. Okay, it failed. And uh, now I have to logins. I'm going to log out here. I refresh this one, I still have to logins. This doesn't look right. Let's, uh, let's try removing this one manually. I will again click on login. So I have to logs, I log out, I have one login. Okay, so now it, uh, worked. Looks good. And uh, also you don't have to have this great login inside the atan path. You can also move it to the sessions controller since we have created it. So we could do it somehow, like, uh, dev create. So we are going to say super dual resource, and we are going to run, uh, create login, uh, if resource persisted in our case resource is user. And now we don't need the after sending path anymore, or at least the create login that we put inside it. And let's see if, uh, everything works now. So, uh, just in case, I will restart the server. And, uh, here I am. I have a couple of tabs. I'm going to log in here. I have two sessions. I will, uh, show the login, uh, remove it. I have logged out. I log in here, I go to slash uh, logins, and lemme try to remove this, uh, edge, uh, browser, EDG. So show and destroy. And I have logged out of, uh, this other browser while staying logged into this one. So it kind of works. Uh, obviously you would want to clean up your, uh, uh, logins controller. So here you don't need most of these actions. You only need the index and, uh, destroy. And, uh, you are going to allow a user to access only his own, uh, data. So they're not going to have all logins. I will have current user logins, and here it's going to be current user logins. Find. Now we don't need to respond with different formats, so it just going to be a redirect. Here. We have cleaned up, uh, the controller. Let's also clean up the login views. We don't need, uh, most of this. Let's, uh, just leave the login, uh, index. And, uh, what we have, we have, uh, uh, we want to have a remove button straight on the index. Let's, uh, add it. We don't need all these. We are just going to add, uh, equals button to delete, uh, login path at login method, delete. Okay, so here have the delete button. And let's also, uh, not allow the user to log out from the current device that he is, uh, in. So we can log out of other devices. We can not log out from the current device. Let's go to slash logins and let's display the current device. How can we, uh, do this? Uh, let's say If login dot device ID equals session device id, then we're going to say that this is the current device else. They're going to have the button to delete. Let's see here. We have a delete button. Here we have the current device button. Okay, it looks quite good. And let's, uh, add some additional information about the specific device that we are logged in into. And we can do it based on our user agent. Let's know whether this is a tablet or a desktop or a mobile phone. We can do it with using the GEM device detector. So Gem device detector. So based on the user agent, it can give us different additional data. Let's install this GEM bundle. Add the device detector. I made typo. Let's see if the gem has been, uh, added to the GEM file. Okay, I must have typed it correctly this time. And, uh, let's get some additional information about, uh, our device. So, uh, uh, let's, uh, run the device detector somewhere. Um, somewhere here we can, uh, say equals uh, device, uh, details. Login, user agent, let's create this method. Device details in the helper, login, helper dev device details based on the login. Uh, yeah, based on the user agent. And let's put a debugger here. So, uh, I'm going to start the real server. And here I'm in the debugger and I have the information about the user agent. And, uh, we can draw the device equals device detector. Uh, new based on this user agent, and we have some additional information. Let's run device and we can get.name. So Safari OS name, uh, uh, device type. So we know that it is Safari Mug desktop. Some good additional metadata. Let's, uh, display this data also, uh, in, uh, our helper. I'm going to say we have device, uh, name, device or type and device. Uh, device type. We're going to display them. Uh, how did I invoke device detector? Uh, join, uh, with, uh, slash for example. Okay, let's skip it now. And now when I refresh, you see, uh, instead of just displaying this user agent information that is not very useful, I can display that this is Safari Mug Desktop, and this is Edge Mark desktop. Uh, so yeah, that looks much better. Uh, and just this way you can allow a user to see his, uh, logins or devices that a user is logged in with and look out of other devices Here, I see that, uh, yeah, let's just add some border because it's not truly visible and let's remove the great action. We can't really create it. Okay, here we go. So here we see information about our logged in devices. I see that the, this is the current device and I can log out from another device. I clicked on, log out here. I refresh, I'm logged out. Uh, if I log in again, the device has been added. If I log out, the device has been removed, so looks quite good. And just like this, you can allow a user to, uh, have a list of all the devices that, uh, he's logged in with, and remote log, log out from another device, just like Facebook does it here. And your application is going to be, uh, considered more secure. So thanks for being with me and see you in the next one.
1
Join the conversation
Sign in to access PRO lessons, access private repos, leave comments, create watch lists.
We collect your email address, name and username to create your account. We do not share your email address with anyone else.