블로그
안전한 WordPress REST API 개발: 실용 가이드
WordPress에서 적절한 보안, 검증 및 성능 모범 사례를 적용하여 커스텀 REST API 엔드포인트를 구축하는 방법을 알아보세요. 이 가이드는 경로 등록, 입력 정제, 권한 콜백, nonce 사용 및 캐싱 전략을 다루어 API를 강력하고 효율적으로 만듭니다.

요약
WordPress REST API는 사이트 기능을 확장하는 강력한 도구이지만, 잘못 구현된 엔드포인트는 사이트를 보안 취약점에 노출시킬 수 있습니다. 이 글은 안전하지 않은 커스텀 API 경로의 일반적인 문제를 해결하기 위해 안전한 엔드포인트 구축 방법을 단계별로 안내합니다. 경로를 올바르게 등록하고, 사용자 입력을 검증 및 정제하며, 콜백을 통해 권한을 적용하고, nonce를 사용하여 CSRF를 방지하는 방법을 배우게 됩니다. 또한 캐싱 및 WP_Query 인수 사용과 같은 성능 최적화 기법도 다룹니다. 이러한 방법을 따르면 안전하고 성능이 뛰어난 REST 엔드포인트를 만들 수 있습니다. 실제 사례와 주의사항이 포함되어 있어 일반적인 함정을 피하는 데 도움이 됩니다.
WordPress REST API는 헤드리스 프론트엔드 구동부터 커스텀 통합까지 개발자에게 다양한 가능성을 열어줍니다. 그러나 큰 힘에는 큰 책임이 따릅니다. 모든 커스텀 엔드포인트는 제대로 보호되지 않으면 공격의 진입점이 될 수 있습니다. 이 가이드는 등록, 입력 처리, 권한, nonce 및 성능에 초점을 맞춰 WordPress에서 안전한 REST API 엔드포인트를 구축하는 방법을 안내합니다. 완료되면 안전하고 효율적이며 유지 관리가 가능한 엔드포인트를 만드는 반복 가능한 프로세스를 갖게 됩니다.
경로 등록: 기초
모든 REST 엔드포인트는 register_rest_route()로 시작합니다. 그러나 많은 개발자가 보안을 강화하는 중요한 매개변수를 생략합니다. 경로를 등록할 때는 네임스페이스(일반적으로 플러그인/테마 슬러그), 경로, 그리고 콜백, permission_callback 및 검증을 위한 args를 포함한 옵션 배열을 지정해야 합니다. 다른 플러그인과의 충돌을 피하기 위해 항상 고유한 네임스페이스를 사용하세요. 예:
add_action('rest_api_init', function () {
register_rest_route('myplugin/v1', '/secure-data', array(
'methods' => 'GET',
'callback' => 'myplugin_secure_data_callback',
'permission_callback' => 'myplugin_permission_check',
'args' => array(
'user_id' => array(
'required' => true,
'validate_callback' => function($param) { return is_numeric($param); },
'sanitize_callback' => 'absint',
),
),
));
});
args 배열을 주목하세요. 이곳에서 검증 및 정제 규칙을 정의합니다. 콜백으로 예상 매개변수를 명시적으로 선언하면 잘못된 데이터가 메인 콜백에 도달하는 것을 방지할 수 있습니다. 이는 Mastering WordPress Hooks의 핵심 원칙입니다(단, REST에 적용됨). 간단한 엔드포인트라도 항상 args를 정의하여 예상 입력을 문서화하고 조기에 오타를 잡아내세요.
입력 검증 및 정제: 데이터 잠금
입력 처리는 가장 흔한 취약점의 원인입니다. WordPress는 검증(데이터가 기준을 충족하는지?)과 정제(데이터 정리)의 두 계층을 제공합니다. validate_callback을 사용하여 처리 전에 잘못된 입력을 거부하세요. 예를 들어 양의 정수만 허용하려면:
'validate_callback' => function($param) { return is_numeric($param) && $param > 0; }
그런 다음 sanitize_callback을 사용하여 값을 정제합니다(예: absint, sanitize_text_field, sanitize_email). HTML이 필요하지 않은 경우 wp_kses_post 사용을 피하고 더 엄격한 정제기를 선호하세요. 배열 매개변수의 경우 array_map('sanitize_text_field', $param)으로 각 항목을 매핑하세요.
모든 인수를 미리 정의하고 싶지 않다면 콜백 내에서 정제할 수도 있습니다:
$user_id = isset($request['user_id']) ? absint($request['user_id']) : 0;
하지만 이 방법은 문서화가 덜 됩니다. 복잡한 엔드포인트의 경우 인라인 검증 콜백을 선호하세요.
권한 콜백: 누가 접근할 수 있는가?
모든 엔드포인트에는 permission_callback이 있어야 합니다. 없으면 WordPress는 여전히 콜백을 요구하지만, 이전 버전에서는 __return_true가 기본값이어서 보안에 매우 취약합니다. 항상 불리언 또는 WP_Error 객체를 반환하세요. 일반적인 패턴:
function myplugin_permission_check() {
if (!current_user_can('edit_posts')) {
return new WP_Error('rest_forbidden', '이 리소스에 접근할 수 없습니다.', array('status' => 403));
}
return true;
}
역할 기반 접근의 경우 current_user_can()과 manage_options, edit_others_posts 또는 커스텀 기능과 같은 능력을 사용하세요. 역할 이름(예: 'administrator')을 하드코딩하지 말고 기능을 사용하여 사이트 소유자가 플러그인을 통해 조정할 수 있도록 하세요. 공개 엔드포인트(예: 게시된 글 가져오기)의 경우 정말 필요한 경우에만 'permission_callback' => '__return_true'를 설정하고, 항상 콜백에서 적절한 데이터 제한을 함께 사용하세요.
Nonce를 사용한 CSRF 보호
REST API 요청은 쿠키 인증 사용자에 대해 유효한 nonce를 확인하지만, 엔드포인트는 쿠키를 사용하지 않는 외부 클라이언트(예: 모바일 앱)에서 접근할 수 있습니다. 엔드포인트가 데이터를 수정하는 경우 CSRF(교차 사이트 요청 위조)로부터 보호되어야 합니다. 내부 사용의 경우 wp_rest nonce를 통해 nonce를 보내며(REST API 미들웨어가 자동 처리), 외부 엔드포인트의 경우 토큰 기반 인증을 구현하거나 요청 헤더에 WordPress nonce를 사용할 수 있습니다. JavaScript 예:
wp.apiFetch({ path: '/myplugin/v1/add-post', method: 'POST', data: { title: 'New' } });
이 방법은 내장 nonce를 사용합니다. 커스텀 통합을 구축하는 경우 wp_create_nonce('wp_rest')로 nonce를 생성하고 X-WP-Nonce 헤더에 포함하세요.
성능: 캐싱 및 최적화
안전한 엔드포인트도 느릴 수 있습니다. 트랜션트를 사용하여 비용이 많이 드는 쿼리를 캐싱하세요:
$cache_key = 'myplugin_recent_posts_' . $user_id;
$posts = get_transient($cache_key);
if (false === $posts) {
$posts = get_posts(array('author' => $user_id, 'posts_per_page' => 10));
set_transient($cache_key, $posts, HOUR_IN_SECONDS);
}
데이터가 변하는 경우 사용자별로 캐싱하세요. 또한 $request->get_param('page')와 offset을 사용하여 페이지네이션을 구현하여 데이터베이스에 과부하가 걸리지 않도록 하세요. 반환되는 필드를 제한하세요. 모든 게시물 데이터를 반환하는 대신 ['ID', 'post_title']를 사용하세요. 트래픽이 많은 엔드포인트의 경우 Redis를 사용한 객체 캐싱을 고려하세요.
실제 사례: 최근 게시물을 위한 안전한 엔드포인트
주어진 사용자 ID에 대한 최근 게시물 제목을 반환하고 edit_posts 권한이 있는 사용자만 접근할 수 있는 엔드포인트를 구축해 보겠습니다. 전체 코드:
add_action('rest_api_init', function () {
register_rest_route('myplugin/v1', '/user-posts/(?P<user_id>\d+)', array(
'methods' => 'GET',
'callback' => 'myplugin_user_posts_callback',
'permission_callback' => function() { return current_user_can('edit_posts'); },
'args' => array(
'user_id' => array(
'required' => true,
'validate_callback' => function($param) { return is_numeric($param); },
'sanitize_callback' => 'absint',
),
),
));
});
function myplugin_user_posts_callback($request) {
$user_id = $request->get_param('user_id');
$cache_key = 'myplugin_user_posts_' . $user_id;
$posts = get_transient($cache_key);
if (false === $posts) {
$query = new WP_Query(array(
'author' => $user_id,
'post_status' => 'publish',
'fields' => 'ids',
));
$posts = $query->posts;
if (!empty($posts)) {
$titles = array();
foreach ($posts as $post_id) {
$titles[] = get_the_title($post_id);
}
set_transient($cache_key, $titles, 6 * HOUR_IN_SECONDS);
return new WP_REST_Response($titles, 200);
}
return new WP_REST_Response(array(), 200);
}
return new WP_REST_Response($posts, 200);
}
이 엔드포인트는 user_id를 검증하고, 권한을 확인하며, 결과를 캐싱하고, 깨끗한 데이터를 반환합니다.
주의사항 및 일반적인 함정
- REST 콜백 내에서
$_GET또는$_POST를 신뢰하지 마세요. 항상$request->get_params()를 사용하세요. - 민감한 데이터(예: 사용자 이메일)를 노출하는 GET 엔드포인트에 대해서도 권한을 검증하세요.
- 오류 처리: 콜백 내에서
wp_send_json_error()를 사용하지 말고 일관성을 위해WP_Error또는WP_REST_Response를 반환하세요. - 테스트: Postman 또는 nonce 헤더가 포함된 curl과 같은 도구를 사용하여 요청을 시뮬레이션하세요.
- Global $wpdb: SQL 인젝션을 방지하기 위해 데이터베이스 쿼리를
$wpdb->prepare()로 감싸세요. - 속도 제한: 엔드포인트가 공개된 경우 트랜션트를 사용하여 커스텀 속도 제한을 구현하는 것을 고려하세요.
결론
안전한 WordPress REST 엔드포인트를 구축하려면 경로 등록, 입력 처리, 권한, nonce 및 성능 등 모든 계층에 주의를 기울여야 합니다. 여기에 설명된 방법, 특히 검증/정제 콜백이 포함된 args 정의, 항상 permission_callback 설정, 비용이 많이 드는 쿼리 캐싱, nonce 사용을 따르면 검증을 견딜 수 있는 엔드포인트를 만들 수 있습니다. 이러한 패턴을 모든 커스텀 경로에 적용하고 인증된 요청과 인증되지 않은 요청 모두를 철저히 테스트하세요. 관련 보안 주제에 대한 자세한 내용은 WordPress Plugin Security & Performance 가이드를 확인하세요. 이제 API를 안전하게 만드세요.
Sources (5)
- WordPress Architecture: A Complete Guide - Liquid Web
- Essential WordPress Plugin Development Best Practices - Pixel Fish
- Best Practices – Plugin Handbook - WordPress Developer Resources
- A Guide To Understanding WordPress Architecture - Pressable
- Modern approach to WordPress plugin development | by Gabriele Bellini - Medium
