GraphQL — это популярная альтернатива традиционной архитектуре RESTful API, предлагающая гибкий и эффективный язык запросов и манипулирования данными для API. С этими Растущее распространение становится все более важным уделять приоритетное внимание безопасности API-интерфейсов GraphQL для защиты приложений от несанкционированного доступа и потенциальных данных. нарушения.
Одним из эффективных подходов к обеспечению безопасности API GraphQL является внедрение веб-токенов JSON (JWT). JWT предоставляют безопасный и эффективный метод предоставления доступа к защищенным ресурсам и выполнения авторизованных действий, обеспечивая безопасную связь между клиентами и API.
Аутентификация и авторизация в API GraphQL
В отличие от REST APIAPI 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
Наконец, создайте сервер.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. контекст Опция настраивает объект req в контексте каждого преобразователя, что позволит серверу получить доступ к деталям, специфичным для запроса, таким как значения заголовка.
Создайте базу данных MongoDB
Чтобы установить соединение с базой данных, сначала создать базу данных MongoDB или настроить кластер на MongoDB Atlas. Затем скопируйте предоставленную строку URI подключения к базе данных, создайте .env файл и введите строку подключения следующим образом:
MONGO_URI=""
Определите модель данных
Определите модель данных с помощью Mongoose. Создать новый модели/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.
Чтобы определить схему, создайте новую папку в корневом каталоге вашего проекта и назовите ее. графкл. Внутри этой папки добавьте два файла: typeDefs.js и резольверы.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;
Создайте резольверы для GraphQL API
Функции преобразователя определяют, как данные извлекаются в ответ на запросы и изменения клиента, а также другие поля, определенные в схеме. Когда клиент отправляет запрос или мутацию, сервер GraphQL запускает соответствующие преобразователи для обработки и возврата необходимых данных из различных источников, таких как базы данных или API.
Чтобы реализовать аутентификацию и авторизацию с использованием веб-токенов JSON (JWT), определите преобразователи для мутаций регистрации и входа в систему. Они будут обрабатывать процессы регистрации и аутентификации пользователей. Затем создайте преобразователь запросов на выборку данных, который будет доступен только прошедшим проверку подлинности и авторизованным пользователям.
Но сначала определите функции для создания и проверки JWT. в резольверы.js файл, начните с добавления следующих файлов import.
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
Чтобы определить преобразователи для 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 от различных потенциальных атак.