GraphQLに対する攻撃について
GraphQLは、クエリ言語として広く利用されるようになってきたAPI通信のプロトコルで、クライアントが必要とするデータを柔軟に取得することができます。しかし、この柔軟性が攻撃者に悪用される可能性もあり、さまざまな攻撃が可能です。代表的な攻撃には、情報漏洩、DoS攻撃、スキーマの解析、認証・認可の不備を悪用した攻撃などがあります。
脆弱なコード例(Node.js):
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type User {
id: ID!
username: String!
password: String!
email: String!
}
type Query {
users: [User]
}
`;
const resolvers = {
Query: {
users: () => [
{ id: 1, username: 'admin', password: 'secret', email: 'admin@example.com' },
{ id: 2, username: 'user', password: 'password', email: 'user@example.com' },
],
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});
攻撃手法:
攻撃者が以下のクエリを送信して、すべてのユーザーの機密情報を取得することができます。
{
users {
id
username
password
email
}
}
この攻撃により、ユーザーのパスワードやその他の個人情報が漏洩する可能性があります。
脆弱なコード例(Python Flask with Graphene):
from flask import Flask
from flask_graphql import GraphQLView
import graphene
class User(graphene.ObjectType):
id = graphene.ID()
username = graphene.String()
class Query(graphene.ObjectType):
users = graphene.List(User)
def resolve_users(self, info):
return [
User(id="1", username="admin"),
User(id="2", username="user")
]
schema = graphene.Schema(query=Query)
app = Flask(__name__)
app.add_url_rule(
'/graphql',
view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=True)
)
if __name__ == '__main__':
app.run()
攻撃手法:
攻撃者が以下のように大量のバッチリクエストを送信し、サーバーのリソースを枯渇させることができます。
query {
user1: users { id username }
user2: users { id username }
user3: users { id username }
...
user1000: users { id username }
}
この攻撃により、サーバーの負荷が増大し、他のユーザーがサービスを利用できなくなる可能性があります。
脆弱なコード例(Java with Spring Boot and GraphQL):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import java.util.List;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Controller
class UserController {
@QueryMapping
public List<User> users() {
return List.of(
new User(1, "admin", "secret", "admin@example.com"),
new User(2, "user", "password", "user@example.com")
);
}
}
攻撃手法:
認証や認可が適切に実装されていない場合、攻撃者が以下のクエリを送信して、機密データを取得できます。
{
users {
id
username
password
email
}
}
この攻撃により、管理者の認証情報が漏洩し、不正アクセスのリスクが高まります。
脆弱なコード例(PHP with GraphQL-PHP):
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Schema;
use GraphQL\GraphQL;
require_once __DIR__ . '/vendor/autoload.php';
$queryType = new ObjectType([
'name' => 'Query',
'fields' => [
'users' => [
'type' => Type::listOf(new ObjectType([
'name' => 'User',
'fields' => [
'id' => Type::nonNull(Type::string()),
'username' => Type::nonNull(Type::string()),
'bio' => Type::string(),
]
])),
'resolve' => function() {
return [
['id' => '1', 'username' => 'admin', 'bio' => str_repeat('a', 1000000)],
['id' => '2', 'username' => 'user', 'bio' => str_repeat('b', 1000000)],
];
}
]
]
]);
$schema = new Schema(['query' => $queryType]);
$rawInput = file_get_contents('php://input');
$input = json_decode($rawInput, true);
$query = $input['query'];
$result = GraphQL::executeQuery($schema, $query);
$output = $result->toArray();
echo json_encode($output);
攻撃手法:
攻撃者が巨大なデータをリクエストすることで、サーバーのメモリを使い果たし、他のユーザーがサービスを利用できなくなります。
{
users {
id
username
bio
}
}
Node.jsの例:
const { AuthenticationError } = require('apollo-server');
const resolvers = {
Query: {
users: (parent, args, context) => {
if (!context.user.isAdmin) {
throw new AuthenticationError('Unauthorized');
}
return context.db.getUsers();
}
}
};
Pythonの例:
from graphql.execution.executors.asyncio import AsyncioExecutor
from graphql.validation.rules import NoComplexity
schema = graphene.Schema(query=Query, mutation=Mutation)
app.add_url_rule(
'/graphql',
view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=True, executor=AsyncioExecutor())
)
app.config['GRAPHQL'] = {'MAX_COMPLEXITY': 100}
Javaの例:
public class ValidationDirective extends SchemaDirectiveWiring {
@Override
public GraphQLFieldDefinition onField(SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition> env) {
GraphQLFieldDefinition field = env.getElement();
return field.transform(builder -> builder.argument(GraphQLArgument.newArgument()
.name("limit")
.type(Scalars.GraphQLInt)
.defaultValue(10)
));
}
}
まとめ: GraphQLは柔軟性が高いため、攻撃者によって悪用される可能性があります。認証と認可の適切な設定、
クエリの複雑さの制限、入力データの検証などの防止策を講じることで、GraphQLに対する攻撃を効果的に防ぐことができます。これにより、アプリケーションのセキュリティと信頼性を向上させることが可能です。