GraphQL Security

Introduction

GraphQL은 웹에서 브라우저가 서버로 데이터를 효율적이게 가져는 것을 목적으로 하는 Query Lanauge로 기존 REST API의 문제를 해결하고자 등장한 기술입니다. REST API가 서버에서 정의한 스펙에 따라 호출해서 데이터를 얻는 형태라면 GraphQL은 클라이언트가 정의한 포맷으로 가져오고 싶은 데이터를 가져오는 기술입니다.

REST API, SQL을 통한 서비스들이 백엔드에서 쿼리하고 처리한다면, GraphQL 서비스는 프론트에서 쿼리하고 처리하는 비중이 높습니다. 그래서 GraphQL의 HTTP 요청은 구조를 정의하는 Schema와 실제 쿼리인 Query와 Mutation 부분으로 나뉘어집니다. 그리고 이를 GQL이라고 부릅니다.

Schema

type  Query {
   polygon (sides: Int, regular: Boolean): Polygon
}

type  Polygon {
   perimeter: Float
   area: Float
}

Query & Mutation

query {
  polygon(sides: 1, regular: true) {
	perimeter
	area
  }
}

mutation {
  createPerson(input: { ... }) {
    person {
      id
      name
    }
  }
  updatePerson(input: { ... }) {
    person {
      id
      name
    }
  }
}

Offensive techniques

How to Testing

GraphQL을 사용하는 서비스는 보통 자주 사용되는 Form, JSON 기반이 아닌 별도의 Body를 가지고 있습니다. 그리고 보통 /graphql 등의 경로를 많이 사용하며 해당 경로가 아니더라도 query를 포함한 경우 GrphQL 서비스로 확인합니다.

Testing point

위에서 설명했듯이 GraphQL에서의 백엔드는 전달받은 GQL에 따라 데이터를 처리만 합니다. 쿼리를 구성하는 부분은 프론트에서 담당하게 됩니다. 그 소리는 결국 기존 백엔드에 감춰진 로직들이 프론트엔드로 많이 넘어온다는 의미이고, 곧 우리가 테스팅할 포인트가 늘어났다는 의미가 되기도 합니다.

ZAP

ZAP은 GQL을 지원합니다. Request에서 Schema와 Query 정보를 확인할 수 있고, Site tree에서도 GQL 내용에 따라 일부 표기가 됩니다. 또한 GQL의 각각 필드들은 모두 Input Vectors에 포함되기 때문에 ActiveScan 시 GQL도 테스팅 벡터가 추가되어 스캔됩니다.

GraphQL 처리에 대한 설정은 Options > GraphQL에 존재합니다.

그리고 상단 Import > Import a GraphQL ~~~ 메뉴를 통해 File이나 URL 기반으로 GraphQL Scheme을 로드할 수도 있습니다.

BurpSuite

BurpSuite 또한 GQL을 지원합니다. BurpSuite는 Request 탭에서 별도로 GraphQL 탭을 지원하며, Schema와 Query를 나눠서 볼 수 있습니다.

다만 GQL 지원을 위해선 GraphQL Raider란 확장 기능 설치가 필요합니다.

Insomnia

Insomnia는 API 테스팅 도구입니다. 일반적으로 개발자가 많이 사용하며, Postman과 같이 API 스펙을 정의하고 맞춰서 테스트할 수 있는 도구입니다. 여기서도 GraphQL을 지원하기 때문에 GQL 정보를 로드하여 실시간으로 테스트할 수 있습니다.

개인적으로도 개발/보안테스팅 시 정말 잘 사용하고 있고 좋아하는 도구에요. Insomnia가 궁금하시다면 “Insomnia로 REST API를 쉽게 테스트하자 😎” 글을 읽어주세요!

Attacks

GraphQL Injection

Cullinan > GraphQL Injection“을 참고해주세요.

IDOR

API, REST API 서비스와 동일하게 GraphQL 또한 IDOR의 영향을 많이 받습니다.

Original

query {
  getMyInfo(userid: "31114") {
	key
	secret
  }
}

Attack

query {
  getMyInfo(userid: "0") { # 0 is admin user id
	key
	secret
  }
}

IDOR에 대한 자세한 내용은 Cullinan > IDOR 항목을 참고해주세요!

Improper Access Control

GrphQL은 GraphQL을 처리하는 서버가 여러 API를 핸들링하고 있습니다. 마치 API G/W와 유사한 형태인데요, 이러한 경우 각 API의 권한 범위나 인증 등을 놓치기 쉽습니다. GraphQL에 연동되는 서비스는 GQL에 따라 충분히 API를 수행할 수 있고 외부로 스펙을 직접적으로 노출시키지 않아도 GQL을 통해 구조를 파악하고 호출될 수 있습니다.

query {
  getAdminData() {
	key
	secret
  }
}

Race condition

API 계통의 서비스들이 코드를 작성한 형태에 따라서 Race condition의 영향을 받는 것 처럼 GraphQL 또한 단시간에 많은 수의 요청이 왔을 때 백엔드의 처리 로직에 따라 Race condition의 영향을 받을 수 있습니다. 또한 Query를 공격자가 자유롭게 던질 수 있기 때문에 쿼리를 잘 구성하여 Race condition을 성공할 확률을 높일 수도 있습니다.

Etc

이외에도 여러 Injection 이나 웹 취약점 등이 모두 유효할 수 있습니다. 전반적으로 Schema나 Query 대해서 테스트가 필요합니다.

Defensive techniques

Basic

기본적으로 위에 이야기한 공격 방법에 대한 대응들은 모두 이루어져야 합니다. GraphQL이 연결되는 API 들은 모두 특수문자 검증 등으로 Injection 공격을 예방하고 불필요한 정보는 Client로 넘겨주지 않도록 에러 처리 등이 잘 되어 있어야 합니다.

Access Control

GraphQL의 편의성으로 잃는 것 중 하나가 바로 인증/인가라고 생각합니다. 각각 개별 기능들이 하나의 GQL로 묶이기 때문에 신경써서 권한을 잘 분리하고 권한에 맞는 요청인지 구별하는 로직들이 필요합니다.

Large Query 대응

GQL은 클라이언트에서 요청하기 때문에 때때로 악의적인 쿼리가 전송될 수 있습니다. 이에 대해서 Timeout이나 Maximum Query Depth 등으로 큰 요청에 대해 제한하는 것이 좋습니다. 자세한 내용은 아래 글을 참고해주세요.

https://www.howtographql.com/advanced/4-security/

Tools

Articles

References