[NestJS] Rate limit

김승현·2023년 12월 16일
0

무차별 대입 공격(brute-force attack)

  • 인증 정보(사용자 이름과 비밀번호)를 알아내기 위해 공격자가 반복적, 체계적으로 매번 다른 사용자 이름과 비밀번호를 입력하는 방식의 공격
  • 단순하지만 리소스를 많이 소비하는 시행착오 기반의 접근 방식으로, 보통 자동화된 툴이나 스크립트 또는 봇을 사용해 액세스 권한을 획득할 때까지 가능한 모든 조합을 대입한다.

Rate Limiting

  • 무차별 대입 공격으로부터 애플리케이션을 보호하는 일반적인 기법

package 설치

npm i @nestjs/throttler
import { ThrottlerModule } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot([
      // 60초동안 최대 요청 개수 10으로 제한
      {
        ttl: 6000,
        limit: 10,
      },
    ]),

    ...

  ],
})
export class AppModule {}
}

전역 설정

import { ThrottlerModule } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot([
      // 60초동안 최대 요청 개수 10으로 제한
      {
        ttl: 6000,
        limit: 10,
      },
    ]),

    ...

  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,
    },
  ],
})
export class AppModule {}
}

비활성화

@SkipThrottle() 를 이용하여 전체 class 또는 단일 route에 Throttel 설정을 비활성화 할 수 있음

import { Controller } from '@nestjs/common';
import { SkipThrottle } from '@nestjs/throttler';

@SkipThrottle()
@Controller('api/user')
export class UserController {}

비활성화한 class에서 특정 route를 비활성 무효 처리 할 수 도 있음

import { Controller } from '@nestjs/common';
import { SkipThrottle } from '@nestjs/throttler';

@SkipThrottle()
@Controller('api/user')
export class UserController {

	@SkipThrottle({default: false})
	dontSkip(){
		return 'Rate Limiting 설정 됨'
	}

	doSkip(){
		return 'Rate Limiting 비활성화 됨'
	}
}

Override

  • @Trottle() 을 이용하여 글로벌로 설정된 limit, ttl 을 재정의하여 특정 class 또는 route에 설정할 수 있음
@Throttle({ default: { limit: 3, ttl: 60000 } })
@Get()
findAll() {
  return "Custom Rate Limiting 설정";
}

Proxies

  • 애플리케이션이 프록시 서버 뒤에서 실행되는 경우 특정 HTTP 어댑터 옵션(express 및 fastify)에서 trust proxy 옵션을 확인하고 이를 활성화하세요.
  • 이렇게 하면 X-Forwarded-For 헤더에서 원래 IP 주소를 가져올 수 있으며, getTracker() 메서드를 재정의하여 req.ip가 아닌 헤더에서 값을 가져올 수 있습니다.

src/common/guard/throttler-behind-proxy.guard.ts

import { Injectable } from '@nestjs/common';
import { ThrottlerGuard } from '@nestjs/throttler';

@Injectable()
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
  // proxy 뒤에 서버가 있더라도 실제 클라이언트의 IP를 빼내는 방법
  protected getTracker(req: Record<string, any>): Promise<string> {
    // 배열이면 첫번째 원소가 클라이언트 IP
    return req.ips.length ? req.ips[0] : req.ip;
  }
}
import { Controller, UseGuards } from '@nestjs/common';
import { ThrottlerBehindProxyGuard } from 'src/common/guard/throttler-behind-proxy.guard';

@UseGuards(ThrottlerBehindProxyGuard)
@Controller('api/user')
export class UserController {}
profile
개발자로 매일 한 걸음

0개의 댓글