利用时间戳校验防止爬虫滥用接口

爬虫是现代互联网中常见的恶意行为,尤其是在接口暴露给外部时,爬虫往往通过自动化脚本频繁访问接口、获取数据,甚至滥用接口导致服务器压力增大,影响正常用户的使用体验。为了有效防止爬虫的恶意抓取行为,除了常规的防护手段外,利用带校验位的时间戳也是一种非常巧妙且高效的防护方式。

本文将介绍如何通过时间戳校验来增加接口的防爬虫能力,具体是通过在接口中加入一个带校验位的时间戳,令爬虫抓取时多次失败,成功几率极低,从而达到防止爬虫滥用接口的目的。

一、带校验位的时间戳防止爬虫
为了防止爬虫轻松抓取接口数据,我们可以利用时间戳并附加校验位。爬虫在爬取接口时,通常会认为接口中的时间戳是一个简单的时间标识,而忽略了背后的校验机制。这样,通过在时间戳中加入一个校验位,爬虫大多数情况下无法正确猜测出校验位,从而导致请求失败。

1.1 时间戳生成与校验位机制
时间戳本身作为接口的一个参数,可以用来标记请求的时间,确保请求的时效性。而通过附加校验位,爬虫无法轻易预测正确的时间戳。具体的步骤如下:

生成时间戳:获取当前系统的 13 位毫秒级时间戳。
计算校验位:根据前 12 位时间戳,使用某种算法计算一个校验位(通常是取前 12 位各位数字和对 10 取模)。
拼接时间戳和校验位:将计算出的校验位附加在时间戳的第 13 位,形成一个新的 13 位时间戳。
接口校验:当客户端请求时,接口会验证时间戳的校验位是否正确,如果不匹配,则认为该请求是无效的。
通过这种方式,爬虫在尝试访问接口时,由于无法知道校验位的生成规则,因此即便发送了带有时间戳的请求,绝大多数情况下也会因为校验失败而导致请求被拒绝。

1.2 示例:

Java 实现时间戳校验
以下是一个简单的 Java 实现代码,通过该代码可以生成带校验位的时间戳,并校验其有效性。


public class TimestampWithCheck {

    // 生成带校验位的 13 位时间戳
    public static long generateTimestampWithCheck() {
        // 获取当前的 13 位时间戳
        long timestamp = System.currentTimeMillis();
        
        // 提取前 12 位时间戳
        long timestampWithoutCheck = timestamp / 10;
        
        // 计算校验位
        int checkBit = calculateCheckBit(timestampWithoutCheck);
        
        // 拼接时间戳和校验位
        return timestampWithoutCheck * 10 + checkBit;
    }

    // 校验位计算:取前 12 位各位数之和的个位数
    private static int calculateCheckBit(long timestampWithoutCheck) {
        int sum = 0;
        while (timestampWithoutCheck > 0) {
            sum += timestampWithoutCheck % 10;
            timestampWithoutCheck /= 10;
        }
        return sum % 10; // 取个位数作为校验位
    }

    // 校验时间戳的有效性
    public static boolean validateTimestamp(long timestampWithCheck) {
        // 提取时间戳的前 12 位和校验位
        long timestampWithoutCheck = timestampWithCheck / 10;
        int providedCheckBit = (int) (timestampWithCheck % 10);
        
        // 计算正确的校验位
        int calculatedCheckBit = calculateCheckBit(timestampWithoutCheck);
        
        // 校验时间戳的校验位是否正确
        return providedCheckBit == calculatedCheckBit;
    }

    public static void main(String[] args) {
        // 生成带校验位的时间戳
        long timestampWithCheck = generateTimestampWithCheck();
        System.out.println("Generated Timestamp with Check: " + timestampWithCheck);
        
        // 验证时间戳是否有效
        boolean isValid = validateTimestamp(timestampWithCheck);
        System.out.println("Is the Timestamp Valid? " + isValid);
    }
}

 

C#实现时间戳校验
以下是一个简单的 C#实现代码,通过该代码可以生成带校验位的时间戳,并校验其有效性。


/// <summary>
/// 时间戳校验
/// </summary>
public static class TimestampWithCheck
{
    /// <summary>
    /// 生成带校验位的 18 位时间戳
    /// </summary>
    /// <returns></returns>
    public static long GenerateTimestampWithCheck()
    {
        // 获取当前的 18 位时间戳
        long timestamp = DateTime.Now.Ticks;

        // 提取前 17 位时间戳
        long timestampWithoutCheck = timestamp / 10;

        // 计算校验位
        long checkBit = CalculateCheckBit(timestampWithoutCheck);

        // 拼接时间戳和校验位
        return timestampWithoutCheck * 10 + checkBit;
    }

    /// <summary>
    /// 校验位计算:取前 17 位各位数之和的个位数
    /// </summary>
    /// <param name="timestampWithoutCheck"></param>
    /// <returns></returns>
    private static long CalculateCheckBit(long timestampWithoutCheck)
    {
        long sum = 0;
        while (timestampWithoutCheck > 0)
        {
            sum += timestampWithoutCheck % 10;
            timestampWithoutCheck /= 10;
        }
        return sum % 10; // 取个位数作为校验位
    }

	/// <summary>
	/// 校验时间戳的有效性
	/// </summary>
	/// <param name="timestampWithCheck">待验证时间戳</param>
	/// <returns>校验是否通过</returns>
	public static bool ValidateTimestamp(long timestampWithCheck)
	{
		// 提取时间戳的前 17 位和校验位
		long timestampWithoutCheck = timestampWithCheck / 10;
		int providedCheckBit = (int)(timestampWithCheck % 10);

		// 计算正确的校验位
		long calculatedCheckBit = CalculateCheckBit(timestampWithoutCheck);

		// 校验时间戳的校验位是否正确
		return providedCheckBit == calculatedCheckBit;
	}
}

// 测试
static void Main()
{
	var timestampWithCheck = YYFUtils.TimestampWithCheck.GenerateTimestampWithCheck();
	Console.WriteLine("生成带校验的时间戳: " + timestampWithCheck);
	var isValid = YYFUtils.TimestampWithCheck.ValidateTimestamp(timestampWithCheck);
	Console.WriteLine("时间戳校验是否通过: " + (isValid ? "通过" : "不通过"));
	Console.ReadKey();
}

 

二、为什么带校验位的时间戳能有效防止爬虫?
爬虫通常会直接抓取接口的数据,并通过时间戳作为一个普通的参数进行请求。然而,在我们的方案中,时间戳并不仅仅是一个简单的时间标识。爬虫无法轻易知道校验位的生成规则,因此:

难以猜测校验位:时间戳的校验位是通过前 12 位的时间戳计算出来的,如果爬虫不清楚这个规则,它就无法正确预测校验位。
大多数请求失败:即使爬虫抓取时随机生成时间戳,也会因为校验位不匹配导致请求失败。这样,爬虫很难找到正确的时间戳,造成它在频繁访问接口时,大多数请求都会失败。
增加猜测的难度:爬虫若要猜测校验位,需要大量的请求尝试,并且无法确定正确的校验位,这会显著增加爬虫的抓取难度。
三、好处与局限性
3.1 好处
简洁有效:通过在时间戳中增加校验位,爬虫难以绕过这种防护方式,成功率低,能有效减少接口被滥用的可能性。
无需复杂配置:该方法实现简单,不需要太复杂的设置,适用于大多数接口。
增加爬虫抓取成本:每次请求需要进行时间戳计算,爬虫无法直接复制时间戳,因此增加了其抓取的成本。
3.2 局限性
可能的猜测成功:在某些情况下,爬虫可能通过反复尝试猜测到正确的校验位,但这种概率非常低,且无法持续稳定。
四、总结
通过在接口中加入带校验位的时间戳,可以有效防止爬虫对接口的滥用。爬虫通常会将时间戳视为普通的时间标识,而无法猜测到隐藏在时间戳背后的校验位规则。这样,它们大多数情况下会因校验失败而无法成功抓取数据。

虽然这种方法并非绝对的防护手段,但它增加了爬虫抓取的难度,提高了接口的安全性,适合用作防爬虫策略之一。结合其他技术(如验证码、IP 限制等),可以进一步加强接口的安全防护,保障系统的稳定运行。

© 版权声明

☆ END ☆
喜欢就点个赞吧
点赞0 分享
图片正在生成中,请稍后...