GraphQLを使ったWebアプリを攻めの視点から覗いてみる

4.1K Views

September 23, 24

スライド概要

「OWASP Kansai DAY 2024.09 〜10周年記念セキュリティ・マシマシ全部盛り〜」で使用した資料です。

profile-image

セキュリティが好きです

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

GraphQLを使ったWebアプリを 攻めの視点から覗いてみる 2024/9/21 OWASP Kansai Adachi Tomohiro

2.

• 本資料は攻撃を推奨するものではなく、攻撃側の視点からできる ことを⾒ることで、防御に必要な知識を得ていただくことを⽬的 に作成しています。 • ⾃⾝の管理外の環境に対して本資料内の⼿法を実⾏した場合、法 律で罰せられる可能性がありますので、絶対に実⾏しないでくだ さい。 • 前述の⾏為等により発⽣する問題・責任について、著作者は⼀切 の責任を負いません。 2

3.

⾃⼰紹介 名前︓ Adachi Tomohiro(@att0att0) 所属︓ GMOサイバーセキュリティ by イエラエ株式会社 OWASP Kansaiボードメンバー 主にWebアプリの脆弱性診断をしています 3

4.

GraphQLとは • API⽤のクエリ⾔語 • 必要なデータを指 定して取得するこ とが可能なので、 効率的なデータ取 得ができる 参考・引⽤︓https://graphql.org/ 4

5.

GraphQLとは このハンズオンの中では以下を取り扱います。 (Subscriptionは出てきません) ■Query 主にデータ取得で⽤いる ■ Mutation 主にデータの作成・更新・削除で⽤いる 5

6.

クエリの⾒⽅ Queryなら「query」 Queryの場合は省略可 Mutationなら「mutation」 6

7.

クエリの⾒⽅ オペレーション名 実⾏するクエリの識別につけ る名前 リクエストで使⽤しない場合 は省略可 7

8.

クエリの⾒⽅ Query / Mutation型に属す るフィールド名 8

9.

クエリの⾒⽅ 「thread」というオブジェ クト型のフィールドに属する フィールド名 9

10.

クエリの⾒⽅ レスポンスでは、実⾏したクエリ で指定したフィールド(id,title) のデータが返される 10

11.

クエリの⾒⽅ 引数として、動的パラ メータを渡すことも可能 11

12.

クエリの⾒⽅ クエリ内にハードコードせず、 外部から「variables」という パラメータを介して渡すことも 可能 12

13.

クエリの⾒⽅ フィールドを追加することで、取得するデータが増える 13

14.

クエリの⾒⽅ 何でもフィールドが追加できるわけではない 14

15.

GraphQLとは 指定できるフィールドって何で分かる︖ 15

16.

Introspection イントロスペクションクエリを 実⾏することで、APIで定義さ れたスキーマ情報を取得可能 スキーマ情報から、実⾏可能な Query・Mutationや指定でき るフィールド・引数の情報が分 かる 16

17.

Introspectionの結果例 17

18.

Introspectionの結果例 18

19.

実際に触ってみる① Burp Suiteの「Open browser」より起動したブラウザから、 デモサイトにアクセス 19

20.

実際に触ってみる① Burpの「proxy」タブの 「HTTP history」を選択 表⽰されたログから、 TOPページにアクセスし た際のリクエスト(query thread)を選択 20

21.

実際に触ってみる① ⾚枠内で右クリック→ 「Send to Repeater」を クリック 21

22.

実際に触ってみる① 「Repeater」タブのリクエ スト部分で右クリック→ 「GraphQL」→ 「Set introspection query」 をクリック 22

23.

実際に触ってみる① イントロスペクションクエリ が反映されていることを確認 し、「Send」をクリックして リクエストを送信 23

24.

実際に触ってみる① レスポンスでスキーマの情報が返される 24

25.

実際に触ってみる① ] Query型に属するフィールド「thread」があり 「Thread」という名前のオブジェクト型のデータを返す 25

26.

実際に触ってみる① 「Thread」という名前のオブジェクト型のフィールドに属する、 26 「id」「title」フィールドがあることが確認できる

27.

とはいえレスポンスのJSONから 辿っていくのはちょとしんどい 27

28.

GraphQL Voyager SDL形式のスキーマ情報 や、イントロスペクショ ンの結果(JSON)を使⽤ し、スキーマ情報を視覚 化してくれるツールの1つ https://github.com/graphql-kit/graphql-voyager 28

29.

実際に触ってみる② 「CHANGE SCHEMA」 をクリック後、 「INTROSPECTION」 を選択 29

30.

実際に触ってみる② イントロスペクション 実⾏時のレスポンスの JSONをコピー 30

31.

実際に触ってみる② 「INTROSPECTION」 にペースト後、「DISP LAY」をクリック 31

32.

実際に触ってみる② Queryのスキーマ情報 32

33.

実際に触ってみる② Mutationのスキーマ情報 33

34.

スキーマの情報の⾒⽅ 例えばQueryであれば、 この2つがQuery型に属 するフィールドである ことが分かる フィールド名 返すデータの型 (!はnullを許容しない) 34

35.

スキーマの情報の⾒⽅ スキーマ情報からクエリを組むことができる 35

36.

Introspectionでスキーマ情報が取得できると 実⾏できるQueryやMutation等の情報が分かる 36

37.

GraphQLとは スキーマ情報なんてなくても アプリ操作すればそれで分かるのでは︖ 37

38.

TOP画⾯アクセス時 38

39.

投稿時 39

40.

ログイン時 40

41.

新規登録時 41

42.

Queryの場合 Queryの場合、「flag」は先ほどの画⾯操作で確認できていない 42

43.

Mutationの場合 Mutationでは、「loginotp」が先ほどの画⾯操作で確認できていない 43

44.

実際に触ってみる③ 「query flag」を実⾏するために 「support」フォルダ内の「query_flag.txt」の内容をコピー 44

45.

実際に触ってみる③ 「/graphql」へのリクエ ストのログを選択後、 ⾚枠内で右クリック→ 「Send to Repeater」を クリック 45

46.

実際に触ってみる③ Repeaterに送信したリクエストのリクエストボディを 先ほどコピーした内容に置換 46

47.

実際に触ってみる③ この時に実⾏されるクエリの 内容は、「GraphQL」タブ から確認できる 「Send」をクリックしてリ クエストをAPIへ直接送信 47

48.

実際に触ってみる③ 未認証での実⾏はエラーになる ことが確認できたので、次は認 証状態で実⾏してみる 48

49.

実際に触ってみる③ 以下の認証情報 or 「Signup」 からアカウント登録をしてログ イン email︓[email protected] PW :password 49

50.

実際に触ってみる③ 「HTTP history」のロ グから、レスポンスで 返されたアクセストー クンをコピー 50

51.

実際に触ってみる③ 「authorization: Bearer null」のnullの部分に、コピーしたトーク 51 ンをペースト「Send」でリクエスト送信

52.

実際に触ってみる③ レスポンスの内容から、 管理者で実⾏する必要が あることが分かった 52

53.

スキーマの情報を⾒ながら 何か⽅法がないか探してみる 53

54.

実際に触ってみる④ 表⽰をMutationに切り替 えた後、左のメニューの 「Mutation」をクリック 54

55.

実際に触ってみる④ Mutation時に渡せる引数の情報 が⾒える 55

56.

実際に触ってみる④ 各Mutationを選択すると、引数 の型情報も確認できる 56

57.

実際に触ってみる④ 「registerUser」を⾒ると気に なる部分がある 57

58.

実際に触ってみる④ 「adminflg」という、いかにも な引数が定義されている 58

59.

実際に触ってみる④ 画⾯操作時のMutationでは送信されていなかった引数 59

60.

実際に触ってみる④ 引数を追加した「mutation registerUser」を実⾏するために 「support」フォルダ内の「mutation_registerUser.txt」の内容をコピー 60

61.

実際に触ってみる④ 「/graphql」へのリクエ ストのログを選択後、 ⾚枠内で右クリック→ 「Send to Repeater」を クリック 61

62.

実際に触ってみる④ Repeaterに送信したリクエストのリクエストボディを 先ほどコピーした内容に置換 62

63.

実際に触ってみる④ この時に実⾏されるクエリの 内容は、「GraphQL」タブ から確認できる 「Send」をクリックしてリ クエストをAPIへ直接送信 63

64.

実際に触ってみる④ 先ほど登録したアカウント でログイン email︓[email protected] PW︓1qazxsw2 64

65.

実際に触ってみる④ 通常のアカウントのログイ ンでは表⽰されなかった、 2段階認証の画⾯が表⽰さ れた 65

66.

実際に触ってみる④ 正しいコード以外はエラー になる 66

67.

コードの総当たり コードを「0001」〜「9999」まで総当たりしてみる 67

68.

コードの総当たり 短い間隔で多数のリクエストを送信するとレート制限がかかる 68 かつ、コードの有効期限内(3分)の制限がある

69.

コードの総当たり リクエストを⼤量に送信する⽅法では難しい 69

70.

GraphQLとは 今回のデモアプリの場合、 限られた時間内に 限られたリクエスト数で総当たりができること が条件になる 70

71.

GraphQLのエイリアス 引数の値が異なる同名フィールドを追加して、1度のリクエスト で複数の処理を実⾏してみる 71

72.

GraphQLのエイリアス フィールド名「loginopt」で返すデータが「0001」と「0002」 の時の異なる結果があるので、コンフリクトしている 72

73.

GraphQLのエイリアス エイリアスを使⽤し、フィールドに別名をつけることで、コンフ リクトを回避することができる 73

74.

GraphQLのエイリアス エイリアスで増やした分だけ、引数の値が異なる同じ処理を、1 つのリクエストで複数実⾏することが可能になる 74

75.

実際に触ってみる⑤ エイリアスを使⽤した「mutation loginotp」を実⾏するために 「support」フォルダ内の「mutation_loginotp.txt」の内容をコピー 75

76.

実際に触ってみる⑤ email︓[email protected] PW ︓1qazxsw2 3分間の制限があるので、再度作成したadmin権限のアカウント でログインして、任意の認証コードを送信 76

77.

実際に触ってみる⑤ 「mutation loginotp」の ログを選択後、 ⾚枠内で右クリック→ 「Send to Repeater」を クリック 77

78.

実際に触ってみる⑤ 「GraphQL」タブのmutationの内容を、先ほどコピーした内容 に置換後、「Send」でリクエストを送信 78

79.

実際に触ってみる⑤ レスポンスで返されるデータの中に、アクセストークンが含まれ 79 ていることを確認

80.

実際に触ってみる⑤ 最初に実⾏した「query flag」のリクエストの、アクセストーク ン部分を先ほど取得した値に置換後「Send」でリクエスト送信 80

81.

実際に触ってみる⑤ 管理者権限で実⾏したこ とで、データの取得がで きたことを確認 81

82.

デモアプリでできたこと 通常の画⾯操作では登録できない、管理者権限のユーザを作成す ることができた 2段階認証の仕組みを回避することができた 通常の画⾯操作では実⾏されず、かつ管理者権限のユーザのみが 実⾏可能なqueryを実⾏し、データ取得ができた 82

83.

GraphQLとは 何がダメだったかを考えてみる 83

84.

きっかけ Introspectionが有効化されていて、利⽤者がスキーマ情報を取得できた 84

85.

その結果できたこと 通常の画⾯操作では実⾏されないqueryの存在が確認できた 85

86.

その結果できたこと 通常の画⾯操作では送信されない引数の存在が確認できた 86

87.

他にも実は 「query thread」では「secretmemo」や「author」フィールドから 「email」「admin」フィールドを指定することができた 87

88.

他にも実は TOP画⾯アクセス時に実⾏されるqueryの内容と、取得したデータ 88

89.

他にも実は ※Joeアカウント・・・ID・PWが同じアカウント フィールドを追加することで、通常の画⾯操作では取得できない 情報が取得できた 89

90.

GraphQLとは Introspectionを無効化しておく 90

91.

Field suggestion 誤ったフィールド名を指定した際に、親切にフィールド名のサ ジェストをしてくれる場合は、そこから存在するフィールド名 の特定につながる可能性がある 91

92.

GraphQLとは Field suggestionも無効化しておく 92

93.

GraphQLとは 攻撃に繋げられるような情報を 簡単に与えない 93

94.

GraphQLとは スキーマで定義された情報が隠されていても 定義されたquery・mutationの実⾏や フィールド・引数の追加が 禁⽌されるわけではない 94

95.

GraphQLとは スキーマで定義したquery・mutation フィールドや引数等の情報は 全てアクセス・利⽤される 可能性があることを前提に考える 95

96.

GraphQLとは フィールド追加によって、 ⾒えると困る情報の取得が可能ではないか︖ 96

97.

デモアプリでの例 NGなら認可制御を⾏う・不要なら実装からフィールドを取り除くなど 97

98.

GraphQLとは 引数の追加によって、 本来はアプリ操作ではできると困るような 処理が⾏えてしまわないか︖ 98

99.

デモアプリでの例 不要なら実装から引数を取り除く 99

100.

DoSに繋がるクエリ ⼤量のエイリアス フィールドの循環参照 100

101.

⼀例として・・・ GraphQLとは リクエストの中で実⾏するエイリアス数を制限 101

102.

⼀例として・・・ GraphQLとは 循環参照を⾏わないスキーマ設計を⾏う 難しい場合はクエリのネストの深さを制限する 102

103.

GraphQLとは クエリのコスト計算による対策 103

104.

GraphQLとは あらかじめ決まったクエリのみ実⾏を許す 「Persisted Query」というものも存在する 104

105.

まとめ • 攻撃に繋げられるような情報を簡単に与えない • Introspectionを無効化する • Field suggestionを無効化する • アプリ操作で実⾏されるクエリ以外にも、スキーマで定義され ているフィールドや引数等を追加することで、通常のアプリ操 作では取得しない情報の取得や、処理の実⾏につながる可能性 がある • スキーマで定義したフィールドや引数等の情報は、全てアクセスされ る可能性があることを前提に、対象のデータが返されても問題ないか、 困るような処理が⾏われないかを確認する • NGなら認可制御を⾏う、不要なら実装からそのフィールド・引数を取 り除くなど 105

106.

まとめ • 攻撃に繋げられるような情報を簡単に与えない • Introspectionを無効化する • Field suggestionを無効化する 106

107.

まとめ • アプリ操作で実⾏されるクエリ以外にも、スキーマで定義され ているフィールドや引数等を追加することで、通常のアプリ操 作では取得しない情報の取得や、処理の実⾏につながる可能性 がある • スキーマで定義したフィールドや引数等の情報は、全てアクセスされ る可能性があることを前提に、対象のデータが返されても問題ないか、 困るような処理が⾏われないかを確認する • NGなら認可制御を⾏う、不要なら実装からそのフィールド・引数を取 り除くなど 107

108.

ああ ご参加 ご清聴ありがとうございました︕ 108