Why GraphQL API Security Is Unique
Securing GraphQL APIs requires some unique considerations for developer and security teams — here's what to know as you scale your GraphQL adoption.
Join the DZone community and get the full member experience.
Join For FreeEnterprise security teams have had since 2015 to familiarize themselves with GraphQL API security. But many — if not most — still haven’t captured the security nuances of the popular open-source query language. Simply understanding GraphQL’s processes and vulnerable attack vectors isn’t sufficient; it’s also necessary to recognize exploit attempts and nefarious queries (and trigger actions whenever those threats arise). A complete GraphQL security strategy must also be ready to defeat attacks designed to infiltrate GraphQL specifically. A more generalized API security strategy isn’t going to cut it, as headlines continue to prove.
Security teams likely have either a web application firewall (WAF) performing active application monitoring to detect threats or a comparable in-house solution that leverages access logs to monitor threat behavior. They also likely depend on specific indicators when monitoring for anomalous activity — including HTTP methods and response status codes, sensitive API routes, and API parameters. Keeping an eye on these indicators can identify attacks where clients try to overwhelm a registration endpoint, perform multiple unsuccessful logins, attempt account enumeration, or tamper with key parameters.
With REST APIs, monitoring these events is relatively simple. An application only needs to return HTTP status codes that adhere to the RFC 2616 protocol for clients and security teams to receive and understand clear activity signals (as long as security teams have a relatively accurate API inventory). This traditional monitoring approach relies on HTTP components. For example, the following rule uses the GET HTTP method and the 403 Forbidden response code to trigger a security alert if a client trying to access forbidden locations on the /v1/user/<id>/profile
route (available for most REST APIs):
alert if (http.method=GET and response.status_code=403 and http.uri=”/v1/user/<id>/profile”)
GraphQL routes don’t matter in the same way. First, GraphQL uses its own singular route (usually /graphql
). But more importantly, client intentions with respect to GraphQL APIs are determined by the query itself. GraphQL APIs may allow GET and POST methods, but only the query offers an accurate picture of what the client is really trying to accomplish. Therefore, traditional monitoring rules based on HTTP components aren’t at all effective at monitoring GraphQL API security.
GraphQL APIs also don’t return traditional HTTP status codes. It varies between different implementations of the GraphQL server, but it will usually return a 200 OK code to a query regardless of whether errors exist. Errors can instead be monitored by checking the server response using a dedicated errors JSON key.
The following example query lets a user receive their own user details by providing their user ID number:
query {
user(id: 1000) {
email
name
address
}
}
A client with the authorization to view user ID 1000 will receive a response like this (provided that the ID exists):
{
"data": {
"user": [
{
"email": "test@example.com",
"name": "test user",
"address": "123 Hollywood Blvd",
}
]
}
}
However, a client without the correct authorization will receive a GraphQL response like this:
{
"errors": [
"message": “You are not authorized to view this user.”
]
}
In each of these three scenarios, the GraphQL server might simply send a 200 OK HTTP code, making monitoring more challenging than with REST APIs. Traditional monitoring tools based on standard HTTP access logs will provide unhelpful log lines, such as these:
1.2.3.4 - - [02/Aug/2022:18:27:46 +0000] "POST /graphql HTTP/1.1" 200 - "-"
"curl/7.43.0" - 539
1.2.3.4 - - [02/Aug/2022:18:27:46 +0000] "POST /graphql HTTP/1.1" 200 - "-"
"curl/7.43.0" - 539
1.2.3.4 - - [02/Aug/2022:18:27:46 +0000] "POST /graphql HTTP/1.1" 200 - "-"
"curl/7.43.0" - 539
Proper security monitoring requires full visibility into both query and response payloads and tooling to contextualize threat events. Unfortunately, GraphQL also allows for batching multiple queries in a single HTTP request, offering attackers the ability to hide a number of actions within just one HTTP packet. The following example query would appear as a single HTTP request while making three separate queries:
query {
alias1000: user(id: 1000) {
email
name
address
}
alias1001: user(id: 1001) {
email
name
address
}
alias1002: user(id: 1002) {
email
name
address
}
}
It gets even better for attackers eager to conceal their activities: some GraphQL servers support arrays of batched queries, which look like this:
import requests
queries = [
{
"query":"query { user(id: 1000) { email name address }}",
"query":"query { user(id: 1001) { email name address }}",
"query":"query { user(id: 1002) { email name address }}"
}
]
r = requests.post('http://test.inigo.local/graphql', json=queries)
print(r.json())
All this shows that GraphQL simply cannot be effectively secured using incumbent API security strategies alone. Security teams must introduce processes specifically designed to provide a robust observability layer for safeguarding GraphQL APIs and applications and be able to identify threat activity even if it’s hidden within arrays of batched queries.
Open-source GraphQL offers tremendous advantages for developers and enterprises but also opens the door to a unique set of security risks. Any organization currently utilizing GraphQL alongside generalized traditional protections should take action now to introduce an effective GraphQL security strategy.
Opinions expressed by DZone contributors are their own.
Comments