Browser에서 ajax POST 호출 시 값 변조 방지 방안 질문

안녕하세요.

얼마전 .NET CS에서 웹으로 전환한 초보 개발자인데 ASP.NET 혹은 웹 보안 관련해서는 잘 몰라서 의견을 듣고자 질문을 작성합니다.

현재 DB에서 가져온 데이터를 가공하여 front단에서 list 형식으로 표현하고 있습니다.
사용자가 list의 row를 선택하고 버튼을 누르면 row 정보에 대한 URL을 ajax로 구하고 있는데 이러한 ajax 호출의 POST body의 변조 혹은 랜덤한 row id 대입한 새 request 생성, replay attack에 대한 방지책을 구상하고 있습니다.

만약 악의적인 사용자가 Chrome Dev Tool에서 XHR 호출 URL을 확인하고, post body에 무작위 값을 넣어 Request를 작성한다면 어떻게 막을 수 있을까요?

image

현재 HTTPS, 기본적인 Session Validation 등은 적용중입니다.

제가 생각한 방법은 아래와 같은데 어떤지 한 번 봐주시면 감사하겠습니다.

1. CORS 적용 및 Origin 헤더 WhiteList로 관리
[장]
- 타 API Testing 웹사이트 및 프로그램에서 API 호출 방지?
[단]
- 다른 시스템에서 우리 API 호출 시 지속적으로 WhiteList 수정해야하는 번거로움 & 배포
- Origin 헤더까지 수정하면 확인 불가

2. HMAC 적용하여 ajax 호출 시 서버에서 무결성 검사
[장]
- 가능하다면 best일 것 같긴한데…
[단]
- HMAC을 client단에서 생성할 수 없으니 결국 서버를 1번 더 왕복해야함
HMAC을 얻기 위해 서버에 전송하는 파라미터도 외부에 노출됨

3. 브라우저의 개발자 도구 감지
[장]
- 내부 xhr 호출, javascript 소스 안 보여줄 수 있을듯함
[단]
- 우회 가능할듯

혹여 제가 미처 생각하지 못했던 다른 방안이 있다면 얼마든지 말씀해주시면 감사하겠습니다.
혹시라도 Q&A 게시판이 올바르지 못했다면 죄송합니다.

감사합니다.

적고 나서 보니 질문이 약간 모호하여 추가 정보를 기입합니다…

  1. 사용자가 Page를 호출하고, list에서 버튼을 눌러 ajax 호출하기까지 전 과정은 세션 기반 권한을 체크합니다.

  2. list의 row id는 시퀀셜한 값으로 되어 있습니다.
    ex) X_001, X_002, etc…

  3. ajax 호출을 패킷 스니핑하여 row id의 숫자를 1씩 올린다면 임의의 결과를 반환할 것입니다.
    그래서 우선적으로 HMAC 무결성 검사가 생각났으나, HMAC 생성 로직을 js로 browser단에 포함할 수 없으니 서버를 왕복해야한다면 그 호출조차 스니핑당할 수 있다는 게 제 우려 사항입니다.

  4. 방금 생각난 방법인데 ajax로 HMAC 생성 메서드를 호출 시 POST body에 작성하는 것이 아니라 hidden input에 작성한다면 스니핑 당하지 않을 수 있을지 궁금합니다.

보통 브라우저의 body request를 검증한다고 한다면, 크게 3가지를 검증한다고 생각합니다.

이 요청이 우리 사용자에게서 왔는가?

이 부분은 CORS와 XSRF에 해당합니다. 둘 다 지정하지 않은 사이트에서 요청하는 것을 방지하죠.

하지만 이 부분은 브라우저에서 문자열로 보내는 것이기 때문에 Postman이나 Bruno같은 API 요청 프로그램으로 충분히 위조 가능합니다.

이 요청이 서버의 다른 자산에 영향을 끼치지는 않는가?

이부분은 SQL Injection 혹은 XSS에 해당합니다. purifier로 위험한 값을 필터링할 수 있겠죠.

이 요청이 내가 생각한 유형으로 왔는가?

유효성 검사에 해당합니다. 런타임 오류를 방지하죠.

그래서 악의적인 사용자가 브라우저 개발자 도구를 켜서 요청을 확인하고 위조해서 다시 보내는 것을 막을 수는 없습니다. 브라우저 개발자 도구를 키고 해당 페이지에 들어가면 JavaScript로 개발자 도구 끌 수도 없고, Wireshark같은 도구로 브라우저 바깥에서 캐치해서 확인할 수도 있습니다. 그래서 API에 보내는 값 검증과 Purifier로 막는 방법이 최선이라고 생각합니다.

제시해주신 상황에 덧붙이면, hidden input 또한 브라우저에만 안 보일 뿐이지 body에 담겨서 전송됩니다. row id를 UUID로 바꿔서 쉽게 접근하지 못하게 막고 row id마다 사용자 권한이 있다면 그 사용자의 권한을 확인하는 방법이 있겠습니다.

5 Likes

댓글이 늦어 죄송합니다.

제가 걱정하던 점은 권한 있는 사용자라 하더라도 버튼을 클릭하여 ajax를 호출하였을 때 A → B가 나와야하나, id 값을 조작하여 request를 작성한다면 그 요청은 적법하거나 올바른가? 라는 점이었습니다.

생각해보니, 해당 ajax를 호출하기 위한 페이지에는 접근 권한이 설정되어 있고 인가된 사용자는 list의 모든 데이터, ajax 호출 등에도 권한이 있다고 볼 수 있을 것 같습니다.
그래서 페이지 로드 시 세션에 임의의 토큰 값을 생성하고, 이 토큰 값이 없는 사용자가 ajax 호출 시 fail하도록 할 생각입니다. 반대로 해당 토큰 값이 있는 사용자는 당연 list의 모든 데이터에 view 권한이 있으므로 id를 조작하더라도 view 권한 있다고 보고 정상 return할 생각입니다.

이렇게 처리하는 것이 맞는지는 잘 모르겠지만 기초적인 처리를 하고 나서 추후 보안 조치를 추가하는 식으로 작업해야겠습니다.

WinForm으로 Client 프로그래밍할 때는 제가 보내는 입장이어서 별 문제가 아닌 것 같았지만 Web에서 받는 입장이 되어보니 생각이 많아지네요.

정성스런 답변 감사드립니다.

1 Like

Jsonp 를 사용하지않나요?cors 만해도

1 Like

같은 IP에서 유사한 요청이 상식적인 선을 넘으면 차단하는 쪽을 추천합니다.
비번도 틀릴 경우 3~5초 정도 후에 응답을 주는 것과 비슷합니다.

2 Likes

생각을 좀 빙 둘러서 어렵게 생각하고 계신듯 합니다.

위 인증(토큰, 세션 등)에 대한 보안 처리가 완벽하다면 생각하고 계시는 우려는 접어두어도 괜찮을 것 같습니다.

토큰을 예로 설명하면

헤더에 정상적인 토큰이 포함 되서 인증이 필요한 데이터에 CRUD API 요청이 오는 경우 해당 HttpContext는 이미 앞단에서 인증을 부여 받은 클라이언트라고 볼 수 있습니다.

즉 신뢰할 수 있는 클라이언트의 요청인 것으로 간주해도 무방 합니다.

중간에 토큰을 가로채기 당할 수 있는 보안적 요소는 별개라는 것을 말씀 드리고 싶습니다.

때문에 엑세스 토큰의 유효기간을 짧게 설정하고, 리프래시 토큰을 별도로 발급 한다던지 방법을 사용해서 인증에 관련해 보안적인 처리를 하는 것이죠.

  1. 토큰, 세션 등의 인증 처리 부분에서 보안이 재대로 갖추어 졌다면,
  2. 데이터를 조작하고자 하는 요청자가 해당 데이터에 접근 권한이 있는지 검증이 이루어 진다면

악의적이든 정상이든 요청되는 것은 서버입장에선 신뢰된 처리로 간주 하면 됩니다.
(서비스 거부 공격 같은 지속적 요청은 별개 입니다.)

7 Likes

이것저것 말씀하신 내용으로 미루어보아 제가 이해하는 바로 말씀드리면

클라이언트가 정상적인 방법으로 획득한 인증 수단을 이용해 변조된 요청을 한다면
서버 입장에서는 이 변조된 클라이언트를 구분할 방법은 사실상 없습니다.

대부분의 보안책은 클라이언트의 인증 자체는 신뢰하는 것을 전제로 이루어집니다.
클라이언트가 누군지는 모르겠지만 인증이 유효하다면 이후 로직을 수행할 수 있게 해주는 거죠.

그래서 중간에 가로채기 같은 것을 통해 데이터가 공개되거나 노출되는 것을 막을 수는 있어도(https 같은 것이 그런 예이죠.)

정상적인 client 가 정상적으로 획득한 인증을 이용해 의도적으로 값을 왜곡하여 서버로 전송한다면
서버 입장에서는 그것이 정상 요청인지 아닌지 구별할 방법은 없어요.

client 가 정상적인 방법으로 획득한 인증을 이용해 정상적으로 api 호출할 경우
애초에 서버 입장에서 위조 불가능한 client 만을 대상으로 통신하는 게 아니기 때문에 이걸 구별할 수단이 없는 거예요.
(지금 질문하신 상황이 이 상황 같아요. 해킹에 의해 노출된 클라이언트를 구별하는 게 목적이 아닌 걸로 이해했어요.)

예를 들면 access token 으로 인증을 하는 방식을 채택했다면

정상적으로 로그인한 사용자가 자신이 획득한 access token 으로
의도적으로 왜곡된 요청을 하게될 경우
서버 입장에서는 이 요청을 변조된 요청이다! 라고 구분할 방법이 없다는 얘기입니다.
(refresh token 같은 보조 수단을 함께 쓰지만 원천적으로 막을 수는 없습니다.)

그래서 이럴 때에는 요청이 논리적으로 합당한가… 에 방점을 두고
유효성 검사를 통해 요청 내용의 헛점을 걸러내는 정도의 방법이 최선이라고 할 수 있겠네요.

5 Likes

약관과 화이트/블랙 리스트로 관리하는 것이 좋겠네요.

이런 저런 문제 생각하면 블레이저 서버가 구조적으로 좋고 간편하다는 생각을 해봅니다.