编程语言
首页 > 编程语言> > Nodejs 如何设计一个限频接口来防止攻击

Nodejs 如何设计一个限频接口来防止攻击

作者:互联网

做过后端研发的基本对接口限频完全不陌生,特别是针对一些核心接口受到攻击的时候,比如 Jmeter 来通过一些用户填写接入恶意灌入脏数据。

那在 nodejs 这边如何设计限频接口呢?

基于 express 的 express-rate-limit

 

源码地址:

https://github.com/nfriedly/express-rate-limit

 Basic rate-limiting middleware for expressUse to limit repeated requests to public APIs and/or endpoints such as password reset.

 

 

 

  

其实这个包不老,都是 typescript 重写的。

这里面有几个问题,因为要限制:

比如同一个 IP 在一定时间内访问的次数不能超过一定的值。

那就需要通过一个 Store 来记录,支持:

 1、memory-store - 比较简单的 in-memory

然后就是依托 redis 或者 mongo,还有估计很多前端不知道的 Memcached:

 

2、rate-limit-redis

3、rate-limit-memcached

4、rate-limit-mongo

这个包的作者还专门写了一篇文章来描述如何自定义一个 Store,提供 typescript 和 js 的版本 

https://github.com/nfriedly/express-rate-limit/wiki/Creating-Your-Own-Store

自定义存储将在内部跟踪每个标识符(如 IP地址)收到的访问次数,并随着时间的推移自动减少次数。

 

一个 Store 必须包含如下方法:

 

定义一个 Store 的 ts 类型:

export interface Store {  init?: (options: Options) => void  increment: (key: string) => Promise<IncrementResponse> | IncrementResponse  decrement: (key: string) => Promise<void> | void  resetKey: (key: string) => Promise<void> | void  resetAll?: () => Promise<void> | void  }

 

这里的 IncrementResponse 类型:

export type IncrementResponse = {  totalHits: number  resetTime: Date | undefined}

 

 

然后在 express-rate-limit 的 memory-store 设计中:

 

源码地址如下:

https://github.com/nfriedly/express-rate-limit/blob/master/source/memory-store.ts

 

源码设计:

export default class MemoryStore implements Store {  windowMs!: number  hits!: {    [key: string]: number | undefined  }  resetTime!: Date}

 

这里的 windowMs:

The duration of time before which all hit counts are reset (in milliseconds)

 

*、increment 方法

It adds 1 to the internal count for a key and returns an object consisting of the new internal count (totalHits) and the time that the count will reach 0 (resetTime).

 

源码设计:接受一个参数 key,进行加一

async increment(key: string): Promise<IncrementResponse> {    const totalHits = (this.hits[key] ?? 0) + 1    this.hits[key] = totalHits
return { totalHits, resetTime: this.resetTime, }}

 

 

*、decrement 方法

is used only to 'uncount' requests when one or both of the skipSuccessfulRequests or skipFailedRequests options are enabled.

 

源码设计:接受一个参数 key,进行减一

async decrement(key: string): Promise<void> {    const current = this.hits[key]    if (current) {      this.hits[key] = current - 1    }}

 

*、init 方法

allows the store to set itself up using the options passed to the middleware.
允许传一些配置对象给 middleware

 

源码设计:

init(options: Options): void {    // Get the duration of a window from the options    this.windowMs = options.windowMs    // Then calculate the reset time using that    this.resetTime = calculateNextResetTime(this.windowMs)
// Initialise the hit counter map this.hits = {}
// Reset hit counts for ALL clients every `windowMs` - this will also // re-calculate the `resetTime` const interval = setInterval(async () => { await this.resetAll() }, this.windowMs) if (interval.unref) { interval.unref() }}

 

看下代码设计:

 

*、resetKey 方法

Resets the rate limiting for a given key.

 

源码设计:接受一个参数 key

async resetKey(key: string): Promise<void> {  delete this.hits[key]}

 

 

*、resetAll 方法

 

reset everyone's hit counter

 

源码设计:

async resetAll(): Promise<void> {  this.hits = {}  this.resetTime = calculateNextResetTime(this.windowMs)}

 

这里核心依赖一个方法:calculateNextResetTime

 

const calculateNextResetTime = (windowMs: number): Date => {  const resetTime = new Date()  resetTime.setMilliseconds(resetTime.getMilliseconds() + windowMs)  return resetTime}

 

本篇核心从源码角度接受了如何设计限频依赖的 Store 的设计,下一篇我们继续剖析

 

标签:resetTime,Nodejs,windowMs,接口,rate,限频,limit,key,源码
来源: https://www.cnblogs.com/cangqinglang/p/16501042.html