GraphQL היא אלטרנטיבה פופולרית לארכיטקטורת RESTful API המסורתית, המציעה שפת שאילתת נתונים ושפת מניפולציה גמישה ויעילה עבור ממשקי API. עם שלה אימוץ הולך וגובר, הופך חשוב יותר ויותר לתעדף את האבטחה של ממשקי API של GraphQL כדי להגן על יישומים מפני גישה לא מורשית ונתונים פוטנציאליים הפרות.
גישה יעילה אחת לאבטחת ממשקי API של GraphQL היא יישום JSON Web Tokens (JWTs). JWTs מספקים שיטה מאובטחת ויעילה להענקת גישה למשאבים מוגנים ולביצוע פעולות מורשות, תוך הבטחת תקשורת מאובטחת בין לקוחות וממשקי API.
אימות והרשאה בממשקי API של GraphQL
בניגוד ממשקי API של REST, לממשקי API של GraphQL יש בדרך כלל נקודת קצה אחת המאפשרת ללקוחות לבקש באופן דינמי כמויות שונות של נתונים בשאילתות שלהם. אמנם גמישות זו היא החוזק שלה, אך היא גם מגבירה את הסיכון להתקפות אבטחה פוטנציאליות כגון פגיעות בקרת גישה שבורה.
כדי להפחית סיכון זה, חשוב ליישם תהליכי אימות והרשאה חזקים, כולל הגדרה נכונה של הרשאות גישה. על ידי כך, אתה מבטיח שרק משתמשים מורשים יכולים לגשת למשאבים מוגנים, ובסופו של דבר, להפחית את הסיכון לפרצות אבטחה פוטנציאליות ואובדן נתונים.
אתה יכול למצוא את הקוד של הפרויקט הזה בו GitHub מאגר.
הגדר שרת Express.js Apollo
שרת אפולו הוא יישום שרת GraphQL בשימוש נרחב עבור ממשקי API של GraphQL. אתה יכול להשתמש בו כדי לבנות בקלות סכימות GraphQL, להגדיר פותרים ולנהל מקורות נתונים שונים עבור ממשקי ה-API שלך.
כדי להגדיר שרת Express.js Apollo, צור ופתח תיקיית פרויקט:
mkdir graphql-API-jwt
cd graphql-API-jwt
לאחר מכן, הפעל פקודה זו כדי לאתחל פרויקט Node.js חדש באמצעות npm, מנהל חבילות ה-Node:
npm init --yes
כעת, התקן את החבילות הללו.
npm install apollo-server graphql mongoose jsonwebtokens dotenv
לבסוף, צור א server.js קובץ בספריית השורש, והגדר את השרת שלך עם הקוד הזה:
const { ApolloServer } = require('apollo-server');
const mongoose = require('mongoose');
require('dotenv').config();const typeDefs = require("./graphql/typeDefs");
const resolvers = require("./graphql/resolvers");const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req }),
});const MONGO_URI = process.env.MONGO_URI;
mongoose
.connect(MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to DB");
return server.listen({ port: 5000 });
})
.then((res) => {
console.log(`Server running at ${res.url}`);
})
.catch(err => {
console.log(err.message);
});
שרת GraphQL מוגדר עם ה typeDefs ו פותרים פרמטרים, ציון הסכימה והפעולות שה-API יכול לטפל בהן. ה הֶקשֵׁר option מגדירה את אובייקט req להקשר של כל פותר, מה שיאפשר לשרת לגשת לפרטים ספציפיים לבקשה כגון ערכי כותרת.
צור מסד נתונים של MongoDB
כדי ליצור את החיבור למסד הנתונים, ראשית צור מסד נתונים MongoDB אוֹ הגדר אשכול ב- MongoDB Atlas. לאחר מכן, העתק את מחרוזת ה-URI של חיבור מסד הנתונים, צור א .env קובץ, והזן את מחרוזת החיבור באופן הבא:
MONGO_URI=""
הגדר את מודל הנתונים
הגדר מודל נתונים באמצעות Mongoose. ליצור חדש models/user.js קובץ וכלול את הקוד הבא:
const {model, Schema} = require('mongoose');
const userSchema = new Schema({
name: String,
password: String,
role: String
});
module.exports = model('user', userSchema);
הגדר את סכמת GraphQL
ב- API של GraphQL, הסכימה מגדירה את מבנה הנתונים שניתן לבצע שאילתה, כמו גם מתאר הפעולות הזמינות (שאילתות ומוטציות) שתוכל לבצע כדי ליצור אינטראקציה עם נתונים דרך ה ממשק API.
כדי להגדיר סכימה, צור תיקיה חדשה בספריית הבסיס של הפרויקט שלך ושמה לה graphql. בתוך תיקיה זו, הוסף שני קבצים: typeDefs.js ו resolvers.js.
בתוך ה typeDefs.js קובץ, כלול את הקוד הבא:
const { gql } = require("apollo-server");
const typeDefs = gql`
type User {
id: ID!
name: String!
password: String!
role: String!
}
input UserInput {
name: String!
password: String!
role: String!
}
type TokenResult {
message: String
token: String
}
type Query {
users: [User]
}
type Mutation {
register(userInput: UserInput): User
login(name: String!, password: String!, role: String!): TokenResult
}
`;
module.exports = typeDefs;
צור Resolvers עבור ה-API של GraphQL
פונקציות Resolver קובעות כיצד נתונים מאוחזרים בתגובה לשאילתות ומוטציות של הלקוח, כמו גם שדות אחרים המוגדרים בסכימה. כאשר לקוח שולח שאילתה או מוטציה, שרת GraphQL מפעיל את הפותרים המתאימים לעבד ולהחזיר את הנתונים הנדרשים ממקורות שונים, כגון מסדי נתונים או ממשקי API.
כדי ליישם אימות והרשאה באמצעות JSON Web Tokens (JWTs), הגדר רזולורים עבור מוטציות הרישום והכניסה. אלה יטפלו בתהליכי רישום המשתמש והאימות. לאחר מכן, צור פותר שאילתות לאחזור נתונים שיהיה נגיש רק למשתמשים מאומתים ומורשים.
אבל ראשית, הגדר את הפונקציות ליצירת ולאמת את ה-JWTs. בתוך ה resolvers.js התחל על ידי הוספת הייבוא הבא.
const User = require("../models/user");
const jwt = require('jsonwebtoken');
const secretKey = process.env.SECRET_KEY;
הקפד להוסיף את המפתח הסודי שבו תשתמש כדי לחתום על אסימוני אינטרנט של JSON לקובץ ה-env.
SECRET_KEY = '' ;
כדי ליצור אסימון אימות, כלול את הפונקציה הבאה, המציינת גם תכונות ייחודיות עבור אסימון JWT, למשל, זמן התפוגה. בנוסף, אתה יכול לשלב תכונות אחרות כגון שהונפקו בזמן על סמך דרישות היישום הספציפיות שלך.
functiongenerateToken(user) {
const token = jwt.sign(
{ id: user.id, role: user.role },
secretKey,
{ expiresIn: '1h', algorithm: 'HS256' }
);
return token;
}
כעת, יישם את לוגיקת אימות האסימון כדי לאמת את אסימוני ה-JWT הכלולים בבקשות HTTP עוקבות.
functionverifyToken(token) {
if (!token) {
thrownewError('Token not provided');
}
try {
const decoded = jwt.verify(token, secretKey, { algorithms: ['HS256'] });
return decoded;
} catch (err) {
thrownewError('Invalid token');
}
}
פונקציה זו תיקח אסימון כקלט, תוודא את תקפותו באמצעות המפתח הסודי שצוין ותחזיר את האסימון המפוענח אם הוא חוקי, אחרת זורקת שגיאה המציינת אסימון לא חוקי.
הגדר את ה-API Resolvers
כדי להגדיר את הפותרים עבור ה-API של GraphQL, עליך לתאר את הפעולות הספציפיות שהוא ינהל, במקרה זה, את פעולות רישום המשתמש והכניסה. ראשית, צור א פותרים אובייקט שיכיל את פונקציות הפותר, אם כן, מגדיר את פעולות המוטציה הבאות:
const resolvers = {
Mutation: {
register: async (_, { userInput: { name, password, role } }) => {
if (!name || !password || !role) {
thrownewError('Name password, and role required');
}const newUser = new User({
name: name,
password: password,
role: role,
});try {
const response = await newUser.save();return {
id: response._id,
...response._doc,
};
} catch (error) {
console.error(error);
thrownewError('Failed to create user');
}
},
login: async (_, { name, password }) => {
try {
const user = await User.findOne({ name: name });if (!user) {
thrownewError('User not found');
}if (password !== user.password) {
thrownewError('Incorrect password');
}const token = generateToken(user);
if (!token) {
thrownewError('Failed to generate token');
}
return {
message: 'Login successful',
token: token,
};
} catch (error) {
console.error(error);
thrownewError('Login failed');
}
}
},
ה הירשם מוטציה מטפלת בתהליך הרישום על ידי הוספת נתוני המשתמש החדשים למסד הנתונים. בזמן ש התחברות מוטציה מנהלת כניסות משתמש - עם אימות מוצלח, היא תיצור אסימון JWT, כמו גם תחזיר הודעת הצלחה בתגובה.
כעת, כלול את פותר השאילתות לאחזור נתוני משתמש. כדי להבטיח שהשאילתה הזו תהיה נגישה רק למשתמשים מאומתים ומורשים, כלול היגיון הרשאות להגבלת הגישה למשתמשים עם מנהל מערכת תַפְקִיד.
בעיקרו של דבר, השאילתה תבדוק תחילה את תקפות האסימון ולאחר מכן, את תפקיד המשתמש. אם בדיקת ההרשאה תצליח, שאילתת הפותר תמשיך לאחזור והחזרת נתוני המשתמשים ממסד הנתונים.
Query: {
users: async (parent, args, context) => {
try {
const token = context.req.headers.authorization || '';
const decodedToken = verifyToken(token);if (decodedToken.role !== 'Admin') {
thrownew ('Unauthorized. Only Admins can access this data.');
}
const users = await User.find({}, { name: 1, _id: 1, role:1 });
return users;
} catch (error) {
console.error(error);
thrownewError('Failed to fetch users');
}
},
},
};
לבסוף, הפעל את שרת הפיתוח:
node server.js
מדהים! כעת, קדימה, בדוק את הפונקציונליות של ה-API באמצעות ארגז החול של Apollo Server API בדפדפן שלך. לדוגמה, אתה יכול להשתמש ב- הירשם מוטציה להוספת נתוני משתמש חדשים במסד הנתונים, ולאחר מכן, את התחברות מוטציה לאימות המשתמש.
לבסוף, הוסף את אסימון JWT למקטע כותרת ההרשאה והמשך לשאילתה במסד הנתונים עבור נתוני משתמש.
אבטחת ממשקי API של GraphQL
אימות והרשאה הם מרכיבים חיוניים לאבטחת ממשקי API של GraphQL. עם זאת, חשוב להכיר בכך שהם לבדם עשויים שלא להספיק כדי להבטיח אבטחה מקיפה. עליך ליישם אמצעי אבטחה נוספים כמו אימות קלט והצפנה של נתונים רגישים.
על ידי אימוץ גישת אבטחה מקיפה, אתה יכול להגן על ממשקי ה-API שלך מפני התקפות פוטנציאליות שונות.