5 Security Tips for Your GraphQL API

5 Security Tips for Your GraphQL API

In 2015 GraphQL was created by Facebook as an alternative to REST APIs to give more power to frontend developers by making API calls more flexible. GraphQL achieves this goal by providing its API consumers with a query language that allows them to query just the data they need.

While GraphQL can improve frontend developer experience, its specification doesn’t have opinions on security. A GraphQL API needs to translate queries to the data fetches, which adds another layer of complexity to your API architecture.

We already wrote an article about the top ten API security threads, and they all hold for GraphQL APIs. In this article, you will learn about additional enhancements to improve your GraphQL API security.

New privacy laws in the US and EU make every security hole a huge financial danger for API companies.

1. Comprehensive Query Authorization

Although small APIs often provide an HTTP accessible way to access databases, GraphQL is generally data-source agnostic. This means GraphQL doesn’t care where the data comes from. The data can come from a database, or it can be fetched from another API. This also means, GraphQL isn’t a data-store, so it has to authenticate against the actual data-sources and check if the users are authorized to access specific data from these sources.

Suppose one GraphQL server that serves many different clients is authenticated as one user in an upstream service or database. In that case, the GraphQL server has to do the authorization checks inside the resolves by itself. Otherwise, this can lead to a leak of private or, even worse, medical data regulated by CCPA laws and makes you liable.

You can check permissions in every resolver if needed:

const resolvers = {
  Query: {
    adminResolver: async (parent, args, context) => {
      if(!context.user || !context.roles.includes("admin")) throw new Error("Permission denied!");

      ...

      return data;
    },
  },
};

2. Input Validation and Normalization

Back in the days, developers would generate SQL queries in the frontend and send them to the API to fetch data from an SQL database; thus, SQL injections were born. An attacker could write SQL and send it to the API, which would execute it without asking further questions.

While nobody prevents a GraphQL API developer from creating a type that accepts an SQL string that gets blindly executed on the server, this is seldom the case.

But accepting data from clients is always a risk. This is why all inputs should be validated and normalized before any data-fetching happens. Especially custom scalars are susceptible to this threat since they don’t do default validations.

3. Introspection Restriction

GraphQL provides its API consumers with a convenient introspection feature, which allows GraphQL clients to ask the API what types of data it provides. This is great, because now a client developer doesn’t have to look into the documentation, but can directly ask the API server what data is available.

But introspection can also be abused when not controlled rigorously. For example, when GraphQL types that provide administrative functionality can be discovered and used by regular users.

GraphQL API creators have to use rigid authorization schemes for introspections to make it harder for attackers to find API vulnerabilities. If external developers don’t use your API, it can also be a good idea to disable the introspection feature in production environments.

Apollo server, for example, allows to disable introspection with a simple configuration flag:

const IS_PRODUCTION = process.env.ENVIRONMENT === "production";

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: !IS_PRODUCTION,
});

server.listen();

4. Query Limitations

GraphQL queries give API consumers much flexibility to fetch exactly the data they want with just one request, but this feature can also be abused in multiple ways.

Malicious clients can create queries that are very deep, complex, or generally long-running for various reasons. If these attackers find an edge case that leads to heavy computations or exploit the N+1 query problem, they can overload the API and degrade performance for other clients drastically.

That’s why a GraphQL API should limit query execution time to a sane maximum. Even better, another way to achieve this goal is to restrict query depth or complexity so it makes it even tougher to execute common GraphQL DDoS attacks which tend to use recursive and deep querying only meant to disrupt services.

5. Upstream Error Hiding

As said before, a GraphQL API isn’t a data-storage mechanism; this means it uses upstream services like databases or other APIs to fetch the actual data. These services can have errors too. If you deliver the upstream services’ errors to your clients, an attacker can use them to get insights about your architecture.

To mitigate this threat, you should always process upstream errors before delivering them to a client; this way, you can hide the services you get your data from and prohibit attackers from taking advantage of bugs or security holes these services could have.

Let’s look at the following code example:

const resolvers = {
  Query: {
    myResolver: async (parent, args, context) => {
      try {
        const data = await fetchFromRemoteDataSource();
        return process(data);
      } catch (upstreamError) {
        const cleanError = analyzeUpstreamError(upstreamError);
        throw cleanError;
      }
    },
  },
};

The resolver tries to fetch some data from a remote data-source, but this could fail. The error we catch comes from the remote data source, so it could contain information about the data-source.

We need to analyze the error and clean it of any upstream information before delivering it to the client.

Keep Your GraphQL APIs Secure

GraphQL APIs are an excellent way to improve the developer experience for the frontend team. It helps to optimize data fetches by giving clients a way to specify what they need. But this comes at the cost of higher complexity in the API architecture, which increases the attack surface of an API.

With the rise of GDPR and CCPA laws, which stipulate fines of multiple million dollars, it’s more crucial than ever for API creators to keep the common API threats in mind when developing an API and also look out for GraphQL specific weaknesses that could appear.

Learn More About Moesif Protect from Threats and Abuse With Moesif 14 day free trial. No credit card required. Learn More
Monitor and Secure Your GraphQL APIs With Moesif Monitor and Secure Your GraphQL APIs With Moesif

Monitor and Secure Your GraphQL APIs With Moesif

Learn More