3 Tuyệt Chiêu Bảo Mật GraphQL: Đừng Để Hacker “Vẽ Đường Cho Hươu Chạy”

Security tutorial - IT technology blog
Security tutorial - IT technology blog

GraphQL: Linh hoạt quá hóa rủi ro?

GraphQL đang dần soán ngôi REST API nhờ khả năng gom dữ liệu cực kỳ linh hoạt. Tuy nhiên, sự tự do này lại là con dao hai lưỡi. Nếu chỉ cài thư viện rồi “để đó”, anh em đang vô tình mời gọi hacker khai thác cấu trúc database của mình.

Mình từng thức trắng đêm xử lý một ca server bị treo cứng do database quá tải. Thủ phạm không phải là một đợt DDoS khổng lồ, mà chỉ là một vài query lồng nhau quá sâu. Bài học rút ra là: Đừng đợi đến khi hệ thống “sập nguồn” mới lo đi vá. Dưới đây là 3 kỹ thuật bảo mật cơ bản nhưng cực kỳ hiệu quả mà junior developer nào cũng phải nằm lòng.

1. Khóa cửa Schema: Vô hiệu hóa Introspection

Introspection là tính năng giúp các công cụ như GraphQL Playground hay Postman tự động gợi ý code. Tuy nhiên, trên môi trường Production, nó giống như việc bạn đưa bản vẽ chi tiết ngôi nhà cho kẻ trộm. Chỉ với một query đơn giản, hacker có thể biết sạch các bảng, quan hệ và cả những field nhạy cảm nhất.

Kinh nghiệm xương máu của mình: Chỉ bật tính năng này ở môi trường Dev. Khi deploy lên Production, hãy tắt nó ngay lập tức.

Cấu hình an toàn với Apollo Server

const { ApolloServer } = require('apollo-server');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  // Chỉ bật khi không phải môi trường production
  introspection: process.env.NODE_ENV !== 'production',
  playground: process.env.NODE_ENV !== 'production',
});

Việc này khiến hacker không thể dùng các tool tự động để scan lỗ hổng. Chúng sẽ phải “mò mẫm” trong bóng tối, giúp giảm thiểu đáng kể rủi ro bị tấn công có chủ đích.

2. Chặn đứng Query Depth (Độ sâu của Query)

Trong GraphQL, các đối tượng thường có quan hệ đệ quy. Ví dụ: Author có nhiều Posts, và mỗi Post lại thuộc về một Author. Kẻ xấu có thể gửi một query lồng nhau 100 lần để làm tràn bộ nhớ (stack overflow).

query evilQuery {
  author(id: "1") {
    posts { author { posts { author { # Lặp lại vô tận... } } } }
  }
}

Chỉ cần một query sâu 10 cấp, server của bạn có thể phải thực hiện hàng ngàn phép tính hoặc query database cùng lúc. Để giải quyết, mình thường dùng thư viện graphql-depth-limit.

Cài đặt và áp dụng

Anh em cài đặt nhanh qua npm:

npm install graphql-depth-limit

Sau đó, giới hạn độ sâu (thường là 5-7 cấp là đủ dùng):

const depthLimit = require('graphql-depth-limit');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [depthLimit(5)], // Chặn mọi query sâu hơn 5 cấp
});

Cơ chế này giúp server từ chối các yêu cầu độc hại ngay từ vòng gửi xe, trước khi chúng kịp chạm vào database.

3. Rate Limiting thông minh tại tầng Resolver

Nhiều người lầm tưởng chỉ cần chặn IP ở Nginx là đủ. Nhưng với GraphQL, mọi request đều đổ dồn về một endpoint duy nhất. Một request HTTP 200 hợp lệ vẫn có thể chứa một query “siêu nặng” lấy ra 10.000 bản ghi kèm dữ liệu liên quan.

Đó là lý do chúng ta cần giới hạn ngay tại Resolver. Thư viện graphql-rate-limit là lựa chọn hàng đầu để kiểm soát số lần gọi một field trong một khoảng thời gian nhất định.

Ví dụ thực tế bảo vệ Mutation

Giả sử bạn có chức năng gửi email reset password. Bạn chắc chắn không muốn một user gọi nó 100 lần mỗi phút.

const { createRateLimitDirective } = require('graphql-rate-limit');

const rateLimitDirective = createRateLimitDirective({
  identifyContext: (ctx) => ctx.user.id,
});

const typeDefs = gql`
  type Mutation {
    # Giới hạn 1 lần mỗi phút cho mỗi user
    sendResetEmail(email: String!): Boolean @rateLimit(window: "1m", max: 1)
  }
`;

Cách tiếp cận này bảo vệ tài nguyên hệ thống khỏi các cuộc tấn công brute-force hoặc spam trực tiếp từ phía client.

Lời khuyên cho anh em

Bảo mật không phải là đích đến, mà là một hành trình liên tục. Hãy áp dụng tư duy “Zero Trust”: Đừng tin bất kỳ dữ liệu nào client gửi lên. Nếu hệ thống lớn hơn, anh em nên tìm hiểu thêm về Query Cost Analysis để tính toán “giá trị” của mỗi request.

Đừng đợi đến khi database bị treo mới bắt đầu vá lỗi. Hãy bắt tay vào cấu hình Depth Limit và tắt Introspection ngay hôm nay. Chúc anh em build app vừa nhanh vừa an toàn!

Share: