GraphQL接口的Resolver层是连接查询请求和数据库操作的核心环节,很多SQL注入攻击都是利用Resolver层未对输入参数做严格校验的漏洞,将恶意SQL片段传入数据库执行。在Resolver层做好参数校验,能从根源上降低注入风险。

GraphQL接口SQL注入的常见攻击路径
攻击者通常会针对GraphQL的参数传递特性构造恶意输入,常见的攻击场景包括:
- 在查询参数的字符串字段中拼接SQL关键字,比如传入
id参数时填写1 OR 1=1 - 利用嵌套查询传入超长参数,绕过前端的简单长度校验
- 在枚举类型参数中传入未定义的非法值,触发数据库异常查询
Resolver层参数校验的核心维度
1. 参数类型强制校验
GraphQL本身有类型定义,但Resolver层仍需再次确认参数类型,避免类型转换漏洞。比如定义查询用户信息的Resolver时,先校验传入的userId是否为整数类型。
// GraphQL类型定义
const typeDefs = `
type Query {
getUser(userId: Int!): User
}
type User {
id: Int
name: String
email: String
}
`;
// Resolver层实现
const resolvers = {
Query: {
getUser: (parent, args) => {
// 强制校验参数类型,不是数字直接返回错误
if (typeof args.userId !== 'number' || !Number.isInteger(args.userId)) {
throw new Error('userId必须为整数类型');
}
// 后续数据库查询逻辑
return db.query('SELECT * FROM users WHERE id = ?', [args.userId]);
}
}
};
2. 参数格式与取值范围校验
对于特定格式的参数,比如邮箱、手机号、日期等,需要使用正则或规则校验格式合法性,同时限制参数的取值范围,避免传入异常值。
const resolvers = {
Query: {
getUserByEmail: (parent, args) => {
const email = args.email;
// 邮箱格式正则校验
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
if (!emailReg.test(email)) {
throw new Error('邮箱格式不合法');
}
// 限制邮箱长度不超过255位
if (email.length > 255) {
throw new Error('邮箱长度超出限制');
}
return db.query('SELECT * FROM users WHERE email = ?', [email]);
}
}
};
3. 特殊字符过滤与转义
对于必须接收字符串类型的参数,需要过滤或转义SQL特殊字符,比如单引号、分号、注释符等,避免拼接SQL时触发注入。
const resolvers = {
Query: {
searchArticles: (parent, args) => {
const keyword = args.keyword;
// 过滤SQL特殊字符
const filteredKeyword = keyword.replace(/['";\]/g, '');
// 限制关键词长度不超过50位
if (filteredKeyword.length > 50) {
throw new Error('搜索关键词长度超出限制');
}
// 使用参数化查询,进一步避免注入
return db.query('SELECT * FROM articles WHERE title LIKE ?', [`%${filteredKeyword}%`]);
}
}
};
4. 枚举参数白名单校验
如果参数是固定可选值的枚举类型,必须在Resolver层校验传入值是否在白名单内,禁止接收未定义的非法值。
const validStatus = ['active', 'inactive', 'banned'];
const resolvers = {
Query: {
getUsersByStatus: (parent, args) => {
const status = args.status;
// 白名单校验
if (!validStatus.includes(status)) {
throw new Error('状态参数不合法,可选值为active、inactive、banned');
}
return db.query('SELECT * FROM users WHERE status = ?', [status]);
}
}
};
校验策略的最佳实践
实际落地时,建议将参数校验逻辑抽成公共函数,避免每个Resolver重复编写校验代码,同时优先使用参数化查询而非字符串拼接SQL,双重保障接口安全。另外可以结合日志记录校验失败的请求,方便后续排查攻击行为。参数校验的粒度需要根据业务场景调整,既要拦截恶意请求,也不能过度限制影响正常用户的使用体验。