System architectures leveraging APIs offer many benefits, but warrant caution, as they also make it easy to introduce serious security vulnerabilities into your products and services.
It is quite common now for web-applications to be designed to utilize one or more proprietary APIs in their background tiers. The motivation to architect applications in this way are many, and includes maximizing re-use, modularity, and flexibility. There is nothing inherently insecure about the approach; however there are some subtleties in properly designing secure APIs and the applications that interface with them that are often overlooked. The failure to understand or address those considerations can result in “designing in” serious security vulnerabilities right from the start.
This article explores various security considerations that can make or break API security, identifying potential problems and highlighting preventative measures. It is intended to be a pre-implementation resource for API designers, and a post-implementation resource for security testers.
The reader is assumed to be familiar with the OWASP API Top 10, and our treatment is intended to complement and elaborate that work, not re-iterate it.
Secure API Design, Documentation, and Testing
One common source of vulnerabilities in applications that utilize API services is the failure to document or otherwise consistently communicate who is responsible for what in regard to security. That is, a lack of clarity regarding the responsibilities of the API vs. the responsibilities of the business or presentation layers of the application. In other words, we often find security vulnerabilities in the gap between what the API thinks its job is and the what the application thinks its job is. Overlaps are OK, or at least harmless, but gaps can be problematic. Our point here is that requirements matter. Requirements are the basis for both Quality Assurance (QA) as well as security testing and must clearly and completely document:
- Authentication and authorization verification
- Role and permissions
- Input validation rules for every data item of function/entry point
- Exception (error) handling and reporting/responses
- Accountability and forensic requirements for logs
Furthermore, the API must be security tested separately from any application that uses it. Applications may not fully utilize the API(s) that it integrates, and partial API testing coverage is incomplete and therefore unacceptable.
Access Control refers to the process by which the caller is verified as having rights to use the service, and consists of two cooperating sub-functions:
- Authentication: The process by which a user or requester proves their identity
- Authorization: The process by which it is determined whether an authenticated user is currently entitled to utilize a particular resource (e.g. API function call in this context).
Authentication is a bit tricky with respect to API design, because APIs are typically stateless, meaning each transaction is completely self-contained, and there is no dependency or “memory” between calls. This presents a challenge because we do not want to be forced to re-authenticate before or during every call.
Thus, there must be a means of authenticating once, and demonstrating that fact with each API call. Authentication may be handled by the application through a separate user-login process that conditionally produces a “Session” Token, which can then be provided for verification with each API request. Alternatively, the API service itself can support authentication, although ideally through another “stateful” service, thereby preserving its own “stateless” design.
The failure of any function call within the API to validate the requestor’s current authentication status puts the API service at risk for unauthorized use, potentially exposing sensitive information to unauthorized parties and the service itself to DoS attack.
It is essential that every API entry point demand current evidence that the user has authenticated and has a valid session, such as a session “token” that can be validated by the API in the moment.
Additional protective measures include the whitelisting of servers authorized to submit requests (i.e. the Business Logic Tier) to the API, as well as server-to-server certificate based authentication.
Finally, API security test cases must verify that the failure to provide a legitimate session token in any request consistently returns an error response. Note that this includes test cases in which:
- The session token is omitted
- The session token is not real (i.e. was never valid)
- The provided session token has expired or been invalidated (e.g. due to user logout
As noted above, authorization refers to the means by which a requestor’s permission to use a particular function call is validated. In the simplest case, permissions can be documented as a table of roles and functions, such as the one below in which a ‘Y’ indicates that Role is authorized to call that function:
|Role||func1 ( )
||func2 ( )
||func3 ( )
The verification process becomes more complex, however as the number of roles associated with a user grows:
If the set of functions offered by the API span multiple user “roles“, and they are not sufficiently distinguished or consistently enforced by the API, this leads to Least Privilege violations. If the API service does recognize multiple user-roles, the failure to consistently enforce the related permissions produces Privilege Escalation vulnerabilities.
The failure of any function call within the API to validate the requestor’s current permissions at the time of the call puts the API service at risk for unauthorized use, potentially exposing sensitive information to unauthorized parties.
Note that mis-configuration of user and their roles (at deployment time) can be a source of authorization failure, even if the API properly implements authorization checks.
There is no magic here. This is a testing activity. QA test cases must include positive tests to ensure each role can access their required functions, and the other roles cannot. Test cases that exercise the combination of roles, with a without a particular permission should also be included.
Obviously, if API services accept or return sensitive data, then the communication channel must be encrypted. Furthermore, if the mechanism for validating authentication or authorization involves providing re-usable values (such as session tokens), then communications must be encrypted because the exposure of session credentials would subvert access control.
Input Validation Issues
Input validation refers to the verification that a data item meets the expectations of the API with respect to one or more of the following “meta-data” characteristics:
- Data type
- Size (minimum and maximum number of characters)
- Bounds (minimum and maximum numeric value)
- Regex pattern(s) (whitelist of acceptable patterns)
APIs typically feature dozens of entry points and hundreds of potential data items. The root cause of injection vulnerabilities is the failure to detect malicious input, potentially impacting back-end systems (e.g. databases) as well as clients (e.g. XSS) when the data is returned. Sometimes this is due to flawed or incomplete detection, other times from the complete lack of validation.
The API must protect itself and the systems behind it (such as databases, other APIs etc.) from malformed data. Note that the Input Validation rules imposed by the API may not be identical to those of the applications utilizing it. The exact expectations/requirements of each input should be documented and enforced within each entry point. Items that fail validation should cause the call to fail — it usually a bad idea for the API to attempt to “cleanse” unacceptable data.
Note that the API is responsible for protecting itself from malformed input, not the applications that use it. That may sound unnecessarily rigid, but consider that:
- There may be multiple applications utilizing the API service with different input validation needs
- Input validation should be performed on both the client and server sides for ergonomic and security reasons respectively
- It is NOT an option to perform input validation solely on the client UI
A standardized error message should be returned in response to the detection of malformed input.
Encoding refers to the pro-active modification of input data to render it compatible and benign to downstream processing. This can be tricky for an API, because it should only account for behavior that ALL its application clients will need. Analyzing all down-stream processing to determine a common core is central to understanding the API requirements. Non-standard needs must be accommodated by the respective applications.
The failure to neutralize input data can lead to injection vulnerabilities including: XSS, XXE, and SSI, depending on what downstream processing occurs on that data item. These can be very serious problems for the applications that engender them.
If and how the API encodes data must be explicitly documented. For example, it is not unreasonable to have a policy to HTML encode any and all data returned to web-applications. The proper approach to output encoding depends on the downstream environment you are trying to protect, but may include:
- HTML Encoding for web-applications and XML processing
- Use of Prepared/Parameterized Statements for database transactions
- File resources
- Operating System meta-character neutralization
- Protecting Comma Separated Value (CSV) file content
- Protecting Log files
- Protecting LDAP queries
Note that Input Validation can also be a remedial strategy, as it may be possible to render dangerous characters or patterns “unacceptable” and thereby avoid the need for output encoding entirely.
Security vulnerabilities sometimes emerge as a consequence of how the API handles errors through the unintentional exposure of sensitive information. Note that this includes:
- Sensitive data that is being processed by the API
- Sensitive information about the API itself or the environment it operates in
The unintended exposure of sensitive information is a security violation, even if it occurs due to an error condition. Just as with Input Validation, attackers will bombard your API entry points with malicious data in hope of generating revealing error messages. Any application or data exposed can be useful to an attacker.
The complete set of error conditions detected by the API and how they are reported back to the caller must be documented. All exceptions must be handled.
It is common to find applications that disclose application-sensitive information in response to malformed input. This is due to one or more of the following factors:
- Input requirements were not satisfied, and the API server consequently choked on the input and is returning a stack-trace or dump of the error information, inclusive of sensitive data.
- The API service properly detected malformed input but failed to respond with a standardized response, devoid of sensitive information.
- A downstream service (such as a database) from the API rejected or choked on the data, and is returning an error message, inclusive of sensitive data.
Error handling is tested by fuzzing API entry points with malformed and malicious data. These are security test cases that overlap with regular Quality Assurance (QA) testing.
Logs are not just development aids, but an essential source of forensic insight when needed. In addition, logs also are often the only source of accountability to prove which user performed which actions retrospectively. You will not get these benefits for free, they must be “by design”. Even then, the desired benefits must be verified through examination. Also note that attackers will attempt to corrupt the integrity of logs through Log Forgery.
Log injection issues arise when designers fail to account for Log Forgery attacks in their Input Validation, and when they write untrusted input directly to the log file, instead of through parameterized strings. The most dangerous character to a log file is a carriage return (\r \n etc.), as it can be used to simulate the end of one entry and the start of another within the log file. Note there are many ways (i.e. different encoding schemes) to represent a carriage return.
Another source of security findings with log files is that they simply don’t contain sufficient information for forensic purposes. This is typically a failure of requirements, which must describe the events and details that must be logged.
- The API must provide accurate time-marked and user-attributed event recording in secure logs. It must be possible to integrate API log entries with the logs of the applications that use it. Testing should include analysis of log content to ensure accountability and sufficient forensic insight. Logging should be supported by a library that prevents Log Forgery.
Session Management is concerned with the integrity of the user-session. That is, the time window within which a user can legitimately access the system. This is related to, but distinct from Authentication which is concerned with proving who the user is. Session management is concerned with the validity of the session token and what it represents. Since the API is probably NOT the ultimate Authentication service, it is also not likely responsible for session management. It is however, responsible for detecting invalid sessions.
Security problems arise in regard to sessions when an application fails to check the validity of the requestor’s session token, thus failing to notice that the user has logged off, terminated their session, or exceeded an inactivity time-out. Of course, if the application or service fails to support these functions properly, the API will be vulnerable as well.
The API must validate that each request is made on behalf of an active, valid session, and reject as erroneous those that are not.
Application designers who boldly take on API design are advised to study not only the common mistakes that others have made, but also to consider the rigor of their design processes if they want to build secure applications. In addition to enumerating many sensitive areas that facilitate the introduction of security defects, we have shown that documentation and communication are as critical to success as technical strategies for avoiding vulnerabilities.
Affinity IT Security
Affinity IT has been helping its clients improve cybersecurity since 2009. With over 30 years of IT expertise, including software and website development, we are uniquely qualified to assess and improve the security of your websites, applications, and networks.
We hope you found the insights in this article to be helpful, and are here to assist you with your application and network security testing needs.
Contact us to discuss your particular challenges and how we can help.