Security Tip: Login Logging
[Tip#25] Try saying that fast 3 times...
Greetings my friends! I hope this email finds you well. 🙂 This week I want to encourage you to add logging for your logins. As you’d expect from Laravel, it’s really easy to add, and it has some nice benefits from a security point of view. (It’s also super helpful when debugging weird login issues!)
In other news, Laravel Security in Depth recently hit 1,050 subscribers, which is just incredible. Thanks for all of your support, and I hope you stick around as we continue to grow! Also, don’t forget you can send in suggestions for topics you’d like me to cover, and tell all the Laravel folks you know to subscribe!
Laravel Security Audits: I have a couple of openings at the end of the year (around Nov-Dec, and possibly earlier), so reach out if you’re interested. 🕵️
Do you log login attempts in your app?
Both successes and failures?
Unless your app has some very specific privacy requirements, you should be logging all login attempts. Both failures and successes should be recorded, alongside both the IP address and email/username used in the login attempt. (But don’t record the password!1) Even if it goes into some log you never look at, and is rotated out in X days, it’s still better to have login attempts recorded somewhere.
Why? I hear you ask…
In the worst case scenario that your app is hacked, you’ll want to review login logs to see if that’s how the attacker got in. You might notice a significant number of failed login attempts and one successful - that could indicate a credential stuffing attack on your site. Or maybe no logins were recorded - that could indicate a vulnerability that allowed the hacker access. When you’re tracking down a breach, you‘ll want this information.
Maybe you’ve had some customers reporting weird activity in their accounts - you can check the logs to see if there are suspicious logins.
If your site is under heavy load, lots of login attempts in the log could indicate an active brute-force attack.
If a user is having trouble logging in (or at least reporting having trouble), you can check the logs to see what username they are attempting and go from there.
Logs are like backups: you hope you’ll never need them, but you’ll wish you had them if you don’t!
Laravel makes logging logins trivial through the authentication events:
Illuminate\Auth\Events\Registered Illuminate\Auth\Events\Attempting Illuminate\Auth\Events\Authenticated Illuminate\Auth\Events\Login Illuminate\Auth\Events\Failed Illuminate\Auth\Events\Validated Illuminate\Auth\Events\Verified Illuminate\Auth\Events\Logout Illuminate\Auth\Events\CurrentDeviceLogout Illuminate\Auth\Events\OtherDeviceLogout Illuminate\Auth\Events\Lockout Illuminate\Auth\Events\PasswordReset
The ones you’ll want to reach for first are:
I’d also suggest logging registrations and password resets, for the same reasons:
You can register to listen to these events in your Event service provider like any other events, and simply check the class of the event to find out what parameters it includes.
For example, the Login event comes with:
/** * The authentication guard name. * * @var string */ public $guard; /** * The authenticated user. * * @var \Illuminate\Contracts\Auth\Authenticatable */ public $user; /** * Indicates if the user should be "remembered". * * @var bool */ public $remember;
And the Failed event:
/** * The authentication guard name. * * @var string */ public $guard; /** * The user the attempter was trying to authenticate as. * * @var \Illuminate\Contracts\Auth\Authenticatable|null */ public $user; /** * The credentials provided by the attempter. * * @var array */ public $credentials;
You should have no problems extracting the details you need to throw into the logs from there. 🙂
To give you an example of how I would log logins, this Gist is based off one of my projects: https://gist.github.com/valorin/b90dc36197f47323a9207093f6a6dfc5
In an entertaining piece of irony, I’d accidently typo’ed this bit so it originally said: “But please record the password!”… whoops! Glad I noticed in time. 🤣