Nodejs 如何设计一个限频接口来防止攻击
作者:互联网
做过后端研发的基本对接口限频完全不陌生,特别是针对一些核心接口受到攻击的时候,比如 Jmeter 来通过一些用户填写接入恶意灌入脏数据。
那在 nodejs 这边如何设计限频接口呢?
基于 express 的 express-rate-limit
源码地址:
https://github.com/nfriedly/express-rate-limit
Basic rate-limiting middleware for express
Use 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