이번 시간에는 Nginx를 활용한 HTTP 인증에 대해 다뤄볼까 한다.

HTTP 기본 인증

기본 인증을 통해 서비스와 리소스를 안전하게 보호할 수 있다. 인증 파일은 다음과 같은 포맷으로 작성한다.

1
2
<username>:<password>
<username>:<password>:<comment>
  • <username>: 인증할 사용자 이름 (시스템 유저와 무관하다)
  • <password>: 패스워드
  • <comment>: 부가 정보 (선택사항)

작성 예시는 다음과 같다.

1
admin:$1$LjYp9Btn$fn.N51gsNCxXaBoQfJTmW0

위 예시는 사용자명과 패스워드를 admin/admin으로 설정해둔 상태이다.

이때 패스워드는 암호화된 형태로 입력이 되어야 하는데 다음과 같이 openssl을 통해 암호화된 패스워드를 생성할 수 있다.

1
openssl passwd <password>
1
2
3
# Ex
$ openssl passwd admin   
$1$LjYp9Btn$fn.N51gsNCxXaBoQfJTmW0

아파치에서 제공하는 htpasswd를 통해서도 생성할 수 있다.

구글링을 해 보면 htpasswd를 사용하는 포스팅이 더 많은것 같다.

이후 해당 인증 파일을 다음과 같이 location 블록에서 사용할 수 있다.

1
2
3
4
location / {
    auth_basic              "Private Contents";
    auth_basic_user_file    conf.d/htpasswd;
}
  • auth_basic: 인증 팝업창에 보여지는 내용이다.
  • auth_basic_user_file: 위에서 작성한 인증 파일 경로를 입력한다.
    • 절대로 정적 리소스 경로에 포함하지 말자

이후 브라우저를 통해 접속하면 다음과 같은 인증창이 뜨는것을 볼 수 있다. http auth basic

CLI 환경에서 curl을 통해 테스트 할 경우 다음과 같이 사용 가능하다

1
curl --user username:password https://localhost

패스워드는 기업에서 사용하는 LDAP나 Salted SHA-1과 같은 형식으로도 만들 수 있다. Nginx는 여러 포맷과 해싱 알고리즘을 제공하지만, 대부분 보안이 취약하며 브루트포스로 탈취당할 여지가 있다.

하위 요청을 통한 인증

http_auth_request_module을 사용해 별도의 인증 서비스로 요청을 보내고 요청자의 ID를 확인할 수 있다.

링크: Module ngx_http_auth_request_module

1
2
3
4
5
6
7
8
9
10
11
12
location /private/ {
    auth_request        /auth;
    auth_request_set    $auth_status $upstream_status;
}

location = /auth {
    internal;
    proxy_pass      http://auth-server;
    proxy_pass_request_body     off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
}
  • auth_request: 내부 인증 시스템의 URI를 지정한다
  • auth_request_set: 인증을 위한 하위 요청의 응답으로 받은 값을 변수에 저장한다

http_auth_request_module은 Nginx 서버가 처리하는 모든 요청에 대한 인증을 제공한다. 사용자 요청에 대한 인증을 위해 하위 요청을 보내소, 인증 시스템으로부터 인증을 받는다.

/auth 경로에 구성된 location 블록은 원본 요청(헤더, 바디 포함)을 인증 서버로 전송한다. 하위 요청에 대한 응답 Status Code는 사용자 접근 허가 여부를 제공한다.

  • 200 -> 인증 성공
  • 401 or 403 -> 인증 실패 인증 시스템에 request body가 필요하지 않다면, proxy_pass_request_body 옵션을 통해 비활성화 시킬 수 있다 (body 포함 여부를 비활성화 시키면 Content-Length 헤더 또한 빈 값이 필요)

만일 인증 서비스가 요청 URI 혹은 부가적인 정보를 알아야 한다면, 요청 헤더를 커스터마이징해 해당 인증 서비스에 맞게 유도리 있게 대처하자.

이후 인증 서비스를 거치고 난 후 auth_request_set구문을 통해 응답 결과를 새로운 변수를 통해 저장한다.

마치며

서비스가 외부로 공개되는 순간, 어떤 인증 절차를 추가해도 100% 완벽한 보안은 힘들다. 결국에는 상황에 맞게 유도리 있께 대처하는 것이 가장 중요한 덕목중 하나라고 본다.

추후 보안 관련된 내용도 포스팅할 생각이다.