How To Submit Your Security Tokens to an API Provider, Pt. 2
This post was originally published as “How to Submit Tokens to an API Provider, Pt 2” on the Apigee Blog.
In a previous post, I discussed a variety of considerations regarding how bearer tokens should be passed from an API consumer to an API provider. I explored two approaches to client-side bearer token storage: cookies and HTML5 local storage. Here, I’ll look at the implications that these two approaches pose for native mobile apps, traditional web apps, and single page applications (SPAs).
Of course, there are plenty of things that don’t fall perfectly into one of these categories (for a more detailed analysis of the evolution of web applications, see this.), but mobile, web, and SPAs comprise a large proportion of the use cases.
Most concerns come down to XSS vulnerabilities and cross-site request forgery (CSRF) attacks. Of course, if the device or server-side components have been compromised in some way, then this entire discussion is moot.
SPAs
If HTML5 local storage is used, then the token is passed in the authorization header. Unlike with cookies, information stored in local storage is not automatically transmitted to the server (this does place an additional burden on the developer, but can be mitigated with supporting libraries).
Attack vectors include:
- XSS: If an attacker successfully gets valid JavaScript inserted into input to your API, then later, when it is retrieved, it could be interpreted and executed by the browser’s JavaScript engine, which in turn could access the bearer token from local storage (because the script was technically loaded from your site and API endpoint). The standard defense against XSS attacks should be sufficient in this situation. Escape all HTML characters that are used to delimit JavaScript in HTML (&, <, >, “, ’,/) with HTML entity encoding (&, <, >, ", ', /) referenced in input and other steps recommended by OWASP (see OWASP recommendations). Not all of these are relevant for API endpoints; for example, APIs typically don’t generate HTML. There is gn support for most of these recommendations built into modern web frameworks.
- CSRF: Conventional wisdom suggests that this is not an issue because the attacking code would not have access to the JWT in the HTML5 local storage for the API endpoint. I’m not going to debate this or dissect the notion here, but let’s assume that all interaction between the SPA and its backend API use standard anti-CSRF patterns as described by OWASP. They describe two steps that should be taken: verify the same origin with standard headers (or a known origin), and require some type of randomly generated value be presented with each request that wouldn’t be known to the CSRF instigator. In a stateless REST API, both of these can be challenging. But many of the major frameworks provide support for implementing this functionality.
- JavaScript from site A accessing data stored in local storage for site B: Not possible based on the security model implemented in browsers. This includes access by code on sub-domains.
If HTTP cookies are used for storage and transportation, then the attack vectors of concern are:
- XSS: The token value stored in a cookie cannot be accessed by JavaScript injected as bad input and later interpreted in the browser. Nevertheless, use the same mitigation patterns described above.
- CSRF: This is possible. Any request that is made (regardless of what’s triggering it) to the API from a browser session will include all cookies that are defined for the API endpoint. In this case, the mitigation strategies described above are absolutely imperative.
- JavaScript code from site A accessing cookies from site B By default, not possible, but CORS can override this.
Mobile apps
A native mobile application doesn’t run in a browser. It likely uses a library that either acts as a user agent to interact with the identity provider or launches the system browser to handle the IdP login workflow interaction. The library used to handle IdP interactions and login shouldn’t be a general purpose JavaScript engine (although it is likely launching the system browser that is). The login workflow that the browser interacts with should be a self-contained entity that isn’t relying upon external JavaScript sources.
Likewise, a native mobile app isn’t going to have local session storage. The use of cookies is limited to the HTTP client library or framework used to make API calls. The best option for securely storing a bearer token on each mobile platform is beyond the scope of this article.
Let’s assume that the API the mobile application is interacting with is the same one that is utilized by a SPA application and other API consumer actors. Attack vectors include:
- XSS: If the native mobile app is using a component that has a JavaScript interpreter, then this is possible. In this case, the mitigation strategies described in the previous section should be utilized.
- CSRF: This is only possible if a library is being use that includes cookies automatically. So, it depends. However, as always, the mitigation strategies described in the previous section should be used.
Traditional web application
For our purposes, the difference between the SPA above and the traditional web application hinges on whether the server returns full HTML pages or JSON objects (or maybe XML). For the most part, the information provided in the SPA section applies here with the following exceptions.
If HTTP cookies are used for storage and transportation, then the attack vectors of concern are:
- XSS: Same patterns described above should be used.
- CSRF: Synchronizer (CSRF) token patterns can be used with the stateful security model.
- Can JavaScript code from site A access cookies from site B: Comments in the previous section apply here.
So which approach should be used?
As I’ve mentioned in other posts, I always fall back on a standards-based approach to security. This implies that per RFC 6750, the bearer token should be placed in the HTTP request authorization header for each API call and the token stored in HTML5 local or session storage (for brower-based applications). For many IdPs, libraries will be provided that abstract these details away.
Is this the only way of accomplishing the desired effect? Obviously, no. But in my attempts to implement standards-based security solutions, it‘s the approach I recommend. Appropriate defense strategies for XSS and CSRF must be used, and, thankfully, can be largely accomplished with functionality in popular frameworks.