API 설계 시 고려사항

jiji·2023년 11월 12일
0

Spring Boot Project 🌱

목록 보기
4/16
  • 목적과 사용자의 요구사항
  • 일관된 인터페이스를 통해 사용자가 예측 가능하게 동작하는 API
  • RESTful 또는 GraphQL 선택
  • 인증 및 보안
  • 에러 처리
  • 성능 최적화 / 가용성과 확장성 / 유지 보수

📍 인증 (Authentication)

🧐 Basic Authentication

  1. 클라이언트가 서버에 요청을 보냅니다.
  2. 서버는 요청 헤더에 "Authorization" 필드를 확인합니다.
  3. "Authorization" 필드에는 "Basic"이라는 인증 방법과 사용자 이름과 비밀번호의 Base64로 인코딩된 조합이 포함됩니다.
  4. 서버는 Base64 디코딩을 통해 사용자 이름과 비밀번호를 추출합니다.
  5. 서버는 추출된 사용자 이름과 비밀번호를 사용하여 사용자를 인증합니다.
  • BA는 웹 서버와 클라이언트 간의 인증을 위한 간단한 인증 방법 중 하나입니다.
    • Basic Auth를 사용하지 말고 표준 인증방식을 사용하세요. (ex. JWT, OAuth 등)
  • HTTP 요청 Header에 사용자 정보(ID와 PW)를 포함시켜 서버에 자격 증명을 제공합니다.
  • 보안 수준이 낮아서 암호화되지 않은 텍스트로 인증 정보를 전송하기 때문에 보안에 취학합니다.
    • 패킷을 중간에 가로채서 인증정보 알아내면 바로 치고 들어와 버리는 방식이어서 되도록이면 지양
  • 대안으로 HTTPS를 쓰는 것은? 서버에 SSL 인증서를 저장 및 관리해야 하는 추가적인 운영 부담이 있고, 결정적으로 SSL을 통해 전송된 트래픽은 캐시되지 않습니다. (이 부분은 잘 생각하셔야 합니다.)
  • 또 어떤 분들은 X.509 인증서를 각 클라이언트에 설치하는 TLS 기능을 이용하시기도 하는데요. 해당 인증서가 만료되었을때 수많은 클라이언트에 깔린 인증서를 어떻게 처리하고 재발행할지에 대한 고민이 필요합니다.
  • 클라이언트에서 인증서를 관리하는 방식이 서버에서 인증서를 관리하는 것보다 훨씬 힘들고 부담스럽습니다. (유념하세요.)
  • 결론은 비보안 컨텐츠들은 HTTP를 통해 트래픽을 받아내고, 인증된 안전한 페이지의 보안은 HTTPS를 혼합해서 사용하세요.
  • SSL이든 TLS든 사용 목적을 분명히 한 후 부분적으로 집중화해서 적용하세요. 범용으로 적용하면 성능 손해가 발생합니다.

표준 인증방식

인증, 토큰 생성, 패스워드 저장은 직접 개발하지 말고 표준을 사용하세요.

  • 참 중요한 포인트입니다. 적극 동감하는 부분입니다.
  • 보안 관련된 체계는 시장에서 널리 인증받고 있는 JWT, OAuth등과 같은 솔루션, 기술을 사용하시기 바랍니다.
  • 다시 말해, 직접 보안 레이어를 구현하시기를 되도록이면 피하시기 바랍니다. 잘못하면 Hell gate를 여는 꼴이 됩니다.
  • 보안 관련 기술은 차용하고 핵심 서비스 구현에 해당 노력과 시간을 투자하시는게 현명한 결정이라 믿고 있습니다.

🧐 JWT (JSON Web Token)

  • 무작위 대입 공격을 어렵게 하기 위해 랜덤하고 복잡한 키값 (JWT Secret)을 사용하세요.
  • 요청 페이로드에서 알고리즘을 가져오지 마세요. 알고리즘은 백엔드에서 강제로 적용하세요. (HS256 혹은 RS256)
  • 토큰 만료기간 (TTL, RTTL)은 되도록 짧게 설정하세요.
  • JWT 페이로드는 디코딩이 쉽기 때문에 민감한 데이터는 저장하지 마세요.
  • Amazon Cognito API를 통해 JWT 방식인 Temporary Credentials(토큰 3개)
  • 요즘 인증 트랜드가 User base 인증 보다는 Role base 인증 방식이어서 JWT 임시 토큰을 발급하여 관리하는게 안전하다 생각합니다.
  • 물론 authorization HTTP Header에 해당 토큰값이 물려서 들어가거나 Session에 물려서 노출될 위험은 있지만, 뒷단에서 Role과 매핑 처리하면 인증과 별도로 권한체크가 수행되어 비인가된 요청은 deny 처리되는 효과도 볼 수 있습니다.

🧐 OAuth

  • 허용된 URL만 받기 위해서는 서버단에서 redirect_uri가 유효한지 항상 검증하세요.
  • 토큰 대신 항상 코드를 주고 받으세요. (respons_type=token을 허용하지 마세요)
  • OAuth 인증 프로세스에서 CSRF를 방지하기 위해 랜덤 해쉬값을 가진 state 파라미터를 사용하세요.
  • 디폴트 스코프를 정의하고 각 애플리케이션마다 스코프 파라미터의 유효성을 검증하세요.
  • 가장 유명한 인증 방식입니다.
  • 적용하기 위한 레퍼런스도 많고, 자료도 풍부한 편입니다.
  • 하지만 적용하려면 기본 Auth Flow와 주변 짝투리 지식들을 파악하실 필요는 있습니다.

📍 접근 (Access)

  • DDoS나 무작위 대입 공격을 피하려면 요청수를 제한하세요. (Throttling)
  • MITM (중간자 공격)을 피하려면 서버단에서 HTTPS를 사용하세요.
  • SSL Strip 공격을 피하려면 HSTS 헤더를 SSL과 함께 사용하세요.
  • 제 경우는 중간에 API Gateway Layer를 하나 두고, 모든 요청 HTTPS Header에 API Key라는 헤더를 추가했습니다.
  • 넘어온 API Key를 통해 Throttling과 모니터링, 통계등 각종 지표 데이터도 수집하는 용도로 사용하고 있습니다.
  • 또한 API Key는 CorelationID와 조합해서 사용하면 로깅과 트레이싱에 유용하여 디버깅에 효과적입니다.

📍 입력 및 요청 (Input)

  1. 각 요청 연산에 맞는 적절한 HTTP 메서드를 사용하세요. GET (읽기), POST (생성), PUT (대체/갱신), DELETE (삭제)
  • REST 방식으로 API를 구현하면 직관적이고, 좋아 보이긴 합니다.
  • But, 구성원간에 합의가 반드시 필요하구요. 해당 기술을 리드하시는 분의 역량과 추진력도 절대적입니다.
  • 그리고 REST라는 개념이 정확한 가이드라인이 없어서 당사자의 편한 입장에서 해석하고 적용하려는 경향도 없지 않아 있습니다.
  • 실례로 PUT Method는 보안 관련 이슈가 있으니 사용하지 말고 POST로 대체하라는 가이드도 있습니다.

  1. 여러분이 지원하는 포맷 (예를 들어 application/json이나 application/xml 등)만을 허용하기 위해서는 요청의 Accept 헤더의 content-type을 검증하여 매칭되는게 없을 경우엔 406 Not Acceptable로 응답하세요.
  • 클라이언트와 서버간의 capability 교환을 위해 가능한 HTTP OPTIONS Method를 사용하고 있습니다.
  • 서버가 지원 가능한 포맷과, 각종 커스텀 헤더들을 OPTIONS Method의 Response로 반환하여 선을 딱 그어 버리는 거죠.
  • 구현할 땐 추가적으로 구현해야 하는 수고도 있지만, Front End 개발시 자주 접하는 CORS 이슈도 덩달아 해결할 수 있습니다.
  1. 요청 받은 POST 데이터의 content-type을 검증하세요. (예를 들어 application/x-www-form-urlencoded나 multipart/form-data 또는 application/json 등)

  2. 일반적인 취약점들을 피하기 위해선 사용자 입력의 유효성을 검증하세요. (예를 들어 XSS, SQL-Injection 또는 Remove Code Execution 등)

  3. URL에는 그 어떤 민감한 데이터 (자격 인증 (crendentials), 패스워드, 보안 토큰 또는 API 키)도 포함하고 있어서는 안되며 이러한 것들은 표준 인증 방식의 헤더를 사용하세요.

📍 서버 처리

  • 잘못된 인증을 피하기 위해 모든 엔드포인트가 인증 프로세스 뒤에서 보호되고 있는지 확인하세요.
  • 사용자의 리소스 식별자를 사용하는건 지양하세요. /user/654321/orders 대신 /me/orders를 사용하세요.
  • 자동 증가 (auto-increment) 식별자 대신 UUID를 사용하세요.
  • XML 파일을 파싱하고 있다면, XXE (XML 외부 엔티티 공격, XML external entity attack)를 피하기 위해 엔티티 파싱을 비활성화 하세요.
  • XML 파일을 파싱하고 있다면, 지수적 엔티티 확장 공격을 통한 빌리언 러프/XML 폭탄을 피하기 위해 엔티티 확장을 비활성화 하세요.
  • 파일 업로드에는 CDN을 사용하세요.
  • 거대한 양의 데이터를 다루고 있다면, HTTP 블로킹을 피하고 응답을 빠르게 반환하기 위해 워커나 큐를 사용하세요.
  • 디버그 모드를 꺼놓는일은 절대 잊지 마세요.
  • 상기의 적혀 있는 사항들 이외에 아래의 사항들도 참고하세요.
  • 일단 기본적으로 HA 구조는 가져가셔야 합니다. 한쪽 서버가 죽으면 전체 시스템 사용이 불가하게 되는 이런 구조는 절대 피하세요.
  • 서버 앞단에 Load Balancer를 추가하여 패킷을 분산 시키세요.
  • 클러스터링 개념도 적용하시고, Auto-Scaling 처리시 Service Discovery를 통해 서비스/서버 자원 관리를 효율적으로 구성하세요.
  • 그리고 서버로의 진입 포인트, 즉 인터페이싱은 여기 저기 문어발식으로 구성하지 마시고, 단일 포인트, 한놈이 전담하게 하세요.
  • 정적 컨텐츠는 서버 트래픽을 줄이기 위해 CDN 서비스를 적용해 보세요. latency 측면이나 비용 측면에서 도움을 받을 수 있습니다.
  • 만약 서버를 클라우드 상에서 운영하고 계신다면, 여건이 허락하는 한도내에서 Managed 서비스를 받아 보세요.(로직에만 집중)
  • 가장 안정되고, 신뢰할 만한 솔루션을 적용하시되, 항상 최신 버전을 유지하도록 하세요. (관리/모니터링을 주기적으로 하란 이야기)

📍 반환 및 응답

  • X-Content-Type-Options: nosniff 헤더를 반환하세요.
  • X-Frame-Options: deny 헤더를 반환하세요.
  • Content-Security-Policy: default-src 'none' 헤더를 반환하세요.
  • X-Powered-By, Server, X-AspNet-Version 등의 디지털 지문 (fingerprinting) 성격의 헤더는 제거하세요.
  • 응답에 content-type을 강제하세요. 만약 application/json 데이터를 반환하고 있다면 응답의 content-type은 application/json입니다.
  • 자격 인증 (crendentials), 패스워드, 보안 토큰과 같은 민감한 데이터는 반환하지 마세요.
  • 각 연산에 맞는 적절한 상태 코드를 반환하세요. (예를 들어 200 OK, 400 Bad Request, 401 Unauthorized, 405 Method Not Allowed 등)
  • 그때 그때 필요한 헤더들을 적절히 구성하시고, 보안 관련된 사항은 두루뭉실하게 제약을 가하기 보다는 명확하게 찝어서 제약 하세요.
  • HTTP 헤더의 상태코드와 더불어 에러시, 내부 에러코드를 표준화해서 클라이언트와 서버간의 명확히 인터페이싱하세요.
[reference] 
API 설계시 고려사항 / 체크리스트|작성자 몽키몽키

0개의 댓글