접근 제어를 충족시키기 위해 api 디자인하는 방법은?

접근 제어란 오직 등록된 컨슈머만이 API를 사용할 수 있으며, 그중에도 허용된 목표들을 사용할 수 있게 하는 역할하게 해주는 것을 의미합니다. 접근 제어를 하는 두 가지 이유가 있습니다.

  1. 컨슈머가 접근 가능한 영역에 제한을 둠으로써 공격에 대한 가능성을 줄일 수 있습니다.
  2. 컨슈머와 프로바이더 사이의 모든 커뮤니케이션은 안전한 채널 상에서 데이터의 탈취나 위변조 우려 없이 진행되어야 합니다. API에서 등록된 컨슈머만 최종 사용자에게 부여된 권한만큼 딱 필요한 수준의 접근만 허용되어야 합니다.
  3. 선택된 컨슈머에게만 API의 다른 영역에 대한 접근 권한을 부여하는 절차가 쉬워집니다.

이런 이유 때문에 API 디자인에서 스코프를 고려해야 합니다.

스코프란 API에 속한 하나에서 여러 목표들을 의미합니다. 스코프 정의의 방법은 세 가지가 있습니다.

  1. 유연하고 정제된 스코프 정의하기 → 각 목표에 대한 주요 컨셉(또는 자원) 식별을 해야 합니다. 일반 명사를 식별하고 목표의 대표 동사를 식별해야 합니다 ex. “account:read”
  2. 단순하지만 더 굵직한 스코프로 정의하기 → 컨슈머에게 여러 목표에 대한 권한을 적절한 수준에서 한 번에 제공합니다.
  3. API 명세 포맷으로 스코프 정의하기 → API 디자인이 API 명세 포맷을 사용하고 있다면 스코프를 설명하고 있는지 확인해야 합니다

접근 제어를 고려한 설계 방법은?

접근 제어에 필요한 데이터 이해하기 해야 합니다. 예약 확인을 할 때 컨슈머에 따라 접근 권한도 다르게 합니다.

유저 입장과 관리자 입장에 따라 접근 목표가 다릅니다.

예약 사용자 관리자
res_01 a1 c1
// user
GET /reservation
{
  id : a1
  role : user
  reservation :
      { id : 1, name : "김철수", plan : "코테풀기", date : "2022-10-14"
      }
  ]
}

//admin
GET /reservation
{
  id : c1
  role : admin
  reservation : [
      { id : 1, name : "김철수", plan : "코테풀기", date : "2022-10-14", status : "cancel",
        id : 2, name : "김영희", plan : "코테풀기", date : "2022-10-20", status : "success"
      }
  ]
}

두 경우의 예약 목록 목표의 표현은 동일합니다. GET /reservation 형태를 변형 없이 유지했습니다. 동작에 따라 최종 사용자에 의해서만 달라집니다. 그렇지만 {id} 같은 파라미터가 GET /reservation 리퀘스트에 별도로 붙지 않았음을 볼 수 있습니다. 그렇지만 사용자는 어떻게 식별할 수 있을까요? 단지 파라미터에 보이지 않을 뿐 숨겨져 있습니다.

이 안에 일반적으로 컨슈머의 ID와 허용된 스코프, 그리고 최종 사용자 ID와 가능하면 그들의 역할 또는 권한이 포함되어 있습니다.

GET /reservation/12345 리퀘스트를 보냈다면, API 서버는 액세스 토큰을 포함한 최종 사용자 ID를 확인하여 12345의 소유자인지 아닌지 확인해야 합니다.

이 내용을 확인하기 위해서는 보안 영역의 담당자와 이야기를 나눠야 합니다.

목표도 최종 사용자나 그들의 역할에 따라 형태도 다를 겁니다.

민감한 정보를 처리하기 위한 api 디자인하는 방법은?

API를 디자인할 때에는 API를 통해 요청, 응답 또는 수행할 수 있는 작업에 민감한 요소가 포함되어 있는지 항상 확인 후 민감한 요소가 정말 취급될 필요가 있는지 확인한 뒤 가장 안전한 방법으로 디자인해야 합니다.

민감한 데이터 항목을 식별하여, 일부를 제거하거나, 안전한 표현으로 변경하거나 알아볼 수 없도록 암호화를 해야 합니다.

민감한 정보 제거하기

코드숨 공부방은 회원 가입 시 아이디, 이름, 비밀번호 정보를 받습니다. 만약 회원 가입에 성공 시 아래와 같은 데이터를 반환한다고 가정해 봅시다.

{
  "message": "회원가입이 성공했습니다.",  
  "id": 1,
  "email": "tester@email.com",
  "password": "codesoom123!"
}

여기서 비밀번호는 민감한 정보에 해당하며, 필수 요소도 아니기 때문에 제거해버리면 됩니다. 민감한 데이터를 안전하게 표현하는 기법 네 가지 중 첫 번째를 알게 되었네요.

{
  "message": "회원가입이 성공했습니다."
}

민감하지 않은 형태로 순화하기

만약 민감한 정보를 제거할 수 없다면, 민감하지 않은 형태로 순화하는 방법이 있습니다. 예를 들어 사용자가 얼마나 구매했는지 노출하고 있는 정보가 있습니다.

{
  "purchaseAmount": "300,000원"
}

위 정보는 사실 사용자의 등급을 판단하기 위해 사용되는 정보인데, 의도치 않게 민감한 정보를 노출하고 있습니다. 이를 등급으로 변경하여 노출하면, 민감하지 않은 형태로 순화할 수 있습니다.

{
  "grade": "GOLD"
}

조합 시 민감한 정보들을 제거하기

사용자가 ‘좋아요’를 눌렀던 내역과 조회한 내역을 모두 노출하고 있다면, 이 두 정보를 ‘카테고리’라는 하나의 정보로 치환함으로써 민감한 정보를 제거할 수 있습니다.

{
  "favorites": [
    "밍순이네 강아지",
    "보통이네 강아지",
  ],
  "views": [
    "산책 100번 시키기",
    "집에 오자마자 목욕하자고 하기"
  ]
}
{
  "favoriteCategories": ["dog", "cat"]
}

값을 암호화하기

민감한 정보가 정말로 필요하고, 컨슈머와 프로바이더가 상호작용을 하는 채널의 암호화가 충분하지 않다면, 민감 정보 자체를 암호화하는 방법이 있습니다.

{
  "accountNumber": "123212-3325894"
}

위의 상황에서는 계좌번호를 조회 시 계좌번호를 그대로 노출시키고 있습니다.

계좌번호를 암호화하여 민감한 정보를 보호할 수 있습니다.

{
  "accountNumber": "MTIzMjEyLTMzMjU4OTQ="
}

그렇지만 이 기법에는 단점이 있습니다. 특히 컨슈머가 이 데이터를 사용하기 위해 복호화를 수행해야 합니다. 이렇게 되면 컨슈머와 프로바이더 사이의 안전한 연결을 확보해 모든 메시지의 전체 내용을 암호화하는 편이 더 단순하고 효율적입니다.

가장 안전한 선택지는 각 컨슈머를 위해 별도로 데이터를 암호화하고 데이터를 해독할 수 있는 올바른 키를 저마다 제공해 주는 것입니다.

민감한 목표 취급하기

민감한 목표에는 두 가지 유형이 있습니다.

  1. 민감한 데이터를 조작하고, 그 결과로 민감한 동작을 유발하는 것
  2. 설령 민감한 데이터를 취급하지 않는다고 하더라도 민감한 요소인것

민감한 데이터가 포함되어 있다면, 목표가 무엇이건 민감한 요소로 취급되어야 합니다. 그렇다면 가장 먼저 해야 하는 일은 ‘정말 필요한가?’를 질문하는 것입니다. 정말로 필요하지 않다면 포함해서는 안됩니다. 정말 필요하다면 컨슈머의 스코프나 최종 사용자의 권한에 따라 이 목표를 접근할 수 있도록 해주어야 합니다.

민감한 목표들을 취급한다는 것은 우리에게 이것들이 민감한 데이터를 조작하는지 또는 민감한 액션을 유발하는지 식별해 내는 것을 필요로 합니다.

안전한 에러 피드백 디자인하기

에러에는 두 가지 타입이 있습니다.

  1. 규격에 맞지 않는 형태의 리퀘스트에서 발생하는 에러
  2. 비즈니스 규칙을 위반하여 발생하는 에러

보안 관련 에러를 명확하게 나타내기 위해 새로운 에러 타입이 필요한데 이러한 에러에 대한 피드백은 동일한 종류의 표현을 사용할 수 있습니다. 바로 메시지와 코드입니다.

만약 본인이 소유한 예약이 아닌 예약 id로 예약 취소나 수정을 시도했을 때,

403 Forbidden

{
  "message": "본인 소유의 예약이 아닙니다",
}

위와 같은 응답을 준다면, 사용자는 본인 소유는 아니지만 다른 사람의 예약 id라는 것을 알게 될 것입니다. 따라서 보안을 위해 아래와 같이 응답함으로써 해당 id를 가진 예약이 존재하는지조차 모르게 할 수 있습니다.

404 Not Found

{
  "message": "예약을 찾을 수 없습니다."
}

아키텍처와 프로토콜 이슈 식별하기

API를 안전하게 디자인했다고 하더라고, 아키텍처와 프로토콜에 민감한 정보가 노출될 수 있습니다. 예를 들어 로드밸런서에서 민감한 정보를 로그로 기록하고 있다면, 이는 잠재적인 위험이 될 수 있습니다. 따라서 API에서 사용하는 프로토콜이나 아키텍처 측면에서 항상 데이터 유출이 발생할 수 있는 부분이 있을지 확인하기 바랍니다.

REST HTTP API를 디자인할 때, 경로 파라미터나 쿼리 파라미터에 로그로 남을 우려가 있는 민감 정보는 포함하지 않도록 신중해야 합니다. 또한, API에서 사용하는 프로토콜이나 아키텍처 측면에서 항상 데이터 유출이 발생할 수 있는 부분이 있을지 확인해야 합니다.

만약 잠재적으로 민감한 정보가 유출될 가능성이 있다면, 예방 차원에서 API 디자인을 변경해야 합니다.