Serverless, also known as Function as a Service, is a cloud computing architecture where applications are hosted on a third-party system. The serverless provider manages the server provisioning, scalability, and maintenance of the infrastructure, while the application owners pay only for the use of the provider’s resources. This architecture helps developers concentrate on writing business logic without having to manage the hosting and infrastructure.
A serverless architecture can be used to solve many different problems and use cases, for example as a backend service for a Web Application. Indeed, serverless applications are becoming very popular.
However, serverless has some unique security issues. In this article, we’ll explore a few security challenges from injection attacks, and discuss the best practices to resolve them.
Web Application Architecture with Serverless
First, how can a web application be built upon serverless? Typically, it is done by combining several backend services.
These various pieces, and the interaction among them, raise several potential security issues, especially injection attacks.
A web application can have many different types of security challenges: authentication, authorization, vulnerability, penetration, injection, etc. Many of these are the same for serverless as for conventional web applications. However, serverless applications have some additional considerations for injection attacks.
An injection attack involves user inputs that are evaluated or executed without being properly validated. However, in addition to user inputs, serverless applications can also receive event data. This is an additional source of possible injections.
Here are a few different injection methods being used by hackers today.
In code injection, a hacker tries to inject code via text input fields so that the server will run the malicious code. This type of injection can occur if an application relies blindly on the user to input genuine data, without correctly validating it.
For example, the URL below accepts a PHP page as an input parameter:
Hackers can exploit this by passing in their own PHP file in the query string, hoping that the server will process and execute the malicious code that it contains:
A successful code injection of this kind can compromise data confidentiality and availability, or even compromise the server itself.
SQL injection is the successful submission of malicious SQL commands to a database. The attacker can steal, delete, or modify the data, or even run administrative commands against the database itself.
With the latest changes in Java and .NET libraries, the chances of SQL injection are lower. But if an application is using older versions of these libraries, or a PHP or ASP application, SQL injection attacks are still significant threats.
Here is an example. An application constructs this query string to retrieve account transactions for a specific customer:
string query = "SELECT * FROM transactions WHERE customer = "'" + userName + "';"
In use, it is supposed to resolve to a query like this:
SELECT * FROM transactions WHERE customer = 'exampleUserName';
However, an attacker manages to pass in this value, which the application accepts and processes without validation:
userName = "foo' OR 'a'='a"
Therefore, the query that will be run is this:
SELECT * FROM transactions WHERE customer = 'foo' OR 'a'='a';
Note that the hacker has constructed a logical OR condition which will be true for every record evaluated. Therefore, the query will return all records in the table.
Other forms of mischief are also possible. For example, for database servers (such as Microsoft SQL Server 2000) which allow the submission of multiple queries separated by semicolons, the attacker could input something like this:
userName = "foo'; DELETE FROM transactions; --"
This will resolve to two queries, followed by a commented line (designated by the double hyphens) that will be ignored:
SELECT * FROM transactions WHERE customer = 'foo'; DELETE FROM transactions; --';
…and the second query will delete all records from the transactions table.
This injection attempts to execute commands against the operating system itself. For example, the following code is used to display the contents of a file within the application:
<?php $file = $_GET['filename']; $contents = shell_exec("cat $file"); echo "$contents"; ?>
A normal use would be this:
But an attacker might abuse it like this:
…which will attempt to list the names of all files in the current directory, and then delete them.
Cross Site Scripting (XSS)
Attackers attempt to insert malicious code into web applications. Victims that use the applications or browse infected sites will have the code run within their browsers. Attackers can then access session cookies and other sensitive information maintained by the browsers.
Common examples of this include malicious scripts being included in user-generated content such as article comments or product reviews. Another popular tactic is to distribute URLs (for example, via email) that include a script; the site in the URL includes the script as part of the content it serves to the victims’ browsers.
Event Data Injection
As mentioned previously, serverless functions potentially have additional vulnerabilities besides the conventional injection attacks described above. Serverless architectures usually must accept event inputs, and this broadens their attack surfaces.
Some types of event data are resistant to corruption by an attacker: for example, standard notifications of database events. Others are much more liable to being hijacked for malicious purposes: for example, HTTP/S API calls.
In theory, validating and sanitizing event data is not any different than validating direct user inputs. In practice, it can be a much more challenging task. User input validation is a well-understood problem, and can be limited to untrustworthy parameters such as GET/POST arguments and HTTP headers. Event-data validation is a much newer, and much broader, challenge. And current DAST/SAST/IAST security testing tools aren’t yet adapted for performing complete injection-based vulnerability testing on serverless applications.
Best Practices for Application Security
Injection attacks on serverless applications are a broad category of threats. To mitigate them, it is important to follow these practices.
Best Practices for Validating Inputs
- Most frameworks include validation libraries. Use them!
- Validate all the input data at the server, even if they are validated on the client side. This is essential, as hackers can easily bypass client-side validation.
- Use a positive security model instead of negative. In other words, instead of blacklisting incorrect data formats or content, whitelist the range, length, format, and content of acceptable inputs.
- Identify and escape special characters consistently during input validation.
- Ensure the validation of XML inputs against a schema, and email inputs against RFC 822 rules.
- If an input is a filename/path, resolve the name of the file in the host OS during validation.
Best Practices for Preventing Code & SQL Injection
- Use prepared statements/parameterized queries.
- Validate user inputs for special characters.
- Limit the privileges of application users. For example, most database access should be done without DDL privileges.
- Hide detailed error message information in responses, as hackers can use these to learn about the database’s architecture and behavior.
Best Practices for Handling Event Data Injection
- Validating and sanitizing user inputs is a must.
- Use a web application firewall (WAF) to inspect incoming HTTP/S traffic to your serverless application. (More on this below.)
- Apply threat modeling in the development lifecycle and ensure that you consider all possible event types and entry points into the system. Assuming that the input can only arrive from limited event triggers can lead to a higher risk of a breach.
- Carefully review API changes before publishing them. Modern DevOps practices emphasize fast, frequent delivery; if you aren’t diligent, this can lead to security compromises.
Achieving Robust Serverless Security
For all Internet-facing applications, robust security is essential.
In the modern threat environment, it is essential to scrub all HTTP/S traffic using a platform such as Reblaze: a comprehensive cloud-based platform that includes a next-generation WAF, DDoS protection, advanced bot management, API security, and more.
However, serverless architectures raise an additional question. What about events that can trigger functions directly? For example, let’s say a Lambda function is written that will process files which are uploaded to an S3 bucket. In this case, protection is not provided, and malicious user activity could result in a security compromise. Even though Reblaze’s WAF excels at detecting and blocking injection attempts, this Lambda function has a vulnerability, because the event data does not pass through the WAF for processing.
In cases like these, a small amount of effort can provide large security dividends. Although it is convenient to allow users to trigger functions when they utilize backend services, a more optimal approach is to structure functions so that user inputs always pass through the WAF (such as AWS WAF). This allows Reblaze to examine the results of all events, and defeat injection attempts.
In the example above, the Lambda function could be restructured so that it is not triggered by a user directly uploading a file to S3. Instead, a simple web application would accept the file from the user, upload it to S3, and then trigger the Lambda function. This small change allows Reblaze to validate the user’s input before anything else happens, and automatically closes a number of security holes that otherwise could exist.
Taking this approach offers significant leverage. A modest effort creates a large payoff—a more robust security profile.
Interested in reading more about securing serverless applications? See our article Security Challenges of Serverless Architectures.