Halo, Habr! Saya sajikan kepada Anda terjemahan artikel Melindungi API GraphQL Anda Dari Kerentanan Keamanan .
GraphQL dengan cepat menjadi pilihan pengembang yang perlu membuat API untuk aplikasi klien mereka. Tetapi, seperti semua teknologi baru, GraphQL tunduk pada beberapa risiko keamanan yang melekat. Terlepas dari apakah Anda sedang membangun proyek pihak ketiga atau aplikasi perusahaan skala besar, Anda perlu memastikan bahwa Anda melindungi diri dari ancaman ini.

Meskipun ancaman yang tercantum dalam posting ini khusus untuk GraphQL, implementasi Anda akan memperkenalkan serangkaian ancaman baru yang perlu ditangani. Penting juga bagi Anda untuk memahami ancaman terhadap aplikasi apa pun yang berjalan di jaringan.
Ancaman: permintaan besar, bersarang sangat, yang mahal untuk dihitung
Solusi : batasan kedalaman bersarang
Kekuatan yang disediakan oleh GraphQL dikaitkan dengan beberapa ancaman keamanan baru. Yang paling umum adalah query yang sangat bersarang, yang mengarah pada perhitungan yang mahal dan JSON yang besar, yang dapat mempengaruhi jaringan dan bandwidthnya.
Cara yang tepat untuk melindungi API Anda dari serangan semacam ini adalah membatasi kedalaman permintaan sehingga permintaan dalam yang berbahaya diblokir hingga hasilnya dihitung.
Batas Kedalaman GraphQL menyediakan antarmuka sederhana untuk membatasi kedalaman kueri.
import depthLimit from 'graphql-depth-limit' import express from 'express' import graphqlHTTP from 'express-graphql' import schema from './schema' const app = express() app.use('/graphql', graphqlHTTP((req, res) => ({ schema, validationRules: [ depthLimit(10) ] })))
Ancaman: permintaan mutasi yang rentan rentan
Solusi : batasi jumlah permintaan
Mencari login dan kata sandi adalah trik tertua dalam sejarah peretasan. Dalam dekade terakhir, begitu banyak kebocoran data terjadi di Internet sehingga database dari 772.904.991 email unik dan 21.222.975 kata sandi unik baru-baru ini ditemukan. Untuk memeriksa apakah informasi tentang email dan kata sandi Anda bocor, Troy Hunt bahkan membuat situs web Have I been Pwned , di antaranya, ia menggunakan database ini.
Untungnya, Anda memiliki cara mudah untuk membuat penghancuran benar-benar sulit dan mahal bagi penyerang, yang akan membuat Anda menjadi sasaran yang kurang menarik bagi mereka.
Plugin GraphQL Rate Limit memungkinkan Anda menentukan batasan untuk kueri Anda dengan tiga cara berbeda: menggunakan arahan graphql-shield kustom atau menggunakan fungsi untuk membatasi jumlah dasar kueri.
Plugin ini akan memungkinkan Anda untuk mengatur jendela waktu dan batas jumlah permintaan untuk itu. Menetapkan jendela waktu besar untuk permintaan yang sangat rentan, seperti masuk, dan jendela waktu kecil untuk permintaan yang kurang rentan akan membantu Anda mempertahankan pengalaman yang menyenangkan bagi pengguna biasa dan akan menjadi mimpi buruk bagi penyerang.
Buat arahan untuk membatasi jumlah permintaan:
Di sini Anda akan memerlukan pengidentifikasi unik untuk setiap permintaan. Anda dapat menggunakan alamat IP pengguna atau pengidentifikasi lainnya, yang unik untuk setiap pengguna dan sesuai dengan setiap permintaan.
const rateLimitDirective = createRateLimitDirective({ identifyContext: (context) => { return context.id }, })
Tambahkan arahan ke skema Anda:
import { createRateLimitDirective } from 'graphql-rate-limit' export const schema = { typeDefs, resolvers, schemaDirectives: { rateLimit: rateLimitDirective, }, } export default schema
Terakhir, tambahkan arahan ke permintaan rentan Anda:
# 60 Login(input: LoginInput!): User @rateLimit( window: "60s" max: 10 message: "You are doing that too often. Please wait 60 seconds before trying again." )
Ancaman: memungkinkan pengguna untuk memengaruhi data khusus pengguna
Solusi : ambil data ini dari sesi pengguna jika memungkinkan
Sangat mudah untuk berasumsi bahwa jika Anda ingin mengizinkan pengguna untuk memperbarui sumber daya, maka ada baiknya membiarkan pengguna memutuskan sumber daya mana yang ingin ia perbarui. Tetapi bagaimana jika pengguna mendapat pengenal sumber daya yang mereka seharusnya tidak memiliki akses?
Misalkan kita memiliki permintaan mutasi updateUser yang memungkinkan pengguna untuk memperbarui profil mereka.
mutation UpdateUser($input: {"id": "test123" , "email": "test@example.com"}) { UpdateUser(input: $input) { id firstName lastName } }
Jika tidak ada perlindungan di sisi server, maka penyerang, yang memiliki daftar pengidentifikasi, berpotensi memperbarui alamat email untuk setiap pengguna. Solusi yang jelas di sini adalah menambahkan tanda centang untuk memastikan ID dari pengguna saat ini cocok dengan ID di bidang input.
Jangan lakukan ini:
function updateUser({ id, email }) { return User.findOneAndUpdate({ _id: id }, { email }) .catch(error => { throw error; }); }
Cara yang kurang jelas tetapi benar untuk menyelesaikan masalah ini adalah untuk mencegah pengidentifikasi digunakan sebagai input dan untuk menggunakan pengidentifikasi pengguna dari objek konteks.
Lakukan ini:
function updateUser({ email }, context) { return User.findOneAndUpdate({ _id: context.user._id }, { email }) .catch(error => { throw error; }); }
Mungkin ini adalah contoh yang agak sepele, tetapi melakukan tindakan seperti itu untuk setiap objek yang berinteraksi secara langsung dengan pengguna dapat melindungi Anda dari banyak kesalahan berisiko.
Pemenuhan beberapa permintaan mahal secara bersamaan
Solusi : batas biaya permintaan
Dengan menetapkan harga untuk setiap permintaan dan menunjukkan harga maksimum per permintaan, kami dapat melindungi diri dari penyusup yang mungkin mencoba memenuhi terlalu banyak permintaan mahal pada saat bersamaan.
Plugin GraphQL Cost Analysis adalah cara mudah untuk menentukan biaya untuk kueri dan batas biaya maksimum.
Tentukan biaya maksimum:
app.use( '/graphql', graphqlExpress(req => { return { schema, rootValue: null, validationRules: [ costAnalysis({ variables: req.body.variables, maximumCost: 1000, }), ], } }) )
Tentukan biaya untuk setiap permintaan:
Query: { Article: { multipliers: ['limit'], useMultipliers: true, complexity: 3, }, }
Ancaman: mengungkapkan detail implementasi GraphQL
Solusi : nonaktifkan introspeksi dalam kode "pertarungan"
GraphQL adalah alat pengembangan yang sangat berguna. Ini sangat kuat sehingga bahkan mendokumentasikan skema Anda, permintaan dan langganan untuk Anda. Informasi ini dapat menjadi tambang emas bagi penyerang yang ingin menemukan kerentanan dalam aplikasi Anda.
Plugin GraphQL Display Introspection akan mencegah skema Anda bocor ke publik. Impor saja plugin dan terapkan pada aturan validasi Anda.
import express from 'express'; import bodyParser from 'body-parser'; import { graphqlExpress } from 'graphql-server-express'; + import NoIntrospection from 'graphql-disable-introspection'; const myGraphQLSchema =