페이지네이션 숫자 조작으로 숨겨진 데이터가 노출될 수 있는 이유
웹사이트나 관리자 페이지를 사용하다 보면 목록 하단에 페이지 번호가 있는 경우가 많습니다. 게시글 목록, 상품 목록, 주문 목록, 회원 목록, 문의 내역, 예약 내역처럼 데이터가 많을 때 한 화면에 모두 보여주지 않고 여러 페이지로 나누어 보여주는 기능입니다. 이것을 페이지네이션이라고 합니다.
페이지네이션은 사용성 측면에서 꼭 필요한 기능입니다. 수천 개의 데이터를 한 번에 불러오면 화면이 느려지고 서버에도 부담이 생기기 때문에, 보통 1페이지에는 10개, 20개, 50개처럼 일정한 개수만 보여줍니다. 사용자는 페이지 번호를 눌러 다음 목록을 확인합니다.
하지만 페이지네이션 기능이 잘못 설계되면 보안 문제가 생길 수 있습니다. 특히 사용자가 URL의 페이지 번호나 요청 값을 직접 바꿨을 때 원래 보면 안 되는 데이터가 노출될 수 있습니다. 단순히 숫자를 바꿨을 뿐인데 다른 사람의 주문, 문의글, 예약 정보, 관리자 데이터가 보인다면 심각한 권한 검증 문제입니다.
페이지네이션이란 무엇인가
페이지네이션은 많은 데이터를 여러 페이지로 나누어 보여주는 방식입니다. 예를 들어 게시글이 1,000개 있다면 한 화면에 모두 보여주는 대신 1페이지에 20개씩 나누어 50페이지로 표시할 수 있습니다. 사용자는 1, 2, 3, 다음 버튼을 눌러 목록을 이동합니다.
웹사이트에서는 페이지 번호가 URL에 표시되는 경우가 많습니다. 예를 들어 ?page=1, ?page=2, ?offset=20, ?limit=50 같은 형태입니다. 사용자는 버튼을 누르지만, 실제로는 서버에 특정 페이지 번호나 시작 위치를 요청하는 방식으로 작동합니다.
관리자 페이지에서도 페이지네이션은 자주 사용됩니다. 회원 목록, 주문 목록, 문의 목록, 결제 목록, 예약 목록을 한꺼번에 보여주지 않고 페이지별로 나누어 표시합니다. 이때 페이지네이션은 단순한 화면 기능처럼 보이지만, 실제로는 데이터 조회와 직접 연결됩니다.
문제는 서버가 사용자가 요청한 페이지 번호만 믿고 데이터를 내려줄 때 발생합니다. 페이지 번호를 바꾸는 것은 누구나 할 수 있기 때문에, 서버는 매번 “이 사용자가 이 데이터를 볼 권한이 있는지” 확인해야 합니다.
페이지 번호 조작이란 무엇인가
페이지 번호 조작은 사용자가 URL이나 요청 값을 직접 바꾸는 행위를 말합니다. 예를 들어 웹사이트에서 1페이지를 보고 있다가 주소창의 page=1을 page=99로 바꾸거나, limit=20을 limit=1000으로 바꾸는 방식입니다.
일반적인 페이지 이동이라면 문제가 없습니다. 사용자가 2페이지, 3페이지를 보는 것은 정상 기능입니다. 하지만 서버가 페이지 번호 조작을 제대로 검증하지 않으면 원래 화면에 노출되지 않아야 할 데이터까지 내려줄 수 있습니다.
예를 들어 사용자는 자신의 문의글만 봐야 하는데, 페이지 번호를 크게 바꾸자 다른 사람의 문의글이 보이는 경우가 있을 수 있습니다. 또는 일반 관리자는 특정 지점의 주문만 봐야 하는데, 페이지 값을 조작하자 전체 주문 목록이 보이는 경우도 문제가 됩니다.
페이지 번호 조작 자체가 고급 해킹 기법은 아닙니다. 하지만 이런 단순한 조작으로 데이터가 노출된다면 시스템의 권한 검증이 잘못되어 있다는 뜻입니다. 보안에서는 이런 문제를 매우 중요하게 봐야 합니다.
숨겨진 데이터가 노출되는 이유
숨겨진 데이터가 노출되는 가장 큰 이유는 서버가 화면 기준으로만 데이터를 제한하고, 실제 조회 권한은 제대로 확인하지 않기 때문입니다. 즉, 프론트 화면에서는 일부 버튼을 숨겨두었지만 서버 요청은 그대로 받아주는 구조입니다.
예를 들어 일반 사용자의 마이페이지에서는 본인 주문만 보이도록 화면을 구성했다고 가정하겠습니다. 하지만 서버 API가 page 값만 받고 사용자 권한 조건을 적용하지 않으면, 페이지 번호 변경으로 다른 주문 데이터가 섞여 내려올 수 있습니다.
관리자 페이지에서도 비슷한 문제가 발생합니다. 지점 관리자는 자기 지점의 주문만 봐야 하는데, 서버 조회 조건에 지점 필터가 빠져 있으면 페이지를 넘기다가 다른 지점 주문이 보일 수 있습니다. 화면에 지점 선택 버튼이 없더라도 서버에서 제한하지 않으면 의미가 없습니다.
결국 중요한 것은 페이지네이션 숫자가 아니라 데이터 조회 조건입니다. 서버는 항상 로그인 사용자, 역할, 소속, 권한, 데이터 소유자를 기준으로 조회 범위를 제한해야 합니다. 페이지 번호는 그 제한된 범위 안에서만 작동해야 합니다.
마이페이지에서 발생할 수 있는 문제
마이페이지는 사용자의 개인정보가 모이는 공간입니다. 주문 내역, 배송지, 문의 내역, 예약 정보, 결제 상태, 쿠폰, 포인트, 첨부파일 등이 표시될 수 있습니다. 따라서 페이지네이션 오류가 발생하면 개인정보 유출로 이어질 가능성이 큽니다.
예를 들어 사용자가 자신의 주문 내역 1페이지를 보고 있는데, URL의 페이지 번호를 바꿨더니 다른 사람의 주문 내역이 나타난다면 심각한 문제입니다. 주문 내역에는 이름, 전화번호, 주소, 상품명, 결제 금액, 배송 상태가 포함될 수 있습니다.
문의글 목록에서도 문제가 생길 수 있습니다. 고객센터 문의에는 환불 사유, 불만 내용, 계정 문제, 건강 정보, 개인 사정, 첨부파일이 포함될 수 있습니다. 본인 문의만 보여야 하는데 페이지 조작으로 다른 사람의 비밀 문의가 보인다면 서비스 신뢰도에 큰 타격이 됩니다.
예약 서비스에서도 위험합니다. 병원, 상담, 클래스, 숙박, 차량, 행사 예약 정보가 노출될 수 있습니다. 예약 날짜와 장소, 이름, 연락처가 함께 보이면 사용자의 일정과 생활 정보가 드러납니다.
관리자 페이지에서 발생할 수 있는 문제
관리자 페이지의 페이지네이션 오류는 더 큰 피해를 만들 수 있습니다. 관리자 페이지에는 일반 사용자보다 더 많은 데이터가 표시되기 때문입니다. 회원 목록, 주문 목록, 신청자 명단, 상담 내역, 결제 내역, 관리자 메모 등이 포함될 수 있습니다.
특히 관리자 권한이 여러 단계로 나뉘어 있는 서비스에서는 주의가 필요합니다. 최고 관리자, 지점 관리자, 판매자, 상담원, 배송 담당자, 마케팅 담당자처럼 역할에 따라 볼 수 있는 데이터가 달라야 합니다. 그런데 페이지네이션에서 권한 필터가 빠지면 다른 역할의 데이터까지 보일 수 있습니다.
예를 들어 판매자는 자신의 상품 주문만 봐야 하는데, 페이지 값을 조작하자 다른 판매자의 주문이 보이는 경우가 있습니다. 상담원은 본인 담당 문의만 봐야 하는데, 전체 문의 목록이 내려오는 경우도 문제가 됩니다.
관리자 페이지에서는 “관리자니까 다 봐도 된다”는 식으로 설계하면 안 됩니다. 내부 관리자도 역할에 따라 권한을 나누어야 하며, 페이지네이션 요청마다 그 권한이 적용되어야 합니다.
limit 값 조작으로 대량 데이터가 노출되는 경우
페이지네이션에는 페이지 번호뿐 아니라 한 번에 몇 개를 가져올지 정하는 값이 함께 사용됩니다. 보통 limit, size, perPage, count 같은 이름으로 쓰입니다. 이 값이 조작되면 대량 데이터 노출 문제가 생길 수 있습니다.
예를 들어 화면에서는 한 페이지에 20개만 보이도록 되어 있지만, 사용자가 요청 값을 limit=10000으로 바꿀 수 있다면 서버가 한 번에 수천 개의 데이터를 내려줄 수 있습니다. 이 경우 페이지를 여러 번 넘기지 않아도 대량 개인정보를 받을 수 있습니다.
회원 목록, 주문 목록, 문의 내역 같은 데이터에서 이런 문제가 발생하면 피해가 큽니다. 공격자는 자동화 도구를 사용하지 않아도 단순한 요청 값 변경만으로 많은 데이터를 수집할 수 있습니다.
서버는 클라이언트가 보낸 limit 값을 그대로 믿으면 안 됩니다. 최대 조회 개수를 서버에서 강제로 제한해야 합니다. 예를 들어 사용자가 어떤 값을 보내더라도 한 번에 최대 50개 또는 100개까지만 내려주도록 설정해야 합니다.
offset 값 조작으로 과거 데이터가 노출되는 경우
페이지네이션에는 offset 값이 사용되기도 합니다. offset은 데이터 목록에서 몇 번째부터 가져올지를 의미합니다. 예를 들어 offset=0은 처음부터, offset=20은 21번째 데이터부터 가져오는 방식입니다.
문제는 offset 값을 조작하면 오래된 데이터나 숨겨진 데이터에 접근할 수 있다는 점입니다. 화면에서는 최근 30일 데이터만 보여주도록 설계했지만, 서버가 offset만 기준으로 전체 데이터를 조회한다면 과거 데이터가 노출될 수 있습니다.
예를 들어 이벤트 신청자 목록에서 운영자는 최근 이벤트 신청자만 보이게 하려고 했는데, offset 값을 바꾸자 이전 이벤트 신청자 정보까지 내려오는 경우가 있을 수 있습니다. 또는 탈퇴한 회원의 과거 문의 기록이 목록에는 보이지 않지만 offset 조작으로 조회되는 경우도 위험합니다.
서버는 offset과 page 값을 처리하기 전에 반드시 조회 범위를 제한해야 합니다. 기간, 상태, 소속, 사용자 ID, 권한 조건을 먼저 적용하고, 그 결과 안에서만 페이지네이션을 수행해야 합니다.
정렬값 조작으로 숨은 항목이 보이는 경우
페이지네이션과 함께 정렬값이 사용되는 경우도 많습니다. 예를 들어 최신순, 오래된순, 주문금액순, 이름순, 상태순으로 목록을 정렬할 수 있습니다. 요청 값으로 sort, order, direction 같은 값이 전달됩니다.
이 정렬값이 제대로 제한되지 않으면 숨겨진 데이터가 노출될 수 있습니다. 화면에서는 특정 정렬만 제공하지만, 사용자가 요청 값을 바꾸면 서버 내부 컬럼 기준으로 정렬되어 의도하지 않은 정보가 보일 수 있습니다.
예를 들어 관리자 화면에서 사용자에게 보여주면 안 되는 내부 점수, 위험 등급, 관리자 메모 작성일, 내부 처리 상태를 기준으로 정렬할 수 있게 되면 운영 정보가 노출될 수 있습니다. 직접 값이 보이지 않더라도 정렬 결과로 특정 정보가 추정될 수 있습니다.
정렬값은 허용 목록 방식으로 관리해야 합니다. 서버에서 허용한 컬럼과 방향만 처리하고, 그 외 값은 무시하거나 오류 처리해야 합니다. 사용자가 보낸 정렬값을 그대로 데이터베이스 쿼리에 넣는 것은 위험합니다.
권한 검증은 화면이 아니라 서버에서 해야 한다
페이지네이션 숫자 조작 문제가 생기는 핵심 원인은 권한 검증을 화면에 의존하는 것입니다. 버튼을 숨기거나 메뉴를 감추는 것은 사용자 경험을 위한 처리일 뿐, 보안 통제는 아닙니다.
사용자는 브라우저 개발자 도구, 주소창, 네트워크 요청 수정 도구 등을 통해 화면에서 보이지 않는 요청도 직접 보낼 수 있습니다. 따라서 서버는 모든 요청에 대해 권한을 다시 확인해야 합니다.
예를 들어 “내 주문 목록” 요청이 들어오면 서버는 로그인한 사용자 ID를 기준으로 주문을 조회해야 합니다. 사용자가 요청에 다른 사용자 ID를 넣거나 page 값을 바꾸더라도, 서버는 현재 로그인 사용자의 데이터만 내려줘야 합니다.
관리자 페이지도 마찬가지입니다. 지점 관리자는 지점 ID 조건이 항상 적용되어야 하고, 판매자는 자신의 판매자 ID 조건이 항상 적용되어야 합니다. 페이지네이션은 권한 검증 이후의 보조 기능이어야 합니다.
숫자 조작을 막는 안전한 설계
페이지네이션 숫자 조작을 막으려면 먼저 서버에서 조회 범위를 명확하게 제한해야 합니다. 사용자별 데이터, 역할별 데이터, 소속별 데이터, 기간별 데이터 조건을 서버가 직접 적용해야 합니다. 클라이언트가 보낸 값에만 의존하면 안 됩니다.
두 번째로 page, limit, offset 값의 범위를 제한해야 합니다. page 값이 음수이거나 지나치게 큰 값이면 거부하거나 안전하게 처리해야 합니다. limit 값은 최대치를 정해두고 그 이상은 허용하지 않아야 합니다.
세 번째로 정렬값과 필터값은 허용 목록 방식으로 처리해야 합니다. 사용자가 보낸 컬럼명이나 조건을 그대로 쿼리에 반영하면 안 됩니다. 서버에서 허용한 값만 사용할 수 있도록 제한해야 합니다.
네 번째로 비정상적인 요청은 로그로 남겨야 합니다. 지나치게 큰 page 값, 비정상 limit 값, 허용되지 않은 sort 값, 반복적인 조회 시도는 공격 탐색 신호일 수 있습니다. 이런 요청은 추적 가능해야 합니다.
페이지네이션과 IDOR 취약점
페이지네이션 숫자 조작은 IDOR 취약점과 함께 나타날 수 있습니다. IDOR는 사용자가 객체 ID를 조작해 권한 없는 데이터에 접근하는 문제를 말합니다. 예를 들어 orderId=1001을 orderId=1002로 바꿨을 때 다른 사람 주문이 보이는 경우입니다.
페이지네이션에서도 비슷한 문제가 발생합니다. 사용자는 page 값만 바꿨을 뿐이지만, 서버가 권한 조건 없이 전체 데이터를 페이지별로 내려주면 다른 사람의 데이터가 보일 수 있습니다. 즉, 데이터 소유권 검사가 빠진 것입니다.
IDOR의 핵심은 “요청한 사용자가 해당 데이터에 접근할 권한이 있는가”입니다. 페이지네이션도 마찬가지입니다. 단순히 목록을 나누어 보여주는 기능이라도, 각 데이터가 현재 사용자에게 허용된 것인지 확인해야 합니다.
개발자는 페이지네이션을 화면 기능으로만 생각하지 말고 접근 제어 기능과 함께 설계해야 합니다. 목록 조회도 상세 조회만큼 권한 검사가 중요합니다.
검색 조건과 페이지네이션 결합 문제
검색 기능과 페이지네이션이 함께 있을 때도 문제가 생길 수 있습니다. 사용자는 키워드, 기간, 상태, 카테고리, 담당자 같은 조건으로 목록을 검색하고, 그 결과를 페이지별로 봅니다. 이때 검색 조건이 제대로 검증되지 않으면 숨겨진 데이터가 노출될 수 있습니다.
예를 들어 일반 사용자는 자신의 문의만 검색해야 합니다. 하지만 검색 요청에 사용자 ID 조건이 빠져 있다면, 특정 키워드 검색으로 다른 사람의 문의가 결과에 포함될 수 있습니다. 이후 페이지 번호를 넘기면서 더 많은 데이터가 보일 수 있습니다.
관리자 페이지에서는 상태값 조작이 문제가 될 수 있습니다. 화면에서는 “처리중”, “완료”만 선택할 수 있지만, 요청 값을 바꿔 “삭제됨”, “비공개”, “내부검토” 같은 상태를 조회할 수 있다면 숨겨진 데이터가 노출될 수 있습니다.
검색 조건도 서버에서 허용 범위와 권한을 확인해야 합니다. 사용자가 보낸 검색 조건을 그대로 믿지 말고, 현재 사용자 권한에 맞는 조건을 강제로 추가해야 합니다.
API 응답에 과도한 데이터가 포함되는 문제
페이지네이션 화면에는 일부 정보만 보이더라도, 실제 API 응답에는 더 많은 데이터가 포함될 수 있습니다. 예를 들어 화면에는 이름과 상태만 표시되지만, 응답 JSON에는 전화번호, 이메일, 주소, 내부 메모, 관리자 ID가 함께 내려오는 경우입니다.
이 경우 페이지 번호를 조작해 더 많은 목록을 가져오면 화면에 보이지 않는 개인정보까지 수집될 수 있습니다. 사용자는 개발자 도구나 네트워크 응답을 통해 화면에 표시되지 않는 데이터를 확인할 수 있습니다.
서버는 화면에 필요한 데이터만 내려줘야 합니다. “어차피 화면에 안 보이니까 괜찮다”는 생각은 위험합니다. 클라이언트로 전달된 데이터는 사용자가 확인할 수 있다고 봐야 합니다.
목록 API는 상세 API보다 더 최소화해야 합니다. 목록에서는 이름 일부, 상태, 날짜처럼 필요한 항목만 주고, 상세 정보는 권한 확인 후 별도 요청에서 제공하는 방식이 안전합니다.
관리자 엑셀 다운로드와 연결되는 문제
페이지네이션 조작 문제는 엑셀 다운로드 기능과 연결되기도 합니다. 목록 화면에서는 페이지별로 일부 데이터만 보이는데, 다운로드 요청에서는 같은 필터 조건을 사용해 전체 데이터를 내려받을 수 있습니다.
만약 페이지네이션 목록의 권한 조건이 약하다면 엑셀 다운로드에서도 같은 문제가 발생할 가능성이 있습니다. 일반 관리자가 page나 filter 값을 바꿔 다른 지점 데이터까지 내려받거나, limit 값을 조작해 전체 회원 정보를 받을 수 있습니다.
엑셀 다운로드는 대량 데이터 반출 기능이므로 페이지네이션보다 더 엄격하게 관리해야 합니다. 화면 조회 권한과 다운로드 권한을 분리하고, 다운로드 항목과 건수를 제한해야 합니다.
또한 다운로드 로그를 남겨야 합니다. 누가 어떤 조건으로 몇 건을 내려받았는지 기록하지 않으면 사고 발생 시 추적이 어렵습니다. 페이지네이션 조회와 다운로드 기능은 함께 점검해야 합니다.
개발자가 테스트해야 할 부분
개발자는 페이지네이션 기능을 만들 때 정상적인 버튼 클릭만 테스트하면 안 됩니다. URL이나 요청 값을 직접 바꿨을 때 어떻게 동작하는지 확인해야 합니다. page, limit, offset, sort, filter 값을 모두 조작해보는 것이 좋습니다.
첫 번째로 다른 사용자의 데이터가 섞여 나오지 않는지 확인해야 합니다. 사용자 A로 로그인한 상태에서 페이지 번호를 바꾸거나 검색 조건을 조작했을 때 사용자 B의 데이터가 나오면 안 됩니다.
두 번째로 권한이 낮은 관리자 계정으로 테스트해야 합니다. 지점 관리자, 판매자, 상담원, 일반 운영자 계정에서 페이지 값을 바꿔도 권한 밖 데이터가 나오지 않아야 합니다.
세 번째로 limit 값을 크게 바꿔도 서버가 최대치를 강제하는지 확인해야 합니다. limit=100000 같은 요청을 보내도 정해진 최대 개수 이상 내려오면 안 됩니다.
네 번째로 API 응답을 확인해야 합니다. 화면에 보이지 않는 개인정보가 응답에 포함되어 있지 않은지 개발자 도구로 점검해야 합니다.
워드프레스와 플러그인에서 주의할 점
워드프레스 사이트에서도 페이지네이션 보안은 중요합니다. 회원제 플러그인, 예약 플러그인, 쇼핑몰 플러그인, LMS 플러그인, 폼 플러그인은 관리자 목록과 사용자 목록에 페이지네이션을 사용합니다.
플러그인이 제대로 만들어져 있다면 권한 검사를 수행하지만, 오래된 플러그인이나 커스텀 수정 코드에서는 문제가 생길 수 있습니다. 특히 프론트엔드에서 회원별 문의, 주문, 다운로드 자료를 보여주는 기능은 반드시 확인해야 합니다.
우커머스 같은 쇼핑몰 기능에서는 고객 주문, 다운로드 상품, 주소 정보가 권한에 맞게 표시되어야 합니다. 사용자가 페이지 번호나 주문 ID를 바꿨을 때 다른 사람의 주문 정보가 보이면 안 됩니다.
커스텀 개발한 숏코드나 테마 기능도 주의해야 합니다. 화면에서는 현재 사용자 데이터만 보이는 것처럼 보여도, 내부 쿼리에 사용자 조건이 빠져 있으면 페이지네이션을 넘기며 다른 데이터가 나올 수 있습니다.
운영자가 확인해야 할 체크리스트
페이지네이션 보안을 점검할 때는 먼저 마이페이지 목록을 확인해야 합니다. 주문, 문의, 예약, 첨부파일 목록에서 page 값을 바꿔도 본인 데이터만 보이는지 확인해야 합니다.
두 번째로 관리자 권한별 목록을 점검합니다. 낮은 권한의 관리자가 페이지 번호, limit, 검색 조건을 바꿔도 권한 밖 데이터가 보이지 않아야 합니다.
세 번째로 limit 최대값을 확인합니다. 사용자가 한 번에 너무 많은 데이터를 요청할 수 없도록 서버에서 제한해야 합니다.
네 번째로 API 응답 항목을 확인합니다. 화면에 쓰지 않는 개인정보가 응답에 포함되어 있지 않은지 점검해야 합니다.
다섯 번째로 검색 조건과 정렬값을 확인합니다. 허용되지 않은 상태값, 내부 컬럼명, 관리자용 필터가 요청으로 사용되지 않도록 해야 합니다.
여섯 번째로 비정상 요청 로그를 확인합니다. 반복적인 page 조작, 큰 limit 요청, 비정상 sort 값은 공격 탐색 신호일 수 있습니다.
일곱 번째로 엑셀 다운로드 기능과 함께 점검합니다. 목록 조회에서 가능한 조작이 다운로드 기능에서도 가능하지 않은지 확인해야 합니다.
결론
페이지네이션은 많은 데이터를 보기 좋게 나누어 보여주는 기본 기능입니다. 하지만 page, limit, offset, sort 같은 값을 사용자가 직접 조작할 수 있기 때문에, 서버에서 권한 검증과 범위 제한을 제대로 하지 않으면 숨겨진 데이터가 노출될 수 있습니다.
특히 마이페이지, 주문 내역, 문의 목록, 예약 정보, 관리자 회원 목록처럼 개인정보가 포함된 화면에서는 더 주의해야 합니다. 화면에서 버튼을 숨기는 것만으로는 보안이 되지 않습니다. 서버는 매 요청마다 현재 사용자가 해당 데이터를 볼 권한이 있는지 확인해야 합니다.
안전하게 운영하려면 페이지네이션 전에 권한 조건을 먼저 적용하고, limit 최대값을 제한하며, API 응답에는 필요한 정보만 포함해야 합니다. 검색 조건과 정렬값도 허용 목록 방식으로 관리해야 합니다. 페이지네이션 숫자 조작은 단순한 장난처럼 보일 수 있지만, 잘못된 설계에서는 개인정보 유출로 이어질 수 있는 중요한 보안 문제입니다.