Backend Development 13 min read

Introduction to Lua and Using Redis + Lua Scripts to Limit IP Login Attempts

This article introduces the lightweight Lua scripting language, outlines its key features and typical use cases, and provides a complete example of using Redis together with Lua scripts (and a Java client) to enforce IP‑based login‑attempt limits for improved security.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Introduction to Lua and Using Redis + Lua Scripts to Limit IP Login Attempts

Lua is a lightweight, embeddable scripting language created in 1993 by a research group at the Pontifical Catholic University of Rio de Janeiro. Written in standard C, it runs on virtually any operating system, can call C/C++ functions and be called from them, and its interpreter is only about 200 KB, making it ideal for embedding.

Lua Overview

Lua provides a minimal core and basic libraries, focusing on simplicity and speed rather than a large standard library. It supports procedural and functional programming, automatic memory management, tables that can act as arrays, hash tables, sets, or objects, closures for object‑oriented patterns, and lightweight coroutines.

Key Features

Lightweight: Small size and fast startup, perfect for embedding.

Extensible: Easy to extend via C/C++ host interfaces.

Supports procedural, functional, and object‑oriented styles.

Automatic memory management.

Coroutines for cooperative multitasking.

Typical Application Scenarios

Game development (e.g., cheat scripts).

Application scripting (e.g., Redis Lua scripts).

Database plugins (MySQL Proxy, MySQL Workbench).

Security systems such as intrusion‑detection.

Basic Lua Syntax

Variable declaration:

local a = 10

Conditional statements:

if x > 0 then
    print("x is positive")
elseif x == 0 then
    print("x is zero")
else
    print("x is negative")
end

Loops:

for i = 1, 10 do
    print(i)
end

Function definition:

function add(a, b)
    return a + b
end

Using Redis + Lua to Limit IP Login Attempts

Redis supports executing Lua scripts atomically via the EVAL command. The following Lua script limits an IP address to five failed password attempts within ten minutes; exceeding this threshold locks the IP for one hour.

-- Limit IP multiple failed password attempts
local ip = KEYS[1]
local current_time = tonumber(ARGV[1])
local expire_time = 600   -- 10 minutes
local max_attempts = 5
local lock_duration = 3600 -- 1 hour

local attempts = tonumber(redis.call('get', ip) or '0')
local lock_time = tonumber(redis.call('get', ip .. ':lock') or '0')

if current_time < lock_time then
    return -1  -- IP is locked
end

attempts = attempts + 1
if attempts >= max_attempts then
    redis.call('set', ip .. ':lock', current_time + lock_duration)
    return -1
else
    redis.call('set', ip, attempts)
    redis.call('expire', ip, expire_time)
    return attempts
end

The script receives the target IP as KEYS[1] and the current Unix timestamp as ARGV[1] . It checks whether the IP is already locked, updates the failure count, and either locks the IP or refreshes the counter with an expiration.

Script Logic Flow

Fetch current attempt count and lock timestamp from Redis.

If the current time is less than the lock timestamp, return -1 (locked).

Otherwise, increment the attempt count.

If the count reaches max_attempts , set a lock key with lock_duration and return -1 .

If not, store the updated count and set its expiration to expire_time .

Execution via Redis CLI:

redis-cli --eval limit_login_attempts.lua 192.168.1.1 , 1620000000

Java Integration Example (Jedis)

The following Java class demonstrates how to load the Lua script as a string, evaluate it with Jedis, and interpret the result.

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class LoginAttemptLimiter {
    private static final String LUA_SCRIPT =
        "local ip = KEYS[1] " +
        "local current_time = tonumber(ARGV[1]) " +
        "local expire_time = 600 " + // 10 minutes
        "local max_attempts = 5 " +
        "local lock_duration = 3600 " + // 1 hour
        "local attempts = tonumber(redis.call('get', ip) or '0') " +
        "local lock_time = tonumber(redis.call('get', ip .. ':lock') or '0') " +
        "if current_time < lock_time then return -1 end " +
        "attempts = attempts + 1 " +
        "if attempts >= max_attempts then " +
        "    redis.call('set', ip .. ':lock', current_time + lock_duration) " +
        "    return -1 " +
        "else " +
        "    redis.call('set', ip, attempts) " +
        "    redis.call('expire', ip, expire_time) " +
        "    return attempts " +
        "end";

    private final JedisPool jedisPool;

    public LoginAttemptLimiter(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    public int checkLoginAttempts(String ip) {
        try (Jedis jedis = jedisPool.getResource()) {
            long currentTime = System.currentTimeMillis() / 1000;
            Object result = jedis.eval(LUA_SCRIPT, 1, ip, String.valueOf(currentTime));
            return ((Long) result).intValue();
        }
    }

    public static void main(String[] args) {
        try (JedisPool jedisPool = new JedisPool("localhost", 6379)) {
            LoginAttemptLimiter limiter = new LoginAttemptLimiter(jedisPool);
            String ip = "192.168.1.1";
            int attemptResult = limiter.checkLoginAttempts(ip);
            if (attemptResult == -1) {
                System.out.println("IP is locked due to too many failed attempts.");
            } else {
                System.out.println("Failed attempt count for IP: " + attemptResult);
            }
        }
    }
}

The eval call passes the script, the number of keys (1), the IP address, and the current timestamp. The method returns the current failure count or -1 if the IP is locked.

Parameter Details

KEYS[1] (ip): The target IP address.

ARGV[1] (current_time): Current Unix time in seconds.

expire_time: Validity period of the failure counter (600 s).

max_attempts: Maximum allowed failures before locking (5).

lock_duration: Lock period after exceeding attempts (3600 s).

attempts: Current failure count fetched from Redis.

lock_time: Timestamp when the lock expires.

Integrating this logic into a login service allows automatic, atomic enforcement of rate‑limiting policies, helping to mitigate brute‑force attacks.

JavaRedisJedisscriptinglualogin securityIP Rate Limiting
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.