graphql 보안 강화 (Schema Dumping 방지)

비고
개발 파트 전체 세션 진행
Tags
security
Select
recommand
notion image

graphi-ql

graphql을 사용한 개발 시, 아래 그림과 같이 GraphiQL과 같은 사이트를 활용하곤 한다.
활용 이유는 쿼리, 뮤테이션 API 종류를 살펴보는 것 뿐만 아니라, 호출 시 필요한 인자리턴으로 받을 수 있는 값들에 대한 정보를 제공하기 위함이다.
프론트엔드 개발자는 해당 정보들을 토대로 필요한 값들만 선택하여 API를 구성하게 된다.
notion image
 
GraphiQL과 같은 사이트는 개발 시점에 개발편의성을 위해 필요한 것으로, Production으로 배포 시에는 보안 목적으로 사이트를 제공하지 않도록 닫아야 한다.
notion image
GraphiQL을 Production에서 닫아야 하는 이유는 뭘까?
  • 공개 API가 아니고서야, 외부 개발자에게 관련 정보들을 제공할 이유가 없다.
  • 악의를 가진 사용자에겐 최대한 아무 정보도 제공하지 않아야 한다.
 

Introspection

graphiQL 접근을 차단하는 것으로 대응은 끝난 것일까?
결론부터 말하면 Production으로 나갈 경우 Introspectiondisable 해야한다.
 
Introspection 설정이 켜져있다면, 그래서 __Schema, __Type, __Field 등을 포함해서 요청을 보낼 수 있다면 graphiQL을 닫지 않은 것과 동일하다.
notion image

Schema Dumping

실제로 schema dumping 이라는 단어를 사용하는 지는 모르겠다.
다만 사용 가능한 모든 스키마들을 수집하는 작업을 말하고 싶어서 Schema Dumping 이라고 부르겠다.
 
Introspection이 활성화 되어 있는 상태라면 아래와 같은 쿼리를 통해 Schema Dumping이 가능하다.
query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } } }
Schema Dumping 예시 쿼리문
Schema Dumping 결과
Schema Dumping 결과
실제 Schema Dumping을 돌린 결과 10498줄의 방대한 양의 스키마 정보들이 수집되었다.
어떤 쿼리,뮤테이션이 있는지, 파라미터는 어떤 것들이 있고 타입은 어떻게 되는지, 리턴으로 받을 수 있는 값들의 종류와 각각의 타입은 어떻게 되는지에 대해 열려있는 모든 스키마 정보를 수집할 수 있었다.
 

InsomniaSchema Documentation

notion image
Insomnia의 graphql 탭에선 아예 graphiQL과 동일한 뷰와 정보를 조회할 수 있도록 Schema Documentation 기능을 제공한다.

스키마 관계도

위 사이트에 Introspection 결과를 붙여넣기 하면 관계도를 확인할 수 있다.
notion image
notion image
위 도식도는 어떤 정보들을 조회할 수 있는지, 또 내가 원하는 정보를 조회하기 위해 어떻게 쿼리를 구성시켜 나가야 하는 지에 대해 파악하기 좋은 자료가 된다.
 

스키마 정보 노출이 주는 의미

백엔드는 블랙박스 영역

백엔드는 클라이언트 사이드에서 뜯어볼 수 없다.
따라서 백엔드는 블랙박스 영역에 있으며, 정보 수집을 위해 반복 행위를 수행할 수밖에 없다.
사소한 테이블명, 칼럼명 하나를 파악하기 위해서도 수많은 쿼리 요청과 반복 작업이 동반되어야 한다.
 

스키마 정보를 열어둔다면?

  • 공격자에게 이러한 수고와 노력을 덜어주는 것과 같다.
  • 이상행위의 반복을 모니터링하며 정보수집 시도에 대응할 수도 있는데, 이런 기회도 놓칠 수 있다.
 

원데이 취약점을 찾는 원리

스키마 정보는 한번 누출되고 나면 이후에 막는다고 하더라도, 공격자는 이미 이전에 누출된 데이터를 갖고 있을 것이며 여기서 크게 달라지지 않으므로 한번 누출된 것 자체로 불쾌하다.
원데이 취약점을 찾는 원리를 이해하면 이 불쾌감을 이해하는 데 도움이 된다.
 
  • 제로데이 취약점 : 아무도 발견하지 못한 곳에서 새로운 취약점을 찾은 것
  • 원데이 취약점 : 제로데이 취약점에 대한 패치가 나온 부분을 분석해서, 또 다른 취약점을 도출해 내는 것.
      1. 패치가 적용되기 전의 정보와, 패치가 적용된 후의 바이너리를 디핑 뜬다.
      1. 이후 패치된 로직 내에서 취약점이 발생할 수 있는 부분들을 찾아서 분석하고,
      1. 기존 취약점에서 약간 공격방식이 변화된 또 다른 취약점을 찾아낸다.
물론 백엔드는 블랙박스이므로 로직을 디핑 떠볼 순 없지만, 스키마가 변경되었다면 변경 전, 변경 후의 스키마 형태를 보면서 어떤 식으로 공격할지 시나리오를 써볼 수 있는 정도로는 활용할 수 있다.
 

Graphql Injection

Graphql 환경에서 Injection을 시도하는 것을 뜻한다.
수집된 스키마 정보를 바탕으로 민감정보 탈취, SQL Injection, XXE 등의 취약점을 유발시키기 위한 쿼리, 뮤테이션을 쏘는 등의 공격을 시도한다.

Fuzzing

위와 같은 공격 시도를 자동화 하여 취약점을 찾아나가는 방식을 뜻한다.
💡
오펜시브 리서치 트렌드임. Fuzzer에 대한 연구가 활발히 이루어지고 있다.
  • 공개 퍼저 툴 예시 : WinAFL, AFL, Bff, Driller, manul + dharma 등등
  • 퍼징 방식
    • Dumb Fuzzing : 무작위, 무논리로 이상 데이터 만들어서 반복 테스트
    • Smart Fuzzing : 정보를 기반으로 유효한 형식에서 조금씩 틀어가며 공격하는 방식
    • Coverage Guided Fuzzing : 모든 커버리지를 채우도록 조건문을 뚫어가며 공격하는 방식. (비교적 기능이 잘 쓰이지 않아서 버그가 많이 제보되지 않은 부분에서 버그를 잘 터트린다. / 따라서 가성비가 좋다)
notion image
📖
pentesting : 모의해킹
Graphql Injection 검색하면 GraphQLmap이 검색되는데, 가장 위에 Dump a GraphQl schema가 있는 것을 볼 수 있다. 공격 시도 전에 선행되어야 하는 작업이기 때문이다.
 

대응방안

핵심 대응방안은 간단한다.
  • Production에선 Introspection을 disable 시킨다. (또는 role-based로 관리한다)
    • 공격 시도 자체를 방어할 순 없지만, 최대한 필요한 정보들을 불편하게 얻도록 유도해야한다.
    • 정보 수집을 위해 반복적인 이상 행위 쿼리 요청을 모니터링 하고 블락 시키는 등 방어체계를 구축하여 공격에 대응할 수 있다.
  • field fuzzing을 어렵게 만들기
    • 에러 메세지를 통해 내부 정보를 획득할 수 없도록 해야한다.
    • 겉으로 드러나는게 없다면 직접 정보를 찾아 나서야 하는데, 에러 메세지에서 주요정보를 제공할 경우 공격자의 비용을 훨씬 절감시킨다.
    • 아래 그림 예시는 공격자의 field fuzzing 도중 서버 에러메세지가 field 명을 알려주는 예시다.
    • notion image
    • 이건 suggestiondisable 처리하여 해결할 수 있다.
 

What’s Next?

  • Production 외의 다른 개발 서버(Staging, Development 등)의 서버 정보가 노출되지 않도록 프로덕트 코드를 철저하게 분리한다.
    • 기껏 Production에서 막아뒀는데, Staging이나 Development의 graphql url을 통해 schema dumping을 시도할 수 있기 때문이다.
    • ⚠️
      특히 프론트엔드에서 dev, stg, prd 관련 정보를 한 프로덕트에 포함시켜서 빌드 배포하면 안된다. - 클라이언트 사이드 프로덕트이기 때문에 모든 것을 뜯어볼 수 있다.
  • Staging, Development 는 내부망에서만 접근할 수 있도록 망분리를 하는 것도 도움이 된다.
  • 이상 트래픽 관련 모니터링/관제 시스템을 구축한다.
    • 공격자가 획득 가능한 정보가 한정적이라면 정보획득/공격 목적의 fuzzing을 택할 수밖에 없고, 이는 이상 트래픽을 만들 수밖에 없다.
    • 이상 트래픽 요청 @회 이상 요청 시 ip 차단 등의 정책으로 풀어낼 수 있다.