
GraphQL, uygulama programlama arayüzleri (API’lar) için modern bir sorgu dilidir. Facebook ve GraphQL Vakfı tarafından desteklenen GraphQL hızla büyüdü ve Shopify, Github ve Amazon gibi büyük endüstri oyuncularının gemiye gelmesiyle teknoloji evlat edinme döngüsünün ilk çoğunluk aşamasına girdi.
Herhangi bir yeni teknolojinin yükselişinde olduğu gibi, GraphQL kullanmak, özellikle GraphQL’i ilk kez uygulayan geliştiriciler için büyüyen ağrılarla geldi. GraphQL, geleneksel dinlenme API’leri üzerinde daha fazla esneklik ve güç vaat ederken, GraphQL, erişim kontrolü güvenlik açıkları için saldırı yüzeyini potansiyel olarak artırabilir. Geliştiriciler GraphQL API’lerini uygularken bu sorunları aramalı ve üretimde güvenli varsayılanlara güvenmelidir. Aynı zamanda, güvenlik araştırmacıları güvenlik açıkları için GraphQL API’lerini test ederken bu zayıf noktalara dikkat etmelidir.
Bir REST API ile istemciler, HTTP istekleri tek tek uç noktalara yapar.
Örneğin:
GET /api/user/1
: Kullanıcı al1
POST /api/user
: Bir kullanıcı oluşturPUT /api/user/1
: Kullanıcıyı düzenle1
DELETE /api/user/1
: Kullanıcıyı sil1
GraphQL, standart REST API paradigmasının yerini alır. Bunun yerine, GraphQL, istemcilerin her ikisini de gönderdiği yalnızca bir uç nokta belirtir. sorgu veya mutasyon istek türleri. Bunlar sırasıyla okuma ve yazma işlemleri gerçekleştirir. Üçüncü bir istek türü, abonelikdaha sonra tanıtıldı, ancak çok daha az kullanıldı.
Arka uçta, geliştiriciler farklı kaynakları temsil etmek için nesne türlerini ve alanları içeren bir GraphQL şeması tanımlar.
Örneğin, bir kullanıcı şu şekilde tanımlanır:
type User {
id: ID!
name: String!
email: String!
height(unit: LengthUnit = METER): Float
friends: [User!]!
status: Status!
}enum LengthUnit {
METER
FOOT
}
enum Status {
FREE
PREMIUM
}
Bu basit örnek, GraphQL’in birkaç güçlü özelliğini göstermektedir. Diğer nesne türlerinin bir listesini destekler (friends
), değişkenler (unit
) ve artışlar (status
). Ayrıca, geliştiriciler yazıyor Çözücülerbir GraphQL isteği için arka ucun veritabanından nasıl sonuçlandığını tanımlar.
Bunu göstermek için, bir geliştiricinin şemada aşağıdaki sorguyu tanımladığını varsayalım:
{
“name”: “getUser”,
“description”: null,
“args”: [
{
“name”: “id”,
“description”: null,
“type”: {
“kind”: “SCALAR”,
“name”: “ID”,
“ofType”: null
},
“defaultValue”: null
}
],
“type”: {
“kind”: “OBJECT”,
“name”: “User”,
“ofType”: null
},
“isDeprecated”: false,
“deprecationReason”: null
}
İstemci tarafında, bir kullanıcı getUser
sorgu ve al name
Ve email
Aşağıdaki sonrası istek üzerinden alanlar:
POST /graphql
Host: example.com
Content-Type: application/json{“query”:”query getUser($id:ID!) { getUser(id:$id) { name email }}”,”variables”:{“id”:1},”operationName”:”getUser”}
Arka uçta, GraphQL katmanı isteği ayrıştırır ve eşleşen çözümleyiciye iletir:
Query: {
user(obj, args, context, info) {
return context.db.loadUserByID(args.id).then(
userData => new User(userData)
)
}
}
Burada, args
GraphQL sorgusundaki alana verilen bağımsız değişkenleri ifade eder. Bu durumda, args.id
ki 1
.
Son olarak, istenen veriler müşteriye iade edilecektir:
{
“data”: {
“user”: {
“name”: “John Doe”,
“email”: “[email protected]”
}
}
}
Fark etmiş olabilirsiniz User
Nesne türü ayrıca friends
diğer referanslar User
nesneler. Müşteriler bunu ilgili diğer alanları sorgulamak için kullanabilir User
nesneler.
POST /graphql
Host: example.com
Content-Type: application/json{“query”:”query getUser($id:ID!) { getUser(id:$id) { name email friends { email }}}”,”variables”:{“id”:1},”operationName”:”getUser”}
Böylece, geliştiriciler ayrı ayrı API uç noktalarını ve denetleyici işlevlerini manuel olarak tanımlamak yerine, arka uçları değiştirmek zorunda kalmadan istemci tarafında karmaşık sorgular oluşturmak için GraphQL esnekliğinden yararlanabilir. Bu, GraphQL’i AWS Lambda ile Apollo Server gibi sunucusuz uygulamalarla popüler hale getirir.
Tanıdık çizgiyi hatırlayın – büyük bir güçle büyük sorumluluk geliyor mu? GraphQL’in esnekliği güçlü bir avantaj olmakla birlikte, erişim kontrolü ve bilgi açıklama güvenlik açıklarından yararlanmak istismar edilebilir.
Basit düşünün User
Nesne türü ve sorgu. Makul bir şekilde bir user
sorgulayabilir email
onların friends
. Ama ya email
arkadaşlarının friends
? Yetkilendirme istemeden, bir saldırgan kolayca elde edebilir emails
Aşağıdakileri kullanarak ikinci derece ve üçüncü derece bağlantıların:
query Users($id: ID!) {
user(id: $id) {
name
friends {
friends {
email
friends {
email
}
}
}
}
}
Klasik dinlenme paradigmasında, geliştiriciler her bir denetleyici veya model kancası için erişim kontrolleri uygular. Potansiyel olarak kendinizi tekrarlamayın (kuru) prensibini ihlal ederken, bu geliştiricilere her çağrının erişim kontrolleri üzerinde daha fazla kontrol sağlar.
GraphQL, geliştiricilere GraphQL katmanı yerine iş mantığı katmanına yetkilendirmeyi önerir.
Bu nedenle, yetkilendirme mantığı GraphQL çözümleyicisinin altında yer alır. Örneğin, GraphQL’den gelen bu örnekte:
//Authorization logic lives inside postRepository
var postRepository = require(‘postRepository’);var postType = new GraphQLObjectType({
name: ‘Post’,
fields: {
body: {
type: GraphQLString,
resolve: (post, args, context, { rootValue }) => {
return postRepository.getBody(context.user, post);
}
}
}
});
postRepository.getBody
İş mantığı katmanındaki erişim denetimlerini doğrular.
Ancak, bu GraphQL spesifikasyonu tarafından uygulanmaz. GraphQL, geliştiricilerin yetkilendirme mantığını GraphQL katmanına yanlış yerleştirmesinin “cazip olabileceğini” kabul eder. Ne yazık ki, geliştiriciler bu tuzağa çok sık düşerek erişim kontrol katmanında delikler oluşturuyorlar.
Peki güvenlik araştırmacıları bir GraphQL API’sına nasıl yaklaşmalıdır? GraphQL, geliştiricilerin verilerini modellerken “grafiklerde düşünmelerini” önerir ve araştırmacıların aynısını yapmaları gerekir. Klasik dinlenme paradigmasında “İkinci Dereceli Güvensiz Doğrudan Nesne Referansları (Idors)” dediğim şeye paralellikler çekebiliriz.
Örneğin, bir REST API’sinde, aşağıdaki API çağrısı düzgün bir şekilde korunabilir:
GET /api/user/1
“İkinci dereceden” API çağrısı yeterince korunmayabilir:
GET /api/user/1/photo/6
Arka uç mantığı, kullanıcı numarası talep eden kullanıcının doğrulamış olabilir 1
o kullanıcının izinlerini okudu. Ancak fotoğraf numarasına da erişip erişemeyeceklerini kontrol edemedi 6
.
Aynı şey grafik çağrıları için de geçerlidir, ancak bir grafik şeması ile olası yolların sayısının katlanarak artması. Örneğin bir sosyal medya fotoğrafı çekin: Ya bir saldırgan bir fotoğrafı seven kullanıcıları sorgularsa ve sırayla erişirse onların Fotoğraflar?
query Users($id: ID!) {
user(id: $id) {
name
photos {
image
likes {
user {
photos {
image
}
}
}
}
}
}
Peki ya beğeniler onlar Fotoğraflar? Zincir devam ediyor. Kısacası, bir güvenlik araştırmacısı grafikte “döngüyü kapatmaya” ve hedef nesnelerine doğru yollar bulmaya çalışmalıdır. Gitlab’dan Dominic Couture, bunu onun hakkındaki yazısında kapsamlı bir şekilde açıklıyor. graphql-path-enum
alet.
GraphQL API’lerinin çoğu uygulamasında, GraphQL uç noktasını hızlı bir şekilde tanımlayabilmelisiniz, çünkü bunlar basitçe olma eğilimindedir. /graphql
veya /graph
. Bunları bu uç noktalarda yapılan isteklere göre de tanımlayabilirsiniz.
POST /graphql
Host: example.com
Content-Type: application/json{“query”: “query AllUsers { allUsers{ id } }”}
Sorgu ve mutasyon gibi anahtar kelimelere dikkat etmelisiniz. Ayrıca, bazı GraphQL uygulamaları kullanıyor GET
Şöyle görünen istekler: GET /graphql?query=…
.
Son noktayı tanımladıktan sonra, GraphQL şemasını çıkarmalısınız. Neyse ki, GraphQL spesifikasyonu tüm şemayı döndüren bu tür “içgözlem” sorgularını destekler. Bu, geliştiricilerin GraphQL sorgularını hızlı bir şekilde oluşturmasına ve hata ayıklamasına olanak tanır. Bu içgözlem sorguları, REST API’lerinde Swagger gibi API çağrı belgeleri araçları ile benzer bir işlevi yerine getirir.
İçgözlem sorgusunu bu özden uyarlayabiliriz:
query IntrospectionQuery {
__schema {
queryType {
name
}
mutationType {
name
}
subscriptionType {
name
}
types {
…FullType
}
directives {
name
description
args {
…InputValue
}
locations
}
}
}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
}
}
}
}
Tabii ki, çağrının yapıldığı yöntem için bunu kodlamanız gerekecek. Standardı eşleştirmek için POST /graphql
JSON Biçimi, Kullanın:
POST /graphql
Host: example.com
Content-Type: application/json{“query”: “query IntrospectionQuery {__schema {queryType { name },mutationType { name },subscriptionType { name },types {…FullType},directives {name,description,args {…InputValue},locations}}}\nfragment 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}}\nfragment InputValue on __InputValue {name,description,type { …TypeRef },defaultValue}\nfragment TypeRef on __Type {kind,name,ofType {kind,name,ofType {kind,name,ofType {kind,name}}}}”}
Umarım, bu tüm şemayı döndürür, böylece istediğiniz nesne türüne farklı yollar için avlanmaya başlayabilirsiniz. Apollo gibi birkaç GraphQL çerçevesi, içgözlem sorgularını ortaya çıkarmanın tehlikelerini kabul eder ve varsayılan olarak üretimde devre dışı bırakmıştır. Bu gibi durumlarda, olası nesne türlerini ve alanlarını sabırla kaba zorlama ve numaralandırarak ileriye doğru hissetmeniz gerekecektir. Apollo için sunucu yararlı bir şekilde döner Error: Unknown type “X”. Did you mean “Y”?
Gerçek değere yakın bir tür veya alan için.
Güvenlik araştırmacıları, orijinal şemayı mümkün olduğunca ortaya çıkarmalıdır. Tam şemaya sahipseniz, bir sorgudan bir hedef nesne türüne kadar farklı yolları numaralandırmak için Graphql-Path-enum gibi araçlardan çalıştırmaktan çekinmeyin. Tarafından verilen örnekte graphql-path-enum
bir şemadaki hedef nesne türü Skill
araştırmacı çalışmalıdır:
$ graphql-path-enum -i ./schema.json -t Skill
Found 27 ways to reach the “Skill” node from the “Query” node:
— Query (assignable_teams) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
— Query (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
— Query (checklist_check_response) -> ChecklistCheckResponse (checklist_check) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
— Query (checklist_checks) -> ChecklistCheck (checklist) -> Checklist (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
— Query (clusters) -> Cluster (weaknesses) -> Weakness (critical_reports) -> TeamMemberGroupConnection (edges) -> TeamMemberGroupEdge (node) -> TeamMemberGroup (team_members) -> TeamMember (team) -> Team (audit_log_items) -> AuditLogItem (source_user) -> User (pentester_profile) -> PentesterProfile (skills) -> Skill
…
Sonuçlar, şemadaki farklı yolları ulaşmak için döndürür Skill
iç içe sorgular ve bağlantılı nesne türleri aracılığıyla nesneler.
Güvenlik araştırmacıları, şemadan manuel olarak geçmelidir. graphql-path-enum
kaçırmış olabilir. Araç da çalışmak için bir grafik şeması gerektirdiğinden, tam şemayı çıkaramayan araştırmacıların da manuel incelemeye güvenmesi gerekecektir. Bunu yapmak için, saldırganın erişebileceği çeşitli nesne türlerini düşünün, bağlantılı nesne türlerini bulun ve korunan kaynağa bu bağlantıları izleyin. Ardından, bu sorguları erişim kontrolü sorunları için test edin.
Mutasyonlar için yaklaşım benzerdir. Doğrudan erişim kontrolü sorunları için testin ötesinde (erişiminiz olmaması gereken nesnelerdeki mutasyonlar), bağlantılı nesne türleri için mutasyonların dönüş değerlerini kontrol etmeniz gerekir.
GraphQL, grafik paradigmasından nesneleri sorgulayarak API’lere daha fazla esneklik ve derinlik katar. Bununla birlikte, erişim kontrolü güvenlik açıkları için bir her derde deva değildir. GraphQL API’leri, REST API’lerini etkileyen aynı yetkilendirme ve kimlik doğrulama sorunlarına eğilimlidir. Ayrıca, erişim kontrolleri hala geliştiricilere uygun iş mantığını veya model kancalarını tanımlamak için insan hataları potansiyelini artırmaya bağlıdır.
Geliştiriciler, erişim kontrollerini olumsuzluk (model) katmanına mümkün olduğunca yakın taşımalı ve şüpheniz olduğunda Apollo gibi aklı başında varsayılanlara sahip çerçevelere güvenmelidir. Özellikle Apollo, veri modellerinde yetkilendirme kontrolleri yapmayı önerir:
En başından beri, gerçek veri getirme ve dönüşüm mantığını, her biri uygulamanızdan bir kavramı temsil eden bir kavramı temsil eden Merkezi model nesnelerine taşımanızı öneririz: kullanıcı, yazı vb. Bu, çözümleyicilerinizi ince bir yönlendirme katmanı haline getirmenizi ve tüm iş mantığınızı tek bir yere koymanızı sağlar.
Örneğin, kullanıcı için model şöyle görünür:
export const generateUserModel = ({ user }) => ({
getAll: () => {
if(!user || !user.roles.includes(‘admin’)) return null;
return fetch(‘http://myurl.com/users');
},
…
});
Yetkilendirme mantığını farklı denetleyicilere yaymak yerine model katmanına taşıyarak, geliştiriciler tek bir “gerçeği” tanımlayabilir.
Uzun vadede, GraphQL daha fazla evlat edinmeye ve teknoloji benimseme döngüsünün geç çoğunluk aşamasına ulaştığından, daha fazla geliştirici GraphQL’i ilk kez uygulayacak. Geliştiriciler, GraphQL şemalarının saldırı yüzeyini dikkatle düşünmeli ve kullanıcı verilerini korumak için güvenli erişim kontrolleri uygulamalıdır.
Dominic Couture, Kenneth Tan, Medha Lim, Serene Chan ve Teck Chung Khor’a girdileri için özel teşekkürler.