GraphQL vs gRPC: Which One Creates More Secure APIs?

Every request goes through the HTTP server to the GraphQL server, which builds context and runs the resolver. However, the business logic should remain separate from the resolvers, as it often changes. In such a situation, the separation allows you to implement the changes with just a few lines of code.

There are three points where authentication is implemented in GraphQL:

  1. Before the HTTP server: The first point where authentication can be reached is directly before a network request reaches the HTTP server. Authentication performed here protects the GraphQL endpoints. Note that authenticating here makes it impossible to connect data from HTTP requests to the GraphQL server, which prevents you from getting access to the logged user.
  2. Before the GraphQL server: Another point where authentication can be performed is between the HTTP server and the GraphQL server using the GraphQL context. This requires three steps.
    • Implement a custom context building function.
    • Access the network request and add the context object.
    • Add CurrentUser to that context object.
  3. In business logic: Authentication can also be part of the business logic code. However, implementing authentication at this stage is not recommended, as it exposes too much information to your application.

Authentication in GraphQL differs from that of REST APIs, as the latter performs authentication of requests using API keys or HTTP authentication schemes. These two simple authentication strategies require the credentials to be attached to the request header.

An example of authentication implemented between the HTTP server and the GraphQL server illuminates this difference:

const HEADER_NAME = ‘authorization’

const typeDefs = gql`
   type Query {
    me: User
   }
   type User {
    id: ID!
    username: String!
   }
`

const server = new ApolloServer({

   typeDefs,
   context: async ({ req }) => {
    let authToken = null
    let currentUser = null

    try {
     authToken = req.headers[HEADER_NAME]

     if (authToken) {
      currentUser = await tradeTokenForUser(authToken)
     }
    } catch (e) {
      console.warn(`Unable to authenticate using auth token: ${authToken}`)
    }

    return {
     authToken,
     currentUser
    }
  }
})

Authorization with GraphQL

In addition to authentication, GraphQL performs authorization to help determine the level of access a verified user can have.

In GraphQL, authorization is more complex than in REST because it changes how servers and clients interact. In REST APIs, the endpoints and responses are statically defined by the servers. As REST allows for the authorization of individual endpoints, GraphQL, permits clients to submit arbitrary requests to the server, while each mutation and query must be authorized.

The goal of GraphQL should be to build authorization logic as close to the data as possible within the GraphQL API.

This example demonstrates the logic in which only a user can see their password:

var postRepository = require(‘postRepository’);

var postType = new GraphQLObjectType({
 name: ‘Password’,
 fields: {
  body: {
   type: GraphQLString,
   resolve: (password, args, context, { rootValue }) => {
    return postRepository.getBody(context.user, password);
   }
  }
 }
});

Authentication with gRPC

This design supports several authentication mechanisms as well as a simple authentication API, this includes:

  • SSL/TLS. Can be used to authenticate the server and encrypt client-server communication.
  • ALTS. Supported as a transport security mechanism.
  • Goggle’s token-based authentication. Attaches metadata-based credentials to responses and requests.

Unlike gRPC, a REST API doesn’t support sophisticated mechanisms. Instead, it uses simpler authentication strategies that involve attaching credentials, such as API keys, access tokens, or using a username and password for a request.

Additionally, unlike GraphQL, which authenticates each request for data, gRPC creates a contract between a server and a client for fast transport. This enables it to easily handle thousands of requests without compromising its security. Unlike gRPC, GraphQL provides multiple options for where to implement authentication.

Here’s an example of a client-side SSL/TLS gRPC authentication example:

// SSL ChannelCredentials object creation.

auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());

// Channel creation.

auto channel = grpc::CreateChannel(server_name, channel_creds);

// Creating a stub on the channel.

std::unique_ptr<Greeter::Stub> stub(Greeter::NewStub(channel));

// Making RPC calls on the stub.

grpc::Status s = stub->sayHello(&context, *request, response);

Authorization with gRPC

The gRPC doesn’t have a built-in authorization mechanism but supports the use of external solutions such as a JSON Web Token (JWT) and an Envoy Proxy.

Authorization with JWT uses gRPC interceptors acting like a middleware function on the client and server sides. A JWT token attached to each request determines what that request is permitted to access.

Unlike gRPC, REST API authentication is only performed on the server-side when a request is made. In gRPC, the server and the client participate in the authorization process via gRPC interceptors.

Vulnerabilities and common attack vectors

Although thorough authentication and authorization practices are useful for maintaining security, both GraphQL and gRPC are susceptible to vulnerabilities. Exploring the more common attack vectors for each API framework and some best practices that can be implemented plays a role in mitigating cyber risk.

GraphQLThis framework includes two primary attack vectors:

1. Batch attacks: One of GraphQL’s strengths is the ability to send multiple requests to the server in a single call. This diminishes API overhead by reducing the number of round trips. However, a threat actor can use this feature to initiate a brute force attack on the system. It can be done by sending many queries with different credentials simultaneously—otherwise known as batch attacks. This risk can be mitigated by enabling a strong authentication policy. Authentication can be strengthened and batch attacks can be prevented by limiting the number of failed login attempts. Additionally, you can restrict logins to a specified IP address range that are safelisted.

2. Injection attacks: In most cases, GraphQL queries are directed to a database through a resolver. This is responsible for extracting the data requested if the API client is trusted. If the client authentication process is weak, cybercriminals can initiate an SQL or NoSQL injection attack that fetches sensitive information from the database. This widespread attack is currently listed as the number three threat on the OWASP Top 10. Injection attacks can be mitigated by creating parameterized queries and adding input validation. For instance, if users must enter their email as a parameter, ensure it has a stylized “at” sign (@). Additionally, creating a safelist of permitted queries stops malicious questions before they can even be considered.

gRPCThere soluition includes three primary attack vectors:

1. Implementation vulnerabilities: You can implement gRPC using its C-core language or wrappers around its code. Doing this by using the C language, which forms its core, introduces a risk to the system’s critical components. The best way to implement gRPC is by using wrappers that translate calls made in different languages into C calls. Using Java or Go eliminates interference with the core system, reducing the chances of vulnerabilities being introduced into the API.

2. Data transmission threats: The other risk gRPC presents is data transmission to the server during remote calls. This increases the risk of a data leak that exposes the service architecture. Such exposure can be the basis of a more dangerous attack. This threat can be mitigated by creating secure channels for data transmission. Create secure channels by ensuring that data in transit is encoded and that it can only be decoded by the specified receiver.

3. Service denial attacks: This threat involves an existing bug that denies service to C/C++ and gRPC users until the service is restarted. The bug is triggered when many connections are opened within a short period of time. You can resolve this by using a load balancer and a service watchdog to control and limit traffic received by the service.
If too many requests are received at once, the load balancer distributes them to ensure denial of service isn’t initiated.

Conclusion

GraphQL supports request batching, and gRPC supports the creation of channels that can process thousands of requests. REST, in contrast, handles one request at a time, making it too slow for applications that make many requests simultaneously.

GraphQL reduces duplication and gives lean responses without any unnecessary data. It’s the best solution in cases where a number of requests need to be made from different sources to render a view on a client’s application. gRPC is most effective when building a highly scalable distributed system and very effective when building a backend with hundreds or thousands of interconnected microservices.

gRPC’s authentication levels and limited frontend exposure makes it more secure than GraphQL. But while GraphQL’s susceptibility to common attacks—such as injection and brute force attacks—make it less safe, there are several proactive steps to mitigate vulnerabilities in GraphQL.

GraphQL and gRPC both provide ways to secure their APIs and build faster and more secure APIs when compared to REST. It’s important to remember that your team doesn’t need to commit to a single API technology, and that both GraphQL and gRPC can be used interchangably based on your team’s requirements and based on each solution’s benefits and functionalities.

It continues to become easier to make applications for the web, and businesses are using them at ever-increasing rates. Unfortunately, not everyone—including developers and those who must defend their systems—knows how to secure them properly. With the interconnection of most web applications and IT systems, this lack of knowledge exposes enterprises to security risks from hackers who know how to exploit vulnerabilities in order to gain access to systems, software, and sensitive data.

Trend Micro Cloud One™ – Application Security is built for speedy deployment, with minimal impact on development streams and performance. It only takes a minute to add the library to your application, and there is no need to change your development code. Application Security bootstraps itself into your application at runtime, as opposed to an SDK that has to be integrated into the application. Learn more about how Application Security minimizes design and deployment risks by protecting against sophisticated hacks from inside the application.

Read More HERE