API Security Best Practices Primer

On this page

Purpose

This document discusses various concepts of API security and best practices when developing an API. It is a companion document to the Guidance on Securing Application Programming Interfaces (accessible only on the Government of Canada network) (Section 7 of the Guidance on Secure Application Development) and Guidance on API Gateways.

It covers the following:

  1. Authentication mechanisms and identity context
  2. Granularity of authorizations
  3. Data filtering
  4. Transport vs. payload encryption
  5. Ensuring safe content

Authentication and Identity

APIs are a part of a system-to-system interaction rather than a user-to-system interaction. As such, the concept of authentication and identity are related, but must be considered separately.

Authentication Mechanisms

There are a wide range of options when determining what authentication mechanisms to support with your API. It is important to note that RESTful authentication mechanisms are optimized for API interactions initiated by web users while SOAP mechanisms have more robust system-to-system options.

Mechanisms appropriate for system-to-system (not user/browser initiated):

Mechanisms appropriate for user/browser-initiated API calls:

Least preferable mechanisms:

Identity Context

The sensitivity of the data along with privacy requirements should dictate the identity context required by the API (i.e., whether the API needs to authenticate the end user or simply the consumer system).

User Context – If the data is personal in nature, highly sensitive, or there are specific data access auditing/logging requirements at the individual level, authentication should be established using the end user credential all the way through to the backend API. Where possible, federated authentication mechanisms such as SAML and OpenID Connect should be used so the authentication token can be traced back to the initiating user. Where federated authentication mechanisms are not available, some user identifier (e.g., User ID) must be passed through the payload of the API request so the backend API can correlate the request to an individual.

System Context – If the data is less sensitive or does not need to be authorized at the individual level (e.g., reporting, financial, reference) and a system-to-system level trust relationship is adequate, then there is no need to propagate the user context. Direct system-to-system authentication methods are typically easier to implement and lower latency as they don’t require the extra interactions with an authentication provider or the extra steps of extracting and passing on the user context in a message.

Authorization

While authentication mechanisms validate the identity that the API consumer, authorization methods validate the permission of that consumer to access an API or components of that API.

API/Resource Level

The simplest authorization method is to grant access to the entire API as long as the consumer is authorized. This is made easier when APIs are sufficiently small in scope or the data is low-sensitivity so that multiple authorization profile are not needed.

Implied Authorization – This is the easiest authorization method on APIs using direct authentication (i.e., non-federated) mechanisms. The assumption is that if the API has validated the user, then the user should have access to the API. It’s important to note that care should be taken where authentication is carried out against credential directories which are shared with other systems and/or security domains (e.g., enterprise LDAP). This method should only be used on data of low sensitivity or where the API authentication is self-contained and does not share authentication data with any other system.

Access Control List – A permitted list of authorized credentials (e.g., API Keys, User IDs) should be implemented on APIs with a small and mostly static set of consumers. The management of ACLs at the individual credential level can get extremely cumbersome as the number of consumers and APIs grow, and thus should only be used in small API environments.

Role Based Access Control – RBAC is the defacto standard for API authorization at scale. Conceptually, consumer credentials should be attributed roles in the credential directory or authentication provider systems. The roles are then passed to the API and authorization is determined based on the role rather than the individual credential.

Externalizing Authorization – Several common mechanisms exist to externalize the authorization decisions from the API to keep the API code small and portable between environments:

Operation/Method Level

APIs can be secured at the Operation (SOAP) or Method (RESTful) level if more granular control is required (e.g., read-only vs. update). This typically requires the API provider to implement code to enforce the authorization. Some externalized access management solutions can also provide Operation or Method level protection. It is generally recommended to avoid managing access at this level as it can be operationally cumbersome. It is typically easier to break the API down into smaller APIs each with a different security/access profile.

Data Filtering

Limiting the amount of data returned via an API, either for privacy or for performance stability is an essential part of designing a secure API.

User-Specific Data

The API code should ensure that only data which the requesting user/system is permitted to see should be returned. Commonly understood identifiers (e.g., user ID, employee ID, system name, account number) should be used and propagated.

Data Segmentation

The ability to return large amounts of data make query APIs targets for DDoS attacks. APIs should be designed to prevent wildcard searches or long running queries. Limits should be set on the amount of data which can be returned per request to mitigate against HTTP timeouts and overflow types of attacks. In cases where large amount of data needs to be exchanged, look to implement a callback handler whereby the return data can be segmented into smaller payloads (e.g., pagination) and sent incrementally to the callback endpoint.

Encryption and Signing

APIs can be encrypted at the transport and message levels depending on the sensitivity of the data being passed.

Transport Encryption

As all APIs take place over HTTP, TLS (i.e., HTTPS) is the simplest form of encryption. This must be the default minimum for all API implementations. Transport encryption prevents the interaction from being observed by unauthorized parties elsewhere in the network, but does not prevent breaches whereby the authentication mechanism is compromised or downstream data leaks.

TLS certificates must be issued by a public or GC approved private certificate authority. Self-signed certificates should not be used for any purpose beyond testing.

Message Encryption

APIs dealing with highly sensitive data should be encrypted at the message layer. Encrypting the API message ensures that only consumers with the appropriate decryption key can view the payload. It provides an additional level of protection from unauthorized access.

WS-Security provides mechanisms (e.g., key exchanges, headers, schemas) for encrypting SOAP payloads.

Javascript Object Signing and Encryption (JOSE) is the best available framework for encrypting and signing JSON messages, but there are a few concerns and considerations:

Message Signing

Similar to message encryption, digital signatures for SOAP web services are supported by the WS-Security standard. In the case of SOAP, a message must be first encrypted for it to be signed. For RESTful APIs, JOSE can be used whereby the JOSE header is used to pass the cryptographic parameters and the entire payload is encrypted and signed. Cleartext JWS should be avoided as the message can be read even if the API consumer doesn’t validate the signature.

Content Protection

APIs must be designed to protect against malicious content and to ensure that responses can’t be intercepted and redirected to inject malicious content back to the API consumer.

Request Validation

APIs are similar in function to web forms in that they take data via HTTP and forward it to the business logic and data layers. Like web forms, the following request validation methods should be implemented:

Schema validation – Having a well-defined request schema and validating against that schema should be the first line of defense against malicious messages. Schema validation can implemented additionally in a gateway or proxy service to reduce the processing load on the API and to move the security perimeter further away from the data. Note that gateway or proxy implementation does not absolve the API itself of schema validation responsibilities. It is only intended as shifting of primary validation further to the perimeter and should not in any way weaken the security posture of the API code itself;

Regex – Implementing regex validation in addition to schema validation further protect against malformed messages and overflow type of attacks looking to exploit some form of data querying edge scenario. Note that regex is only effective on simple field types where the expected content is fairly consistent;

Strong types – The more specific the data structure, the easier it is to identify invalid messages. Define attributes as strong types (e.g., amount, length, name) instead of everything as a String to further limit malformed message attacks. This is also good development practice and allows APIs to be more easily tested; and

Special characters/keywords – Restricting special characters and key words mitigate against classic scripting or injection attacks.

Response Security

The following methods should be implemented to ensure the response message back to the API consumer is as secure as possible.

Response types – The API consumer may request the response be in a particular content type (e.g., JSON, XML, Javascript) in the Accept header. The API provider must first confirm whether the content type requested is safe and supported and ensure the Content-type header is appropriate and not just a copy of the Accept header. For example, the consumer may request Javascript to be returned, but the API provider should return JSON instead.

Error data – Effort must be taken to ensure that unnecessary backend system information (e.g., system names, IP, IDs, stack dumps) are not presented back to the API consumer. Error data is a common area where this is overlooked as it is easier to simply forward the system exception as a part of the response. System errors should be handled internally to the API and error responses should only contain information pertinent to the API consumer.

Virus Protection

APIs accepting attachments, BLOB, or binary payloads (e.g., image upload, documents) are at risk of virus attacks. Most API Gateway products provide virus scanning capabilities, but should be noted that if the API payload are encrypted, the Gateway must be able to decrypt the payload in order to scan it. It is not recommended to handle virus protection within the API code as virus scanning should occur as close to the perimeter as possible.

Page details

Date modified: