NGINX 웹서버를 사용하는 경우 보다 안전한 워드프레스를 위해서 여러가지 안전 장치를 사용할 수 있습니다. 여기서는 워드프레스 보안 강화를 위한 NGINX 설정 방법들을 살펴보겠습니다.
우분투 20.04와 PHP 8 기반 워드프레스를 설치 후 보다 보안을 강화하기 위하여 NGINX 웹서버 세팅을 보완할 수 있는 방법 몇가지를 정리해 봤습니다.
이는 아래 글들을 참조해 적용해 보는 과정에서 조금 체ㅖ적으로 정리할 목적으로 시작했습니다.
nginx Security: How To Harden Your Server Configuration
10 Nginx Rules to Harden WordPress Security
Block access to PHP files on your WordPress site with Nginx
1. XMLRPC 접근 금지
최근에 강제 로그인 공격과 더불어 가장 활발하게 이루어지는 것인 XMLRPC 접속 공략입니다. 저의 경우 로그인 시도보다도 XMLRPC 공략이 더 많은 비중을 차지할 정도로 흔한 공략 방법 중의 하나입니다.
2020/12/20 01:01:11 <strong>[error]</strong> 111371#111371: *190 access forbidden by rule, client: 104.131.116.121, server: goodtrip.kr, request: "POST /xmlrpc.php HTTP/1.1", host: "goodtrip.kr"
2020/12/20 00:41:31 <strong>[error]</strong> 109338#109338: *1276 access forbidden by rule, client: 2.47.139.162, server: goodtrip.kr, request: "POST /xmlrpc.php HTTP/1.1", host: "goodtrip.kr"
2020/12/29 09:02:17 <strong>[error]</strong> 116159#116159: *14596 access forbidden by rule, client: 142.44.251.104, server: happist.com, request: "GET //xmlrpc.php?rsd HTTP/1.1", host: "happist.com"
2020/12/29 09:02:19 <strong>[error]</strong> 116159#116159: *14596 access forbidden by rule, client: 142.44.251.104, server: happist.com, request: "POST //xmlrpc.php HTTP/1.1", host: "happist.com"
2020/12/29 09:24:29 <strong>[error]</strong> 116159#116159: *17211 access forbidden by rule, client: 181.225.65.242, server: happist.com, request: "POST /xmlrpc.php HTTP/2.0", host: "happist.com"
2020/12/29 08:37:06 <strong>[error]</strong> 116159#116159: *12634 access forbidden by rule, client: 50.241.148.97, server: happist.com, request: "POST /xmlrpc.php HTTP/2.0", host: "happist.com"
2020/12/29 01:35:47 <strong>[error]</strong> 113636#113636: *910 access forbidden by rule, client: 164.90.150.130, server: happist.com, request: "GET /.git/config HTTP/1.1", host: "happist.com"
2020/12/29 01:39:55 <strong>[error]</strong> 113636#113636: *1099 access forbidden by rule, client: 64.227.50.5, server: happist.com, request: "GET /.well-known/security.txt HTTP/1.1", host: "happist.com", referrer: "http://happist.com/.
2020/12/29 05:43:59 <strong>[error]</strong> 116159#116159: *5448 access forbidden by rule, client: 67.21.36.5, server: happist.com, request: "GET /.well-known/security.txt HTTP/1.1", host: "happist.com"
2020/12/29 10:15:41 <strong>[error]</strong> 116159#116159: *24201 access forbidden by rule, client: 46.119.190.168, server: happist.com, request: "POST //xmlrpc.php HTTP/1.1", host: "happist.com"
Code language: PHP (php)
이를 방지하기 위해서는 아래와 같은 코드를 적용합니다. 적용 위치는 Nginx 설정 파일 중 server 블럭에 넣습니다.
만약 XMLRPC 기능을 허용해야항 IP가 있다면 allow 다음에 ip 주소를 추가해 줍니다.
location ~* (xmlrpc)\.php$ {
# allow 172.17.0.0/16;
deny all;
log_not_found off;
access_log off;
}
Code language: PHP (php)
이 기능에 대한 자세한 설명은 아래 XMLRPC 방지관련 글을 참조하세요.
2. 요청 타입(Request method)을 제한
대부분 워드프레스 사이트를 비롯한 웹서비스에서 에서 요청 방법은 GET, HEAD, POST, PUT, DELETE 등이 잇습니다. 그렇지만 대부분 웹서비스에서 GET, HEAD, POST만 필요로 합니다.
GET
to retrieve data from your sitePOST
to upload data to your site
따라서 워드프레스 사이트에서 요청 타입 위에서 열거한 두가지 방법 또는 세가비 방법만 사용토록 제한합니다. 다른 방식으로 데이타 요청이 오면 404에러를 내도록 합니다. 보통 GET과 POST 두가지를 제한하도록 되어 있고 어느 자료를 보니 HEAD도 포함하고 있더군요. 그래서 HEAD까지 포함한 스니핏을 여기에 소개해 봅니다.
<code>if</code> <code>($request_method</code> <code>!~ ^(GET|POST|HEAD)$ ) {</code>
<code> return</code> <code>444;</code>
log_not_found off;
access_log off;
<code>}</code>
Code language: PHP (php)
3. 다이렉트 php 파일 접근 방지
해커는 워드프레스 사이트에 악성 PHP 파일을 심어 해킹이나 멀웨어 프로그램을 작동시키려고 시도합니다.
3.1. 다이렉트 php 파일 접근 방지
그래서 제3자가 임의로 PHP 파일을 올릴 수 없도록 제한 합니다. 모든 폴더에 올리는 것을 막을 수 없으니 몇가지 필수 폴더를 지정합니다.
<code>location ~* /(?:uploads|files|wp-content|wp-includes|akismet)/.*.php$ {</code>
<code>deny all;</code>
<code> access_log off;</code>
<code> log_not_found off;</code>
<code>}</code>
Code language: PHP (php)
3.2. find 명령어로 정기적으로 php 파일 삭제
저는 이전에 멀웨어가 침투한 사례가 있었는데요. php 파일이 없어야 되는 곳에 이상한 php 파일이 생기곤 했습니다.
이를 막기 위해서 매일 PHP 파일이 없어야 하는 폴더에 PHP 파일이 있으면 무조건 지우라는 명령을 크론에 놀려 자동 실행시키고 있습니다. 예를 들면 아래와 같은 명령을 적용했습니다.
<em>find /var/happist/wp-content/uploads -name "</em>.php" -exec rm {} \;
find /var/happist/files -name "<em>.php" -exec rm {} \; </em>
<em>find /var/happist/font -name "</em>.php" -exec rm {} \;
Code language: PHP (php)
4. php 유사 파일 실행 금지
php 파일과 유사하한 역활을 하는 파일들이 있습니다. .htaccess
, .user.ini
, .git 파일들이 그러한 예인데요.
보다 안전하게 만들기 위해서 이러한 파일에 직접적으로 접속하는 것을 막도록 합니다.
<code>location ~ /\.(svn|git)/* {</code>
<code>deny all;</code>
<code>access_log off;</code>
<code>log_not_found off;</code>
<code>}</code>
<code>location ~ /\.ht {</code>
<code>deny all;</code>
<code>access_log off;</code>
<code>log_not_found off;</code>
<code>}</code>
<code>location ~ /\.user.ini { </code>
<code>deny all; </code>
<code>access_log off;</code>
<code>log_not_found off;</code>
<code>}</code>
Code language: PHP (php)
5. Nginx와 PHP 버전 정보 숨기기
Nginx와 PHP 버젼을 확인하는 방법은 여러가지가 있기 때문에 이를 숨기는 것은 쉽지는 않습니다. 그렇지만 복잡ㅎ산 방법을 동원해 Nginx와 PHP 버젼을 알아내야 하는 것과 바로 알 수 있는 것과는 차이가 있기 때문에 기본적으로 버전 정보는 숨기도록 합니다.
이렇게 기본적으로 Nginx와 PHP 버전 정보를 숨기는 것은 버전에 따라 보안에 취약한 경우가 있기 때문이 해커가 이를 쉽게 안다면 보다 용이하게 공격할 수 있도록 만들기 때문입니다.
<code>#Hide the nginx version.</code>
<code>server_tokens off;</code>
Code language: PHP (php)
<code>#Hide the PHP version.</code>
<code>fastcgi_hide_header X-Powered-By;</code>
<code>proxy_hide_header X-Powered-By;</code>
Code language: PHP (php)
6. Security Headers
보안 헤더(Security Headers)는 브라우저 행동을 가이드해서 보다 안전한 보안망을 만들어 줍니다. 아래와 같은 보안 옵션들을 추가합니다.
<code>add_header X-Frame-Options SAMEORIGIN;</code>
<code>add_header Strict-Transport-Security "max-age=31536000";</code>
<code>add_header X-Content-Type-Options nosniff;</code>
<code>add_header X-XSS-Protection "1; mode=block";</code>
add_header Referrer-Policy "strict-origin-when-cross-origin";
Code language: PHP (php)
HTTP Sucrity Headers 옵션 적용 시 유의점에 대해서는 아래 글을 참조해 보시기 바랍니다. NGINX 설정 파일에서 add_header 명령을 여러 번 사용하게 되는데 그러면 그 명령 다음부터는 앞에서 설정한 add_header 옵션은 전부 무효가 되어서 다시 세팅해야 한다고 합니다.
참고 위의 옵션 중의 하나인 리퍼러 정책관련 옵션별로 유효성을 정리한 아래 이미지를 보시고 적절한 옵션을 선택하면 될 것 같습니다. 가장 무난한 것으로는 ‘strict-origin-when-cross-origin’을 이야기하고 있습니다.
7. 서브 디렉토리 접근 금지
사이트를 운영하다 보면 /blog와 같은 서브 디렉토리를 만들어 별도 사이트처럼 운영하는 경우가 발생합니다. 그러면 이 경우 외부에서 이러한 디렉토리 접근을 제한할 필요가 있습니다.
<code>location ~ ^/(?!(blog)/?) { </code>
<code> deny all;</code>
<code> access_log off;</code>
<code> log_not_found off;</code>
<code>}</code>
Code language: PHP (php)
8. 로그인 요청 건수 제한 – 사용에 주의 필요
위에서 언급했지만 wp-login.php는 해커들에게 가장 흔한 공격 포인트입니다. 워드프레스에서는 이 로그인 wp-login.php과 XMLRPC.php가 주된 공격 포인트이죠.
해커들은 로그인을 위해서 자동화된 스크립트로 다양한 사용자 이름과 비밀번호를 대입해 1초에만 여러번 무작위 공격을 합니다.
그래서 nginx 웹서버에서 초당 입력할 수 있는 사용자명과 비밀번호 조합을 제한함으로써 해커의 공격을 막을 수 있습니다. 아래는 초당 2번을 입력할 수 있도록 설정했고, 이를 초과하면 막도록 합니다.
먼저 nginx 설정 http블럭에 다음과 같은 정의를 추가합니다.
<code>limit_req_zone $binary_remote_addr</code> <code>zone=WPRATELIMIT:10m</code>
Code language: PHP (php)
그 다음 Nginx 설정하는 server 블럭안에 다음과 같은 location 명령을 추가합니다.
<code>location ~ \wp-login.php$ {</code>
<code> limit_req zone=WPRATELIMIT;</code>
<code>}</code>
Code language: PHP (php)
그러면 자동화된 스크립트로 로그인 시도하는 ip들은 nginx가 알아서 막아 줍니다.
우붙투 20.04 php 8 시스템에서 문제
그런데 이 기능은 php 8이 적용된 시스템에서 워드프레스 로그인 페이지가 로딩되지 못하는 문제가 발생합니다.
워드프레스 자체를 편집할 일이 없다면 문제가 없겠지만 로그인 화면 자체를 띄우지 못하도록 만들어 버리기 때문에 이를 회피하는 방법을 모르는 한 사용할 수는 없겠습니다. 뒤늦게 문제를 발견했는데.. 그럼에도 이 글에서 힌트를 얻어 해결 방법을 찾을 수 있는 분이 있을까 싶어서 이 내용을 지우지 않고 유지했습니다.