Nginx 보안 설정

SSL 설정

ssl 설정 파일에서 통합인증서(서버+체인+루트)와 key파일을 준비합니다. crt 또는 pem으로 구성된 서버, 체인, 루트 파일이 분리 되어 있다면 cmd 콘솔에서 아래와 같은 형식으로 통합인증서로 만듭니다. 순서는 서버-체인-루트 순으로 합치고, 만약 통합인증서가 존재한다면 아래 작업은 skip합니다.

[cat 또는 type] [인증서경로]/서버.crt.pem [인증서경로]/체인.pem [인증서경로]/루트.pem > sample_crt.pem

통합 인증서 파일과 key파일을 conf/ssl 폴더를 만들고 넣어줍니다.

http {
    ...
    upstream sample{
        server 127.0.0.1:9292;
    }

    server {
        listen       80;
        server_name website.com;
        return 301 https://$host$request_uri;
    }

    server {
        listen       443 ssl http2;
        listen       [::]:443 ssl http2;
        server_name website.com;

        ssl_certificate      ssl/sample_crt.pem;
        ssl_certificate_key  ssl/sample_key.pem;
        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  30m;
        ssl_protocols  TLSv1.2 TLSv1.3;
        ssl_ciphers  ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
        ssl_prefer_server_ciphers  on;

        location / {	
            proxy_set_header    Host $http_host;
            proxy_set_header    X-Real-IP $remote_addr;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header    X-Forwarded-Proto $scheme;
            proxy_set_header    X-NginX-Proxy true;

            proxy_pass http://sample;
            proxy_redirect off;
            charset utf-8;
        }
    }
}

불필요 HTTP Method 제한

사용하는 method 이외는 요청 거부합니다.

http {
    ...
    server {
       ...
       location / {	
	    limit_except GET POST OPTIONS {
			deny all;
	    }
    }
}

Request Connection 제한

ngx_http_limit_conn_module 모듈에서 단일 IP 당 연결 수를 제한하는데 사용됩니다. 모든 연결이 계산되는 것은 아니며 전체 요청 헤더를 이미 읽은 경우에만 연결이 계산됩니다.

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_conn_status 444;
    ...
    server {
        ...
        location / {
            limit_conn addr 10;
        }

limit_conn_zone은 여러개를 정의할 수 있으며 하나의 zone은 zone=[zone명]:[공유메모리용량]으로 정의할 수 있습니다. 예제에서 공유메모리 10m은 10Mbyte를 의미합니다. 1Mbtye 당 저장할 수 있는 키 개수는 32bit 시스템의 경우 약 32,000개를 64bit 시스템의 경우 약 16,000개를 저장할 수 있습니다.

정의된 zone은 http, server, location에 설정할 수 있으며 limit_conn [zone명] [connection수]; 형식으로 사용합니다.

limit_conn_log_level info| notice | warn | error (default: error)

제한 오류 레벨을 설정합니다.

limit_conn_status code (default: 503)

제한 오류시 reponse 코드를 지정합니다. 기본값은 503입니다.

Request 처리 속도 제한

ngx_http_limit_req_module 모듈에서 단일 IP에서 들어오는 요청 처리 속도를 제한하는데 사용됩니다.

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
    limit_req_status 444;
    ...
    server {
        ...
        location /search/ {
            limit_req zone=one burst=20 delay=10;
        }

zone=one:10m one이라는 zone이름으로 공유 메모리 10Mbyte로 설정.

rate=1r/s 초당 평균 1개 이하 요청을 수용합니다.

​ 1초안에 동시 3개 요청 시 1개 처리, 2개 503 응답.

rate=5r/s 1초안에 5개를 처리할 수 있는 것이 아니라, 200ms 당 평균 1개 이하 요청을 수용합니다.

​ 200ms 안에 동시 3개 요청시 1개 처리, 2개 503 응답.

burst=5 rate설정 보다 많은 요청에 대해 5개까지는 대기큐에 넣고 rate 요청분을 처리한 다음 대기큐를 순차적으로 처리합니다.

nodelay burst 대기큐를 순차적으로 처리하면 후순위의 요청일수록 응답속도가 떨어지는 단점이 있습니다. 만약 대기큐의 요청건들도 바로 처리하도록 하고싶다면 nodelay 옵션을 사용하면 됩니다. 그렇다고 대기큐가 사라지는건 아닙니다. nginx가 대기큐를 일시적으로 모두 꺼내면서 taken이라는 표시를 해둡니다. taken으로 표시된 대기큐는 사용할 수 없는 상태를 의미하며 만약 대기큐가 taken 상태일 때 요청이 들어오면 rate만큼 처리 후 나머지는 대기큐에 넣는것이 아니라 503응답을 내보냅니다. taken 상태의 대기큐는 rate 설정 시간마다 1개씩 표시 해제됩니다.

예) 동일IP에서 rate=5r/s burst=10 nodelay 일때 200ms안에 15의 요청이 들어온경우

​ 11개 처리, 대기큐 10개 taken상태, 4개 503 응답

​ 이후 200ms에 1개씩 대기큐 taken 표시 해제, 2초뒤에 10개 모두 표시 해제됨.

​ 만약 대기큐 10개 중에 3개만 taken이 해제된 상태에서 15개를 재요청을 하게 되면

​ 4개 처리, 11개는 503응답, 3개는 다시 taken 상태.

nodelay 옵션은 요청 사이의 허용 간격을 제한하지 않고 속도 제한을 적용하려는 경우에 유용합니다.

delay=5 burst 대기큐에서 5개까지는 지연없이 처리되고, 나머지 요청분은 지연처리 합니다.

예) 동일IP에서 rate=5r/s burst=10 delay=5 일때 200ms안에 15의 요청이 들어온경우

​ 6개 처리, 5개 지연처리, 4개 503 응답

limit_req_log_level info| notice | warn | error (default: error)

제한 오류 레벨을 설정합니다.

limit_req_status code (default: 503)

제한 오류시 reponse 코드를 지정합니다. 기본값은 503입니다.

봇, 특정 IP 차단

웹 서비스에서 robots.txt를 설정하면 검색 엔진에서 웹사이트의 정보를 수집하기 위해 웹 서버로 요청을 합니다. 만약 robots.txt에 모든 검색 엔진을 허용하게 되면 해외에 많은 검색엔진 등이 수집 요청을 위해 request를 하기 때문에 서버 자원을 소모할 수 있습니다. 따라서 구글이나 네이버 등 원하는 검색 엔진만 허용하고 나머지는 허용하지 않도록 설정합니다.

User-agent: *
Disallow: /
User-agent: Googlebot
User-agent: Yeti
User-agent: Daumoa
Allow: /

하지만 특정 봇들은 robots.txt를 무시하고 수집하러 오기 때문에 이를 nginx 설정에서 차단해줍니다.

(아래처럼 직접 block 지정하여도 되지만 github의 오픈소스 nginx-badbot-blocker 이용하여 block하는 방법도 있습니다.)

http {
    include bad_bot.conf;
    include bad_ip.conf;
    ...
        
    server {
        listen       80;
        server_name  website.com

    #blacklist bot block
	if ($bad_bot) {
	    return 444;
	}

    #blacklist ip block
	if ($bad_ip) {
	    return 444;
	}
        
	#url에 허용하지 않는 특정단어가 포함되어 있다면 block
	if ($request_uri ~* (xmlrpc|.php|.xml|.json|/wp-|XDEBUG_SESSION_START|/phpunit)){
	    return 444;
	}
    ...

conf/bad_bot.conf

map $http_user_agent $bad_bot {
        default                                  0;
        ~*360Spider                              1;
        ~*360Spider                              1;
        ~*80legs                                 1;
        ~*Abonti                                 1;
        ...
}

bad_ip.conf

geo $bad_ip {
    default                                   0;
    114.201.156.67                            1;
	13.228.104.57                             1;
	13.53.64.97                               1;
	134.209.127.243                           1;
	138.68.165.154                            1;
	...
}

그 외 설정

http {
    ...
    #서버 이름 해시 테이블의 버킷 크기를 설정(default: 32|64|128)
    server_names_hash_bucket_size 256; 
    #Response Headers에 서버 정보 숨김
    server_tokens off;
    sendfile        on;
    #응답 헤더를 TCP packet 한 조각으로 전송.
    tcp_nopush on;
    #전송된 데이터를 버퍼링하지 않음.
    tcp_nodelay on;
    #keepalive 타임아웃
    keepalive_timeout 10s;
    #서버가 응답하지 않는 클라이언트에서 연결 닫을 수 있도록 허용.
    reset_timedout_connection on;
    #클라이언트 송신 타임아웃
    send_timeout 10s;
    #클라이언트 header 타임아웃 시간
    client_header_timeout 10s; 
    #클라이언트 body 타임아웃 시간
    client_body_timeout 10s;
    #클라이언트에서 서버로 전송할때 max size (form, file upload 등)
    client_max_body_size 50M;
    ...

참고:

http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html

http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

https://www.nginx.com/blog/rate-limiting-nginx

댓글남기기