An overview of functions-as-a-service (FaaS) and GraphQL, relevant security considerations and attacks, and a number of demos.
What is Functions-as-a-Service (FaaS)?
FaaS enables developers to write standalone, single purpose functions that are triggered by events (e.g. HTTP requests, an upload to S3, text message to your messaging gateway). When the function runs, a container/VM executes the task and then freezes post execution and gets killed.
Serverless is a natural extension of the progression from monolith applications, to microservices, to simply single purpose functions, the smallest individual unit of compute.
FaaS are short lived, they have no ports (you’re accessing them through an API gateway, the function is not handling the network communication itself), and have no state.
-
Containers/MicroVMs are “thawed” when they are invoked again
-
Additional containers are spawned to scale based on concurrent invocations
-
Function is invoked launching a container to run and are destroyed after.
Note: some serverless deployment managers may give your function more permissions than it needs.
What is GraphQL?
GraphQL is an API query language that enables the client to query and mutate exactly what is wanted; for example, multiple resources or only certain attributes of one resource can be read or modified in a single request.
Usually there’s only a single endpoint to query and insert (mutate) data for the API, and GraphQL implementations support pubsub functionality for realtime data. Example NodeJS Express GraphQL server:
const app = express();
const PORT = 3000;
app.use('/graphql', graphlHTTP({
schema: schema,
graphiql: true,
}));
GraphQL Terminology
-
Schemas and Types: You can define object types (e.g. User) with fields (
first_name
,age
) and their types (String, integer). -
Queries => Select Statements
-
Mutations => Insert/Update Statements
-
Scalar => Custom Data Types
-
Resolver => Function that translates the type system to DB queries
FaaS Security Considerations
No Frameworks
Functions generally don’t use standard, battle-tested web frameworks like Django or Rails. Instead, devs are writing endpoints that don’t have all of the useful built-in security features, so they have to roll their own input validation code, implement access controls and logging per function, and overall have to ensure they protect against standard OWASP-style web app attacks like XSS, CSRF, SQL injection, etc.
No Network Attack Surface 👍
Observability/Debugging is a challenge
Monitoring functions for attacks and security-related logging is challenging unless you specifically architected for them.
Events from Multiple Sources
Functions can be triggered from events like S3, SNS, SQS, etc., which presents a larger attack surface than just web requests. Traditional security controls such as WAFs and others may be ineffective, as functions may be triggered by events instead of just HTTP requests. DAST tools tend to have trouble scanning functions, as they’re short-lived and generally can’t be crawled like standard web apps.
Attacker’s View of FaaS
Routes to FaaS Pwnage!
Abhay outlines three primary FaaS attack vectors, only the first of which is unique to FaaS:
-
Attacking functions (and cloud provider) through non-API Gateway Events
-
As mentioned previously, initiating events, such as an S3 upload, that trigger a function enable you to attack functions out of band.
-
-
Attacking functions (and cloud provider) through its API – standard web service attacks
-
Denial of Service
Function Data Event Injection
Abhay defines event injection as an injection attack that’s triggered through third-party event notifications, for example, a file being uploaded to S3, a message sent over a notification service, a message received on a queue, DynamoDB stream events, etc.
The data in these events can cause standard injection bugs, such as insecure deserialization, XXE, (No)SQL injection, SSRF, and template injection.
An example function data event injection attack
Challenges
Function data event injection vulnerabilities are hard to test for, as execution is largely out-of-band.
Further, as mentioned previously, functions have a wide variety of execution scenarios and are thus hard to protect with WAFs or other network security controls, as events may be triggered by multiple types of non-HTTP protocols.
Privilege Escalation – IAM and Other Misconfigurations
Permissions are a common source of security issues in FaaS, as functions tend to be given overly permissive capabilities for the resources they interact with. Issues range from public S3 buckets, having access to all DynamoDB tables, or having non-required permissions that give an attacker who finds a bug in the function to escalate their privileges.
Note: AWS credentials are often stored in the function’s environment variables, so if a vulnerability allows you to examine the target function’s environment you can extract them and start pivoting as that function’s AWS role.
Other Weaknesses
Authorization weaknesses, especially with JSON Web Tokens (JWTs) can be common as well as DoS attacks that exploit weaknesses in third-party library code.
Attacker’s view of GraphQL
GraphQL is subject to the same standard OWASP-style web vulnerabilities you’d see in any web app or API. However, a core challenge is that as the GraphQL ecosystem is still young, there’s little framework support for common functionality, meaning devs have to take a lot of the responsibility that standard web frameworks like Django handle for you.
Thus, it’s important to keep the following core areas in mind: access controls, input validation, query whitelisting, and rate limiting.
Abhay believes the most prominent and important vulnerability classes for GraphQL are authorization and info disclosure flaws, potentially NoSQL injection, and denial-of-service.
GraphQL Introspection (Information Disclosure)
GraphQL let’s you introspect the server’s API, showing all actions, types (aka DB tables), and fields (aka columns) that are accessible.
This makes it easier to for an attacker to leak sensitive info (they can easily see what’s available) as well as potential mass assignment-style bugs, as the attacker can learn all of the attributes for each type.
GraphiQL is an IDE with autocomplete and introspection capabilities for GraphQL APIs. Think Postman for GraphQL (Click to enlarge)
Injection with GraphQL
Unlike REST APIs, where there’s generally a single query per function, GraphQL resolvers are written for a larger query space. With NoSQL databases, this could lead to injection (and potentially RCE) if dynamic scripting is enabled, in backends like MongoDB, Elasticsearch, etc.
Denial of Service (DoS)
Nested queries can lead to server-side resource exhaustion or high cloud provider bills, especially with many-to-many fields.
For example, an Employee
may have a reference to their Team
, and Team
may have a reference to all of the Employees
on that team. An attacker can craft a malicious query that finds an employee, then that employee’s team, then all of the employees on the team, then…