API Security: What Every Developer Needs to Know

At Traceable, we monitor 500 billion API calls a month from a very diverse customer base across different industries and geographies. These customers have end-users that span 180+ countries. This broad spectrum of traffic provides Traceable with a unique vantage point to observe the evolving landscape of API design and the sophisticated methods used by malicious actors to exploit vulnerabilities.

On average, a significant portion of our API traffic—up to 3%—is flagged as security events, ranging from one-shot malicious attacks to anomalous behaviors and orchestrated sequences of API calls aimed at exploiting vulnerabilities or perpetrating business fraud. The majority of these incidents align with the OWASP API Top 10 Vulnerabilities, highlighting the critical need for robust API security practices.

Many of these vulnerabilities can be avoided by implementing some basic API design best practices. In this blog post, we’ll explore seven key aspects of API design. With APIs as the backbone of most modern applications, prioritizing API security from the outset is paramount. We encourage professionals engaged in API design, implementation, and security to integrate these crucial API security principles into their workflows.

Authentication and Authorization

Broken Authentication is listed as one of the OWASP API top 10 vulnerabilities. It commonly arises from unauthenticated APIs, often obscured behind client-side authentication, leading to a false sense of security.

API keys serve as a prevalent authentication method for APIs, but they come with limitations – 

  • API keys cannot be used for authentication in untrusted environments where an unauthorized actor can access the API keys in-transit or at-rest, for example in audit logs.
  • API keys typically lack explicit expiration dates, allowing unauthorized actors to maintain long-term persistence and makes key revocation hard to implement.
  • API keys often have a global scope which makes it difficult to enforce fine-grained access control.

Instead of relying on API keys, consider a more robust approach to authentication and authorization, using established standards like OpenID and OAuth. Oauth enables explicitly scoped authorization along with token expiry and refresh flows.

Scoped Token Management

Undefined or poorly defied scopes in APIs are one of the leading causes of Broken Function Level Authorization. API endpoints often implement various functions, some of which should be reserved for users with elevated privileges. These may include special API endpoints like /api/admin/ and/or specific HTTP methods like DELETE or PUT or even unique parameters like /api/users/?email=all. To mitigate this issue, APIs should establish a clear hierarchy of scopes that align with user roles. The server must then validate the user’s role against the API scope, preferably in a separate business logic layer.

Data Protection

Data integrity and confidentiality are foundational principles in electronic communication. It’s imperative for all APIs to utilize HTTPS to provide these fundamental guarantees.

One prevalent anti-pattern in API design is excessive data exposure. APIs often expose a surplus of data that clients then filter at the presentation layer. However, relying solely on client-side filtering is insufficient. Malicious users can bypass these filters and directly access the API, exposing sensitive data unintentionally. This vulnerability is known as Broken Object Property Level Authorization.

Lastly, API servers must ensure confidentiality of its own secrets at-rest. For example, if the server uses Oauth 2, proper storage and protection of the client secret and client id is paramount. Unfortunately, too often they are stored incorrectly in public clients like mobile apps.

Input Validation

While most API traffic comes from trusted applications, for example a front end for a mobile app, that is directly developed by a trusted team, it can be tempting to introduce input validation only on the client-side, however attackers will often send requests to APIs directly.

Basic input data types like string, integers, dates etc at the least require validation on format,  character sets, boundaries, range etc. For more complex data types like URL, files, file path etc, more sophisticated input validation may be required. If a parameter is a URL, validate the URL to avoid SSRF attacks. Similarly, when dealing with file parameters, validation is necessary to mitigate LFI attacks.

Another API vulnerability arising from inadequate input validation is Mass assignment. Attackers exploit this weakness by attempting to manipulate object’s properties that they shouldn’t have access to. Consider an API designed to create a new user that accepts input parameters such as username, name and email address. After input validation of these parameters, the server may assign additional properties like role=user, APIs vulnerable to mass assignment would allow a user to include a role parameter in their query and change their role.

Audit Logging

Audit logs are immutable and tamperproof records of all API activity that describes “Who did what, where, and when”. Audit logs of APIs are immensely useful in debugging issues, and investigating security incidents. It is also important to consider the retention policy of audit logs. We recommend at least 30 days of hot storage and a year of cold storage.

Moreover, API developers should ensure that audit logs inadvertently don’t log any sensitive information in the audit logs like JWT tokens, PII field etc.

KISS (Keep It Simple Stupid)

KISS design principle, as the name suggests, advocates for simplicity in the design. In the realm of API design, this entails several key considerations.

  • API endpoints should have a single responsibility. For RESTful API, it often means that a single API endpoint manipulates a single object type, utilizing various HTTP methods such as PUT, DELETE, and POST to facilitate different manipulations. When in doubt rely on industry standards rather than reinventing the wheel
  • Avoid overloading a single API with multiple operations. Instead, it’s preferable to decompose complex operations into separate APIs. As a side effect, it also improves the readability and future extensibility of the API
  • APIs are often subject to evolution and extensions. To accommodate future changes, it’s advisable to segregate new versions by incorporating a version number in the path parameters. For instance, using “/v1/api/” and “/v2/api/” delineates distinct versions and facilitates seamless transition between them, just remember to decommission old versions!

Rate Limiting

Rate limiting is essential for APIs to ensure optimal performance, stability, and security. Rate limiting safeguards against DoS attacks by restricting the number of requests a client can make within a specified timeframe.

Rate limits also help prevent abuse and misuse of APIs by limiting the number of requests by a single client reducing the risk of data exfiltration attempts. Note that implementing rate limits per source IP address isn’t as effective, since those can be bypassed by the use of anonymizing forward proxies. We recommend implementing a rate limit per auth token whenever possible. 

It is important to communicate the rate limit with the client using proper HTTP headers like X-Ratelimit-* headers or graphql-rate-limit, and returning a 429 HTTP error code when the rate limit is exceeded ensures that clients are aware of and can adhere to the imposed limitations. Graphql query complexity calculator is another way to defend against DoS attacks against graphql endpoints.