XSRF 방어하기

Adam Kim·2025년 10월 7일
0

angular

목록 보기
43/88

원인

(서버에서 xsrf 또는 csrf를 허용한 경우)

Angular 6 이하에서는 http 전송 시 csrf 쿠키값을 헤더에 넣기 위해 HttpInterceptor를 사용하였는데, 여기에 csrf의 값이 담긴 쿠키값을 가져와 헤더를 생성하여야 하는 불편함이 있었습니다.

@Injectable({ providedIn: 'root' })
export class ApiInterceptor implements HttpInterceptor {
  constructor(private cookieService: CookieService) { }

  intercept(req: HttpRequest<any>, handler: HttpHandler): Observable<HttpEvent<any>> {
    const headers = {
      'My-Xsrf-Header': this.cookieService.get('My-Xsrf-Cookie'),
    };

    return handler.handle(
      req.clone({ setHeaders: headers, withCredentials: true })
    );
  }
}

Angular 7 버전부터 HttpClientXsrfModule을 지원하여, 이를 보다 손쉽게 정리할 수 있습니다.

모듈에서 직접 설정할 수 있기 때문에 하드코딩이 상위로 올라와 관리 포인트를 줄일 수 있으며, 직관적인 확인이 가능합니다.

import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http';

imports: [
  HttpClientModule,
  HttpClientXsrfModule.withOptions({
    cookieName: 'My-Xsrf-Cookie',
    headerName: 'My-Xsrf-Header',
  }),
],

에러

이렇게 설정하였는데도 막상 실행해보면 에러가 발생한다면,

이는 인증을 이용한 요청방식을 위해 서버에서는 아래와 같이 Credentials 값을 true로 요구하고 있기 때문입니다.

response.setHeader("Access-Control-Allow-Credentials", "true")

해결

Angular에서 http request 마다 또는 Interceptor에서 withCredential 값을 true로 설정해주어야 합니다.

@Injectable()
export class HttpConfigInterceptor implements HttpInterceptor {
  constructor() {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    request = request.clone({
      withCredentials: true
    });

    return next.handle(request);
  }
}

추가: 최신 Angular (v17+)에서의 해결책

Angular 17부터는 NgModule 없이 독립형 API를 사용하는 것이 기본입니다. 전역 HTTP 설정은 app.config.ts 파일에서 provideHttpClient라는 프로바이더 함수와 함께 제공되는 기능(feature)들을 통해 이루어집니다.

해결책 (독립형 API 기반 - Angular 17+ 등)

app.config.ts 파일에서 XSRF 설정과 인터셉터를 한 번에 구성합니다.

  • XSRF 설정: withXsrfConfiguration 기능을 사용합니다.
  • withCredentials 설정: withInterceptors 기능을 사용하여 함수형 인터셉터를 등록합니다.
// src/app/app.config.ts

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
// 1. 필요한 함수들을 @angular/common/http에서 임포트
import { provideHttpClient, withXsrfConfiguration, withInterceptors, HttpInterceptorFn } from '@angular/common/http';

import { routes } from './app.routes';

// 2. 함수형 인터셉터 정의
const credentialsInterceptor: HttpInterceptorFn = (req, next) => {
  const clonedReq = req.clone({
    withCredentials: true,
  });
  return next(clonedReq);
};

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),

    // 3. HttpClient 관련 설정을 providers 배열에 추가
    provideHttpClient(
      // XSRF 설정
      withXsrfConfiguration({
        cookieName: 'My-Xsrf-Cookie',
        headerName: 'My-Xsrf-Header',
      }),
      // 인터셉터 설정
      withInterceptors([credentialsInterceptor])
    )
  ]
};

참고 사이트

profile
Angular2+ Developer

0개의 댓글