GraphQL 是一種用於 API 的查詢語言和執行引擎,由 Facebook 於 2015 年開源。它提供了一種更有效、更靈活的方式來設計、建立和消費 API。
不同於傳統的 REST API,GraphQL 允許客戶端精確指定所需的資料,無需多餘或不足,從而實現更高效的資料傳輸。
GraphQL 的核心特點包括:
https://github.com/fei3363/ithelp_web_security_2024/commit/bdc0fa658bcb0942a7bb2983f93022dc56d81e06
這個檔案定義了 GraphQL 查詢和變更的解析器。它包含了模擬的資料儲存和不安全的操作。
// 模擬資料儲存
let users = [
{ id: '1', username: 'user1', email: 'user1@example.com', password: 'hashedpassword1' },
{ id: '2', username: 'user2', email: 'user2@example.com', password: 'hashedpassword2' }
];
let posts = [
{ id: '1', title: 'First Post', content: 'This is the first post', authorId: '1' },
{ id: '2', title: 'Second Post', content: 'This is the second post', authorId: '2' }
];
let comments = [
{ id: '1', content: 'Great post!', authorId: '2', postId: '1' },
{ id: '2', content: 'Thanks for sharing', authorId: '1', postId: '2' }
];
const resolvers = {
Query: {
hello: () => 'Hello world!',
users: () => users, // 直接回傳所有使用者資料,包括密碼
user: (_, { id }) => {
// 不安全的資料過濾,可能導致原型污染攻擊
return users.find(user => user.id == id);
},
posts: () => posts,
search: (_, { term }) => {
// 不安全的搜尋,可能導致資訊洩漏
const results = [...users, ...posts, ...comments].filter(
item => JSON.stringify(item).toLowerCase().includes(term.toLowerCase())
);
return results;
},
dangerousQuery: (_, { query }) => {
// 非常不安全:允許客戶端執行任意 JavaScript 程式碼
try {
return eval(query);
} catch (error) {
return { error: error.toString() };
}
},
},
// ... 其他解析器
};
module.exports = resolvers;
這個檔案定義了 GraphQL 的類型和操作結構。
const { gql } = require('apollo-server-express');
const typeDefs = gql`
type Query {
hello: String
users: [User!]!
user(id: ID!): User
posts: [Post!]!
search(term: String!): [SearchResult!]!
dangerousQuery(query: String!): JSON
}
type User {
id: ID!
username: String!
email: String!
password: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
comments: [Comment!]!
}
type Comment {
id: ID!
content: String!
author: User!
}
union SearchResult = User | Post | Comment
scalar JSON
type Mutation {
createUser(username: String!, email: String!, password: String!): User!
createPost(userId: ID!, title: String!, content: String!): Post!
createComment(userId: ID!, postId: ID!, content: String!): Comment!
deleteUser(id: ID!): Boolean!
updateUser(id: ID!, username: String, email: String, password: String): User
dangerousUpdate(collection: String!, id: ID!, updates: JSON!): JSON
}
`;
module.exports = typeDefs;
這個檔案設定並啟動 Apollo Server。
const { ApolloServer } = require('apollo-server-express');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
async function startApolloServer(app) {
const server = new ApolloServer({
typeDefs,
resolvers,
});
await server.start();
server.applyMiddleware({ app });
}
module.exports = { startApolloServer };
基本查詢測試
query { hello users { id username email } }
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query { hello users { id username email } }"}' \
http://nodelab.feifei.tw/graphql
密碼洩漏測試
query { users { id username email password } }
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "query { users { id username email password } }"}' \
http://nodelab.feifei.tw/graphql
不安全的使用者建立
mutation { createUser(username: "newuser", email: "new@example.com", password: "password123") { id username email password } }
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "mutation { createUser(username: \"newuser\", email: \"new@example.com\", password: \"password123\") { id username email password } }"}' \
http://nodelab.feifei.tw/graphql
未授權的使用者刪除
mutation { deleteUser(id: "1") }
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "mutation { deleteUser(id: \"1\") }"}' \
http://nodelab.feifei.tw/graphql
mutation { dangerousUpdate(collection: "users", id: "2", updates: { password: "hacked" }) }
curl -X POST \
-H "Content-Type: application/json" \
-d '{"query": "mutation { dangerousUpdate(collection: \"users\", id: \"2\", updates: { password: \"hacked\" }) }"}' \
http://nodelab.feifei.tw/graphql
這些測試展示了多個嚴重的安全問題,包括:
在實際的 GraphQL API 開發中,應該採取適當的安全措施來防止這些問題,如:
GraphQL 作為一種現代化的 API 查詢語言和執行引擎,為開發者提供了更靈活、高效的資料取得方式。
它的特點如聲明式資料取得、單一端點、強類型系統等,使得 API 開發和使用變得更加直觀和高效。
然而,GraphQL 的靈活性也帶來了一系列的安全挑戰。
我們透過實作範例展示了一些常見的 GraphQL 安全漏洞,包括敏感資訊洩漏、不安全的資料操作、缺乏授權檢查等。
這些問題凸顯了在開發 GraphQL API 時需要特別注意的安全考量:
GraphQL 的哪個特性可能導致過度取得(over-fetching)問題?
A. 強類型系統
B. 單一端點
C. 客戶端指定查詢內容
D. 內省功能
答案:C
解析:GraphQL 允許客戶端精確指定所需的資料,這種靈活性可能被濫用,導致客戶端請求過多不必要的資料,造成過度取得問題。
在 GraphQL 中,下列哪種做法最容易導致安全漏洞?
A. 使用強類型定義
B. 實施查詢深度限制
C. 允許執行任意 JavaScript 程式碼
D. 使用單一端點
答案:C
解析:允許執行任意 JavaScript 程式碼(如範例中的 dangerousQuery)是極其危險的做法,可能導致遠程程式碼執行等嚴重的安全漏洞。
為防止 GraphQL API 中的 DoS 攻擊,最有效的方法是?
A. 使用強密碼策略
B. 實施查詢複雜度和深度限制
C. 加密所有資料傳輸
D. 使用 JSON Web Tokens (JWT)
答案:B
解析:透過限制查詢的複雜度和深度,可以有效防止攻擊者構造極其複雜的查詢,從而避免伺服器資源耗盡,防止 DoS 攻擊。
在 GraphQL schema 中,包含密碼欄位的主要問題是?
A. 增加了 schema 的複雜性
B. 可能導致效能問題
C. 違反了 GraphQL 最佳實踐
D. 可能導致敏感資訊洩漏
答案:D
解析:在 schema 中包含密碼欄位可能導致敏感資訊洩漏,因為它允許查詢直接取得密碼資訊。最佳實踐是永遠不在 API 回應中包含密碼。
以下哪種 GraphQL 操作最不可能造成安全問題?
A. 批量查詢
B. 遞迴查詢
C. 變更(Mutation)操作
D. 內省查詢
答案:D
解析:雖然內省查詢可能洩漏 API 結構,但相比其他選項,它通常不會直接導致資料洩漏或未授權的資料修改。批量查詢可能導致效能問題,遞迴查詢可能造成深度問題,而未經適當保護的變更操作可能導致未授權的資料修改。