Authentication And Security

Rate Limit Password Inputs

Summary

In this guide, we'll explore how to set up a rate-limiting feature for password attempts in Xano. This feature suspends users from logging in for a specified duration if they exceed a certain number of failed login attempts within a given time frame. It helps prevent brute-force attacks and reduces server load from excessive invalid requests.

Step 1: Create a Login Attempts Table

Start by creating a table called `login_attempts` to log all login attempts. This table should have the following columns:

  • `user_id` (reference to the user table)
  • `success` (boolean indicating whether the login attempt was successful or not)
  • `created_at` (timestamp of the login attempt)

Additionally, add two columns to your `users` table:

  • `temp_suspension` (boolean indicating if the user is currently suspended)
  • `temp_timeout_end` (timestamp of when the suspension period ends)

Step 2: Update the Login Endpoint

In your `auth/login` endpoint, modify the logic to log each login attempt in the `login_attempts` table. Add a record with the `user_id` and `success` (set to `true` for successful logins and `false` for failed attempts).

 // Successful login attempt
 add_record(login_attempts, {
  user_id: user.id,
  success: true
 });
 
 // Failed login attempt
 add_record(login_attempts, {
  user_id: user.id,
  success: false
 });
 

Step 3: Create a Trigger

Next, create a trigger that checks for failed login attempts within a specific time frame (e.g., 5 attempts in the last 60 seconds). If the condition is met and the user is not already suspended, update the `users` table to set `temp_suspension` to `true` and `temp_timeout_end` to the current timestamp plus a specified duration (e.g., 60 seconds).

  1. Go to your `login_attempts` table and click the three dots in the top-right corner, then select "Triggers" and "Add Database Trigger."
  2. Name the trigger "rate_limit_password_attempts" and set the action to "Insert."
  3. Add a query to count the number of failed login attempts for the user within the last 60 seconds:

sql
 query_all_records(login_attempts, {
  filters: [
  {
  field: 'user_id',
  operator: 'equals',
  value: new.user_id
  },
  {
  field: 'success',
  operator: 'equals',
  value: false
  },
  {
  field: 'created_at',
  operator: 'greater_than',
  value: timestamp_subtract(now(), seconds(60))
  },
  {
  field: 'created_at',
  operator: 'less_than',
  value: now()
  }
  ]
 })
 

  1. Check if the count is greater than or equal to 5 (or your chosen threshold) and if the user is not already suspended:

 if (login_attempt_count >= 5 && get_record(users, new.user_id).temp_suspension == false) {
  edit_record(users, new.user_id, {
  temp_suspension: true,
  temp_timeout_end: timestamp_add(now(), seconds(60))
  });
 }
 

This suspends the user for 60 seconds after the threshold is reached.

Step 4: Create Pre-Middleware

To prevent suspended users from making further login attempts, create pre-middleware that checks the user's suspension status before executing the login endpoint logic.

  1. Go to the "Library" section and click "Add Middleware."
  2. Name the middleware "rate_limit_password_attempts" and set the response type to "Replace Exception Critical and Safe."
  3. In the middleware function, retrieve the user record based on the provided email:

 user = get_record(users, {email: vars.email});
 

  1. Add a precondition to check if the user is not suspended:

 precondition(user.temp_suspension == false, "Too many requests. Please try again later.", 429);
 

  1. Attach the pre-middleware to your `auth/login` endpoint by going to the endpoint settings, clicking "Customize" under "Middleware," and adding the pre-middleware you just created.

Step 5: Create a Background Task

Finally, create a background task to automatically unsuspend users after the timeout period has expired.

  1. Go to the "Background Tasks" section and click "Add Background Task."
  2. Name the task "rate_limit_password_attempts."
  3. Query all users where `temp_suspension` is `true` and `temp_timeout_end` is less than the current time:

sql
 query_all_records(users, {
  filters: [
  {
  field: 'temp_suspension',
  operator: 'equals',
  value: true
  },
  {
  field: 'temp_timeout_end',
  operator: 'less_than',
  value: now()
  }
  ]
 });
 

  1. Loop through the retrieved users and update their `temp_suspension` to `false` and `temp_timeout_end` to `null`:

 foreach(users, user => {
  edit_record(users, user.id, {
  temp_suspension: false,
  temp_timeout_end: null
  });
 });
 

  1. Set the task to run every 10 seconds (or your desired interval) and enable it.

With these steps completed, your Xano application now has a rate-limiting feature for password attempts, improving security and preventing potential brute-force attacks.

This transcript was AI generated to allow users to quickly answer technical questions about Xano.

Was this helpful?

I found it helpful

I need more support
Sign up for XanoSign up for Xano

Build without limits on a secure, scalable backend.

Unblock your team's progress and create a backend that will scale for free.

Start building for free