WP 보안: wp-admin 보안 | 로그인 주소 변경·로그인 제한·XML-RPC

공정위 광고 문구 Ver 3.1
공정위 광고 문구 Ver 3.2(모바일 전용)

wp-admin 보안은 워드프레스 관리자 화면과 로그인 화면으로 들어오는 무차별 대입 공격을 막는 설정입니다. 워드프레스 사이트 여러 개를 운영하는 Nginx + Apache 구성 서버의 접근 기록을 확인한 결과, 로그인 주소를 바꾼 뒤에도 xmlrpc.php로 들어오는 자동 요청이 이어져 서버 단의 속도 제한까지 함께 적용하게 되었습니다.

1️⃣ wp-admin과 wp-login.php가 공격받는 이유

워드프레스 관리자 주소가 알려져 있다는 것이 취약점이지만 보안을 강화하여 이를 보완할 수 있습니다. 모든 워드프레스 사이트가 같은 /wp-admin/과 [wp-login.php]를 사용하며, 로그아웃한 상태로 /wp-admin/을 열면 [wp-login.php] 로그인 화면으로 이동합니다.

공격은 이 로그인 화면에 사용자명과 비밀번호를 반복해서 넣어 보는 무차별 대입으로 들어옵니다. 그래서 wp-admin 보안은 주소를 감추는 일보다 로그인 시도를 막는 일이 먼저입니다.

워드프레스 로그인 입구 두 곳
브라우저로 접속
/wp-admin/
로그아웃 상태면 로그인 화면으로 이동(302)
wp-login.php
로그인 화면 · 사용자명과 비밀번호 입력
앱·자동 요청
xmlrpc.php
로그인 화면 없이 POST 요청으로 사용자명과 비밀번호 전달
로그인 화면을 거치지 않음
system.multicall 로 요청 한 번에 여러 비밀번호 시도 · 로그인 주소를 바꿔도 주소 그대로
같은 사용자명·비밀번호 확인
두 입구 모두 워드프레스의 같은 인증 함수로 계정을 확인
무차별 대입 공격은 두 입구 중 열려 있는 쪽으로 들어옵니다. 로그인 주소 변경은 위쪽 입구에만 적용되므로, xmlrpc.php는 따로 닫거나 요청 수를 제한합니다.

2️⃣ 로그인 주소 변경의 효과와 한계

보안 플러그인으로 로그인 주소를 바꾸면 기본 [wp-login.php]는 404 오류를 돌려주게 되어, 기본 주소를 그대로 두드리는 자동 공격은 로그인 화면까지 오지 못합니다. 바꾼 주소는 관리자 계정을 가진 사람에게 따로 알려야 합니다.

🧭 워드프레스 로그인 입구
설명 다이어그램
⬜ 플러그인 없음
🛡️ Hide My WP
🚪 로그인 입구
/wp-login.php · /wp-admin/모든 워드프레스에 같은 주소
로그인 화면404
/newlogin바꾼 주소 · 관리자만 앎
로그인 화면
POST /xmlrpc.php로그인 화면 없이 비밀번호 확인
비밀번호 확인 응답비활성 405
테스트 사이트 실측 · 로그아웃 상태 요청
→
🤖 자동 공격
알려진 주소로 비밀번호를 넣어 볼 수 있는 입구
2곳
1곳
0곳
플러그인이 없으면 모든 워드프레스 사이트와 같은 [wp-login.php]와, 로그인 화면 없이 비밀번호를 확인하는 [xmlrpc.php] 두 곳으로 비밀번호를 시험할 수 있습니다.
로그인 주소를 바꾸면 [wp-login.php]와 /wp-admin/은 404를 돌려주지만, [xmlrpc.php]는 사용자명과 비밀번호가 맞는지 그대로 답합니다.
XML-RPC까지 끄면 [xmlrpc.php]의 로그인 요청은 XML-RPC 서비스가 비활성이라는 답으로 끝나, 알려진 주소로는 비밀번호를 시험할 수 없습니다.

이 방법은 손이 적게 들지만, 바꾼 주소를 잊으면 관리자 화면에 들어갈 수 없어 FTP나 파일 관리자에서 그 플러그인 폴더 이름을 바꿔 비활성화해야 합니다. 여러 사람이 관리하는 사이트라면 주소를 한곳에 기록해 두는 편이 맞습니다.

3️⃣ 로그인 시도 제한과 2단계 인증

로그인 주소를 바꿔도 새 주소가 알려지면 비밀번호를 맞히는 시도는 그대로 이어집니다. 워드프레스 코어에는 로그인 실패 횟수 제한이 없어, 틀린 비밀번호를 몇 번 넣어도 같은 로그인 화면이 다시 열립니다.

🧭 로그인 시도 제한과 2단계 인증
설명 다이어그램
⬜ 플러그인 없음
🛡️ 보안 플러그인
🔑 같은 IP의 로그인
1번째틀린 비밀번호
로그인 화면 다시3번 남음
2번째틀린 비밀번호
로그인 화면 다시2번 남음
3번째틀린 비밀번호
로그인 화면 다시1번 남음
4번째틀린 비밀번호
로그인 화면 다시20분 차단
5번째차단 중틀린 비밀번호로그인 · XML-RPC 요청
로그인 화면 다시xmlrpc.php도 403
비밀번호 입력맞는 비밀번호
코드 입력 화면
Authentication Code인증 앱의 6자리 · 30초마다 바뀜
관리자 화면
비밀번호만 알아낸 공격자코드를 모름
코드 화면에서 멈춤
테스트 사이트 실측 · 같은 IP에서 연속 요청
→
🤖 자동 공격
비밀번호를 계속 시험할 수 있나
제한 없음
4번째에 차단
코드에서 멈춤
플러그인이 없으면 같은 IP에서 틀린 비밀번호를 여섯 번 넣어도 매번 같은 로그인 화면이 다시 열립니다.
로그인 시도 제한을 켜면 틀릴 때마다 남은 횟수가 표시되고, 4번째로 틀리면 그 IP는 20분 동안 로그인할 수 없습니다. 차단 중에는 xmlrpc.php 요청도 403으로 끝납니다.
2단계 인증을 켜면 비밀번호가 맞아도 인증 앱이 30초마다 새로 만드는 6자리 [Authentication Code]를 넣어야 관리자 화면이 열립니다.

로그인 시도 제한 플러그인은 정해진 횟수를 넘긴 IP를 일정 시간 차단하고, 2단계 인증은 비밀번호가 맞아도 휴대폰 인증 앱의 코드를 한 번 더 요구합니다. 2단계 인증을 켤 때는 휴대폰을 잃었을 때 쓸 백업 코드를 함께 보관합니다.

4️⃣ xmlrpc.php 닫기

[xmlrpc.php]는 로그인 화면을 거치지 않고 사용자명과 비밀번호를 받는 두 번째 입구이며, [system.multicall] 방식을 쓰면 요청 한 번에 여러 비밀번호를 시험할 수 있습니다.

워드프레스 사이트 여러 개를 운영하는 Nginx + Apache 구성 서버의 접근 기록을 보면, 10월 13일 0시부터 9시 50분까지 [xmlrpc.php]로 들어온 POST 요청이 2,250건이었습니다. 요청은 IP 22개에서 들어왔고, User-Agent는 Jetpack과 WordPress.com을 사칭하고 있었습니다.

워드프레스 모바일 앱이나 Jetpack을 사용하지 않는다면 Nginx 설정에서 [xmlrpc.php] 요청을 막거나 보안 플러그인의 XML-RPC 끄기 설정을 사용합니다.

# server 블록 안 — xmlrpc.php 요청 거부
location = /xmlrpc.php {
    deny all;
}

이 설정을 넣으면 [xmlrpc.php]로 들어오는 GET과 POST 요청이 PHP까지 가지 않고 모두 403으로 끝납니다. 보안 플러그인의 XML-RPC 끄기는 로그인이 필요한 요청만 거부하고 파일은 계속 응답하므로, PHP 실행까지 줄이려면 Nginx에서 막는 편이 맞습니다.

5️⃣ 서버에서 막기: 관리자 접근 IP 제한·속도 제한

고정 IP에서만 관리한다면 Nginx에서 [wp-login.php]와 /wp-admin/을 그 IP만 허용하는 방법이 가장 확실합니다. 집이나 사무실 IP가 자주 바뀌는 환경에는 맞지 않습니다.

/wp-admin/ 전체를 막으면 될 것 같지만, 방문자 화면의 검색이나 장바구니 같은 기능이 [admin-ajax.php]로 요청을 보내는 경우가 많아 그 파일은 열어 두어야 합니다.

# server 블록 안 — PHP 처리 location 보다 위에
location = /wp-login.php {
    allow 203.0.113.10;   # 관리하는 고정 IP
    deny all;
    # 이 아래에 PHP 처리 location 의 설정을 그대로 옮김
}

지정한 IP가 아닌 곳에서 [wp-login.php]를 열면 403이 나오고, 지정한 IP에서는 로그인 화면이 열립니다. location 안에는 사이트의 PHP 처리 설정(fastcgi_pass 등)을 그대로 옮겨 넣어야 로그인 화면이 정상으로 실행됩니다.

IP를 고정할 수 없다면 로그인하지 않은 방문자의 PHP 요청에 속도 제한을 거는 방법이 있으며, 1분에 30회로 제한해 자동 요청이 서버 자원을 차지하지 못하게 한 과정은 php-fpm max_children 경고 글에 있습니다.

# http 블록 — 로그인 쿠키가 없는 요청만 IP별로 계산
map $http_cookie $php_anon_key {
    default                 $binary_remote_addr;
    "~*wordpress_logged_in" "";
}
limit_req_zone $php_anon_key zone=php_anon:10m rate=30r/m;

# PHP 처리 location 안
limit_req zone=php_anon burst=60 nodelay;
limit_req_status 429;

로그인하지 않은 IP 하나가 PHP 요청을 연달아 보내면 61번째까지 처리되고 그 뒤로는 429가 반환되며, 로그인 쿠키가 있는 요청은 계산하지 않습니다. 페이지 캐시가 PHP를 거치지 않고 응답하는 구성이라면 일반 방문자의 페이지 열람은 이 제한에 걸리지 않습니다.

ℹ️ 제휴 안내
본 사이트의 콘텐츠에는 제휴 링크가 포함되어 있습니다. 방문자가 이 링크를 통해 상품 또는 서비스를 구매하면 본 사이트는 판매처로부터 수수료를 지급받습니다. 이 과정에서 구매자가 지불하는 금액(이벤트 할인 시 금액이 내려갑니다↓)은 오르지 않습니다. 게시된 가격·할인·재고 정보는 작성 시점 기준이며 실제와 다를 수 있으므로, 구매 전 판매처에서 최종 확인하시기 바랍니다. 상품 선정과 평가는 자체 기준에 따라 작성되며, 수수료 지급 여부가 소개 순서나 평가 내용에 영향을 주지 않습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

인간임을 증명하세요: 2   +   10   =