Most of the time, hackers aren’t breaking in; they’re logging in. The vast majority of security breaches don’t follow the Hollywood cliché of hoodie-clad figures overcoming a system’s security measures through sheer technical know-how (and declaring “I’m in!” once they’ve succeeded). Most of them “go through the front door” — that is, via the login box — using credentials that they’ve acquired through con artistry, coercion, or a co-conspirator.
How do you catch malicious users when they’re accessing your applications like legitimate ones? By making them think they’re accessing applications like legitimate users when they’re actually announcing their presence. You can do this with Honeytokens — and a little help from Auth0 Actions.
Honeytokens
Honeytokens, as their name implies, are digital resources that look “sweet” to attackers but, in fact, are decoys whose use means that someone unauthorized has gained access. They are the data version of honeypots, decoy systems that lure attackers away from the real ones.
Honeytokens come in many varieties, including:
- Designated access tokens, API keys, or secret values that, if used, are a sign that an unauthorized party is accessing your API
- Files with tantalizing filenames or seemingly valuable and confidential contents that cause an alert event if copied or read
- Records in a database or filestore that, if accessed, is a sign of intrusion
- Fake user accounts that, if used to log in, indicate that attackers have somehow acquired a collection of login credentials
The information in Honeytokens isn’t real and serves no purpose other than to lure unauthorized parties into accessing or using it. Just as a honeypot mimics a real system, honeytokens mimic real data — and both are designed to trick attackers into revealing themselves.
Honeytokens provide these benefits:
- They’re a proactive early warning system for malicious activity
- They can help security teams observe and learn from the way attackers interact with their systems
- They’re cost-effective; their expense comes from the time required to set up and monitor decoy accounts, records, and files
Honeytokens got some high-profile promotion at the 2023 RSA Conference in a presentation titled The State of Cybersecurity – Year in Review given by Google Cloud’s Kevin Mandia. In it, he says that honeytokens are one of the best ways to counter what he calls the “apex attackers.” “If you have a few dummy accounts and build rules around them that any use of them is bad, that’s a great move,” he says.
In the abovementioned presentation, Kevin Mandia talks about honeytokens at the 17:17 mark.
In this article, I’ll show how you can combine Honeytoken user accounts with Auth0 Actions to add a security measure to your applications.
What are Auth0’s Actions?
Actions are serverless functions hosted by Auth0 that activate in response to specific events during authentication workflows, such as when the user logs in, signs up for an account, or updates their password. You can write your own custom Actions in Node.js (complete with support for NPM Modules) or use one of the many third-party Actions in the Auth0 Marketplace.
Once you’ve written or acquired an Action, you insert it into the workflow you want to modify using a drag-and-drop editor. This system makes it simple to see 1what the workflow does and allows administrators who don’t code to change it without requiring developer intervention.
All you need to see how Auth0 Actions can help secure your application is an Auth0 account. If you don’t already have an account, you can sign up for a free one.
Observing Logins with Auth0 Actions and the Login Flow
In this implementation of honeytokens, we’ll make use of a couple of Actions’ key features:
- The Login flow
- The
api.access.deny()
method
The Login flow is the authentication workflow that takes place immediately after the user logs in and before Auth0 issues an ID token. You add Actions to the Login flow to modify its behavior, as shown below:
You can use an Action in the Login flow to compare the email address of the user logging in to a collection of Honeytoken email addresses. If there is a match, you know the user is malicious, and you’ll have to decide a course of action:
- You could deny access to the user. In this case, you would display an ordinary-looking error message, such as a 500-series HTTP status code, but internally log the event as someone attempting to log in using a Honeytoken account.
- You could grant access to the user but keep them under close watch. This is riskier, but it can give you clues about what the malicious party is after, how they work, and possibly who’s employing them. This approach requires a good “blue team” and is complex enough to be beyond the scope of this article.
Most of the time, you should opt for the first option, and that’s what we’ll do in this example.
Since the Action we’ll define will deny access to honeytoken accounts, we’ll need the api.access.deny()
method. It allows an Action in the Login flow to apply additional requirements before granting the user access to the application. In this Action, the requirement will be that the user must log in using an email address that isn’t for a Honeytoken account.
Here’s the syntax for calling api.access.deny()
:
api.access.deny(reason)
It expects a single parameter: reason
, a string value containing the user-facing message explaining why their login request was denied.
For more details about using Actions to allow or deny login requests, see this article: Permit or Deny Login Requests Using Auth0 Actions.
Setting up Honeytokens and a Honeytoken Action
Create one or more honeytoken accounts
The first step in setting up a system for detecting honeytoken user accounts is to create those accounts.
For this exercise, you can create honeytoken accounts in the Auth0 Dashboard by selecting User Management → Users from the left column menu…
…and then clicking the Create User button. This will take you to the Create User dialog box:
When creating a honeytoken account, keep these in mind:
- Usernames need to be believable and not conflict with any existing ones or ones you want to reserve for future use. It would also be helpful if the Honeytoken account was verified. You could borrow a trick from this post in the Microsoft Defender XDR Blog and create usernames that suggest they have more privileges or permissions that the average user — they could contain words like
admin
orroot
, for example. You might find this list of top usernames helpful. - Passwords should also be plausible: strong enough to meet the default password policy yet weak enough to be easily guessable. In this example, I used the password
12345Ab!
.
Make a note of the email addresses for the honeytoken accounts because you’ll need them for the next step: creating an Action to catch them during login.
You should create a non-honeytoken user if you don’t already have one.
Create the Action
In the Auth0 Dashboard, select Actions from the left sidebar menu, followed by Flows. This will take you to the Flows page, where you should select the “Login” flow:
Once you’re on the Login page, create a new custom Action by clicking on the + button in the Add Action section and then selecting the Build Custom item in the menu that appears:
The Create Action dialog box will appear. Enter a name for the new Action — I used Catch honeytoken logins
— and leave the Trigger and Runtime items at their default settings, Login / Post Login and Node 18 (Recommended), then click the Create button:
This will create a new Action, and you’ll see this code editor:
Actions can use these two JavaScript functions to affect the Login flow:
onExecutePostLogin
: Specifies what should happen immediately after a successful user login and before the access token is issued. This function has the following parameters:event
: Contains details about the login request, such as information about the user and the login context.api
: Contains methods to change the login’s behavior.
onContinuePostLogin
: Defines what should happen when control returns to the Login Action after a redirect to another web page. It’s used only when the user returns after being redirected, which is why it’s commented out by default.
In this example, we’re only using onExecutePostLogin
. Update the code in the editor to the following:
const honeytokenAccounts = [
'admin2@yourdomain.net',
]
exports.onExecutePostLogin = async (event, api) => {
if (honeytokenAccounts.includes(event.user.email)) {
api.access.deny(
"500: Internal server error - please try logging in later."
)
// You can add code here to send an alert to your security
// team via email, SMS, or API call!
}
}
The code above declares honeytokenAccounts
, an array of one or more email addresses in string form. It checks to see if the email address is used for logging in, contained in the event.user.email
property, is in honeytokenAccounts.
If so, the Action denies the user’s login request and displays an innocent-looking error message. If not, the Action allows the user to continue logging in.
Replace the placeholder email address in honeytokenAccounts
, 'admin2@yourdomain.net'
, with the email addresses from one or more of the honeytoken accounts you created.
After entering the code, click the Deploy button to save your changes and deploy the Action:
Add the Action to the Login flow
Now that you have created the Catch honeytokens logins Action, you can add it to the Login flow. Return to the Login flow page by selecting Actions → Flows from the left sidebar menu, and then clicking the Login button on the Flows page:
In the Add Action section on the right side of the page, click Custom. This will display the list of custom Actions that you created for the Login flow. Drag the Catch honeytokens logins Action to the Login flow diagram and drop it between Start and Complete.
You must apply the change you just made to the Login flow before leaving the page. Do this by clicking the Apply button near the upper right corner of the page.
Test the Action
Now that the Catch honeytoken logins Action is part of the Login flow, it’s time to test it.
The quickest way to test your tenant’s Login flow is to use the login box testing facility built into the Dashboard. You’ll find it on the Getting Started page, which you access by clicking Getting Started in the left sidebar menu. About halfway down the page, you’ll see the Try your Login box section, with a link labeled Try it out. Click that link to try out your new login flow:
You’ll see a login box if you’re already not logged in as one of your tenant’s users.
First, try logging in as a non-honeytoken user. The Action will not deny your login request, and you’ll see the It Works! page:
Then, try logging in using one of the Honeytoken accounts you created. If the email address for the account is in the Actions “deny” list, your login request will be denied, and you’ll see the Bummer! Something failed page:
Note that the "error_description"
property of the returned JSON contains the string 500: Internal server error - please try logging in later.
— the parameter passed to the api.access.deny()
method call in the Action.
You (and your security team) can find the failed Honeytoken login in your tenant’s logs. View the logs by selecting Monitoring → Logs in the Dashboard’s left menu. Select the most recent Failed Login entry, which should be at or near the top of the log and have a Description value that starts with “500: Internal server error”:
You’ll see a detailed view of the login attempt:
Implementation Ideas
The Action above is a basic example of implementing honeytokens with Auth0 actions. Here are a few ideas for next steps:
Add honeytoken accounts to all your identity providers
The honeytoken account in this article’s exercise was a user in the Auth0 database for simplicity’s sake. If your site or application uses other identity providers such as Active Directory or one of the many social login services out there, you should create honeytoken accounts on those services as well.
Create honeytokens early and often
Think of your site or application as an office building and honeytokens as security cameras and door sensors. Just as you wouldn’t use just one camera and a couple of sensors to protect the building, you shouldn’t rely on a handful of honeytokens to protect your application.
Instead, you should regularly add honeytokens. This spreads them throughout your user accounts, making it more likely for unauthorized parties who somehow acquired a list of user credentials to use one of the honeytokens. By doing this, you’re using your attack surface against attackers.
Automate the creation and tracking of honeytoken accounts
As a follow-up to the previous point, you should consider automating the process of creating honeytokens as your user base grows. As the previous point suggests, this will help ensure you’re creating honeytokens early and often.
The automation should also record the email address of each honeytoken. This will allow you to produce a list that your Action can access, which you can either manually code into the Action or have the Action access so that it always uses the most up-to-date version of the list.
Immediate notification via text or email
Using the Catch honeytoken logins Action as written, you can find out if someone is logging in with a honeytoken account by examining the logs. While this is a perfectly serviceable approach, it becomes more time-consuming, tedious, and error-prone as your user base grows or if your users log in frequently.
If you’d rather be notified immediately when someone logs in using a honeytoken account, you should replace these comments in the Action’s code (you’ll find them right after the call to api.access.deny()
)...
// You can add code here to send an alert to your security
// team via email, SMS, or API call!
…with a call to a service like Twilio’s SMS service or their SendGrid service to notify you of a honeytoken login via text or email as soon as it happens.
You can find out more about using SendGrid to send email from an Action in their article, Send Welcome Emails After New Signups with SendGrid and Auth0 Actions.
To avoid accidentally giving away sensitive information through your Action’s code, use the Secrets feature to hide details such as the API keys for the services you call. You can access this feature by clicking on the “key” icon in the Action editor’s icon menu on its left:
Click the Add Secret button to show the window where you can enter the secret and the key you want to associate with it:
Your newly-defined secret will now appear in the Secrets list:
The editor will never display the secret value. You can only delete a secret by clicking the corresponding “trash can” icon or change its value by clicking the “pen” icon.
Taking action after a honeytoken login
The Security Engineering Notebook article, The Beginner’s Guide to Honeytokens (AKA Canary Tokens), has a lot of good ideas, including a list of things you should do when you detect a honeytoken login. Here’s a condensed version of that list with my suggestions:
- Verify. Make sure that someone actually logged in using a Honeytoken account.
- Identify the source. Use the logs to gather information about the user, such as their IP address.
- Contain the incident. They suggest temporarily blocking the IP address, which I show you how to do with Actions in this article.
- Investigate the breach. What else is vulnerable in your site or application (and related systems)?
- Remediate. Once you or your security team have identified the vulnerabilities, take measures to remediate them.
- Strengthen monitoring. It’s tempting to relax after you’ve detected a breach and taken the appropriate measures — and that’s when trouble often happens.
- Review and learn. Identify the lessons learned from this incident, and more importantly, document them!
Summary
Hackers’ preferred attack vector is an application’s identity architecture; they aren’t breaking in, but logging in. Honeytokens are decoy user accounts that act as “tripwires”; their use signals the presence of unauthorized users, effectively using your attack surface against attackers.
You saw a basic implementation of Honeytokens with Auth0 Actions. The Catch honeytoken logins action uses the Login flow to act during the login process, the event.user.email
property to determine which account is being used for logging in, and the API.access.deny()
method to prevent login with a honeytoken account.
This article also provided a quick tour of the log pages in the Auth0 Dashboard. You should take some time to explore this useful feature.
The beauty of honeytokens is the “bang for the buck” they provide. Unlike most intrusion detection mechanisms, honeytokens are inexpensive — they don’t even come from a vendor! With minimal work and an Auth0 Action, you can get a basic Honeytoken system up and running; with a little creativity and some development effort, you can enhance that system with automation and alerts.
Happy coding!