GraphQL
Last updated
Was this helpful?
Last updated
Was this helpful?
GraphQL은 Rest API와 서로 다른 방식으로 동작하기 때문에 종종 비교 대상이 됩니다.
우선 GraphQL이 오늘날 많이 사용되는 이유와 공격에 대해 배우기 전, 동작 방식을 알아야 합니다.
Rest API 방식은 PUT,READ,GET 등의 HTTP 메소드와 여러 엔드포인트를 사용해야 하며,
여러 엔드포인트를 구성하지 않는다면 필요한 정보만을 추출하는 것은 어렵습니다.
다양한 엔드포인트를 구축하여 필요한 정보만을 질의하여 응답받거나,
단일한 엔드포인트를 구축하여 필요 이상의 정보를 질의하여 응답받을 수 있습니다.
이로 인해 개발 과정에서 여러 엔드포인트를 관리해야 한다는 관리적 측면의 문제와,
필요 이상의 데이터들을 반환한다는 점에서 Overfetching이 발생하여 비용적 문제가 발생합니다.
예를 들어 특정 인물에 대한 데이터를 반환을 요청할 때 Rest API는 다음과 같이 동작합니다.
이에 반면 GraphQL은 단일화된 엔드포인트와 단일화된 HTTP 메소드인 POST만 사용하고도
클라이언트가 원하는 특정 데이터만을 출력하는 것이 가능합니다.
결정적인 차이점은 특정 데이터를 출력하기 위해 Rest API는 다양한 엔드포인트를 구성해야 하는 반면,
GraphQL은 다양한 쿼리를 JSON 형태로 질의해야 한다는 것이 있습니다.
아래의 GraphQL을 통한 사용자 데이터 출력 예시를 보면, Rest API의 질의와는 다르게
쿼리에서 이미 대상을 특정하며, 그 대상의 모든 정보가 아닌 필요한 정보만 질의하는 것이 확인됩니다.
일반 사용자들을 하나씩 놓고 봤을 땐 데이터적인 측면에서 손실이 크다고 생각되지 않을 수 있으나,
하나의 테이블이 아닌 여러개의 테이블을 출력하는 상황과, 재귀적인 서브 필드가 존재하는 상황일 수록
장점이 더욱 명확하게 나타나기 때문에, 오늘날 새로 개발되는 서버들은 대부분 GraphQL을 사용합니다.
GraphQL은 SQL과 같은 데이터베이스와 다르게 질의 시 반드시 명시해야 하는 Non-Null 필드가 있습니다.
이러한 필드는 사용자가 다른 사용자의 특정 데이터를 알지 못하는 환경에서
모든 사용자의 데이터를 무작위로 출력하는 것을 방지해주기도 합니다.
이것이 GraphQL에서 스키마에 대해 질의할 수 있는 Introspection 기능이 있어도 안전한 이유입니다.
다음은 GraphQL의 모든 쿼리 스키마 구조를 파악하는 쿼리입니다.
위 사진은 실습 서버의 GraphQL 구조를 그래프화 한 결과입니다.
users
필드는 필수적으로 요구되는 서브필드가 없는 반면,
user
필드는 username이 서브필드가요구되는 것을 확인할 수 있습니다.
아래와 같이 필수적으로 실제 값의 요구가 없는 테이블의 경우 모든 데이터를 출력하는 것이 가능하지만
반대의 경우 오류 메시지를 통해 값이 요구된다는 것을 확인할 수 있습니다.
GraphQL은 엔드포인트에 요청 시 모든 스키마 구조를 반환해주는 명령어가 있습니다.
GraphQL은 객체 지향적 구조를 갖고 있는 언어이기 때문에 SQL의 컬럼과는 다른 개념을 가집니다.
예를 들어 SQL에서 컬럼에 대한 공유는 JOIN을 통한 외래키 참조가 있지만
GraphQL에서는 서브 필드들을 하나의 객체로 만들어서 여러개의 루트 필드가 공유하고 있습니다.
따라서 Injection 포인트가 발견되었을 시, 일반적인 SQLi 접근 방법처럼 메타데이터를 출력하고
해당 필드에 있는 서브필드가 무엇인지 탐색하고 추적할 필요가 없습니다.
단지 인트로스펙션 쿼리를 통한 전체 필드를 출력한 뒤, Voyager를 통해 구조를 파악할 수 있습니다.
GraphQL에서 데이터를 입력하는 쿼리를 뮤테이션이라고 합니다.
뮤테이션 쿼리를 사용하여 새로운 객체를 생성하거나, 업데이트 및 삭제 등이 가능합니다.
앞서 살펴본 인트로스펙션 쿼리는 전체 쿼리와 필드 스키마 정보를 출력했다면,
아래의 인트로스펙션 쿼리는 전체 뮤테이션에 관련된 스키마 정보를 출력하는 명령입니다.
위 사진은 서버의 뮤테이션 목록을 질의한 결과입니다.
현재 서버에선 registerUser 이라는 뮤테이션이 유일하게 있습니다.
데이터 삽입을 위해 쿼리 필드인 객체들과 동일하게, 뮤테이션 객체도 필드 목록을 확인해야 합니다.
또한 뮤테이션 쿼리를 실행하기 위해서는 어떤 루트 필드에 삽입해야 하는지도 명시해야 합니다.
만약 registerUser라는 뮤테이션의 입력값이 user 루트 필드가 아닌,
board 등의 다른 루트 필드로 명시한다면 오류가 출력됩니다.
현재 실습 서버의 registerUser 뮤테이션은 유저 정보를 그대로 삽입하는 것이 아니라
user 객체 필드를 사용하여 그 안에 데이터를 삽입하기 때문에 쿼리에서 user 객체 안에 데이터를 삽입해야 합니다.
뮤테이션에 데이터를 삽입할 때 객체가 요구되는데도 사용하지 않으면 오류 메시지를 통해서 객체의 이름을
오류 코드에서 힌트로 알려줍니다.
질의를 할 때는 뮤테이션 함수가 아닌 뮤테이션 객체 값을 질의해야 합니다.
뮤테이션 함수는 뮤테이션 시에 사용하는 registerUser 라는 그 자체의 개발자가 정의한 함수이고,
뮤테이션 객체는 함수 실행 후, 그 함수의 결과가 반환하는 객체 타입입니다.
registerUser 함수는 결국 user 루트 필드에 데이터를 삽입하게 되므로 RegisterUser 객체에는
루트 필드 user 를 가리키고 있습니다.
이제 뮤테이션 객체와 함수, 반환되는 루트 필드를 모두 파악한 후에는 데이터 삽입이 가능합니다.
이러한 명령을 Introspection이라고 부르며 에서 제공하고 있습니다.