Secure API for both mobile and web
Asked Answered
P

1

6

I have three applications:

  • REST API
  • Single Page Web App
  • Native Mobile App.

Both the web app and the mobile app uses the API for user authentication and fetching user specific data.

My problem is then how to secure this API against CSRF, XSS and other types of attacks.

As I understand, there are two common authentication strategies that each has pros and cons:

  • Token based authentication
  • Session/cookie based authentication (secured and httponly)

Session/cookie based authentication

Cookies are vulnerable to CSRF attacks, so to protect against that i would need to implement CSRF tokens, or a similar protection strategy. For a secure CSRF token implementation I should also setup proper CORS policy, so foreign sites can't get a CSRF token from the API. This is also good and well for the web app, however not possible for the mobile app, as mobile apps doesn't support CORS (it has now domain or origin header).

Token based authentication

The alternative is token based authentication, which works well for mobile apps, and removes the need for CSRF tokens as they themselves validates the authenticity of the request. However there are no secure way to store tokens in a Single Page Web App (locale/web storage is not secure, and if I use cookies, we are basically back to our first problems).

Problem

So my problem is, how should i implement secure authentication for both apps?

My current idea would be to implement both strategies but only allow token auth when origin is not present, and only allow session/cookie auth when origin header is present and allowed by the CORS policy.

But I'm in no way an expert on the subject, and might easily have misunderstood something. Any suggestions or further explanations would be very welcome :)!

Pentyl answered 11/1, 2019 at 11:15 Comment(0)
H
12

Authenticity of the request

The alternative is token based authentication, which works well for mobile apps, and removes the need for CSRF tokens as they themselves validates the authenticity of the request.

Auth tokens cannot be trusted to validate the authenticity of a request, because they only identify the user, not that is the genuine mobile app doing the request.

An attacker controlling the device where the mobile app is running can extract the auth token to automate the requests to the API server. Another technique used by attackers is the creation of a fake captive portal for free wifi(airports, train stations and other public spaces), where they trick who signs in to install a custom ssl certificate in their mobile device, so that the attacker can decrypt the https traffic, thus stealing the auth tokens and perform automated requests to the API server in behalf of the user.

Implementing both strategies

My current idea would be to implement both strategies but only allow token auth when origin is not present, and only allow session/cookie auth when origin header is present and allowed by the CORS policy.

This can be easily bypassed and automated by an attacker. So the attacker would only make requests with the stolen token and without using the origin in the headers of the request, thus avoiding the security for web and bypassing the ones for a mobile app.

Suggestions

But I'm in now way an expert on the subject, and might easily have misunderstood something. Any suggestions or further explanations would be very welcome :)!

I will recomend you to read this series of articles about Mobile API Security techniques, that will give you good insights in how to protect an API. In the article we can see how api-keys, HMAC, certificate pinning, OAUTH can be used to secure the API and also how they can be bypassed. While in the scope of a mobile API some of the techniques are valid in the context of a web app.

For the the web:

Use the Strict Transport Policy header to guarantee that your web app is always loaded over https.

Your web app should use CSP(Content Security Policy) with a report service that will let you know in real-time when any of the policies is violated.

If using cookies you should enable httpOnly flag to protect them from being accessed via javascript. Further more you want to enable the secure flag for cookies to be sent only hover a https connection. Also try to scope cookies by the path they belong to, i.e. cookies for a login page should be scoped to the /login path, thus they will not be sent for other pages or assets(images, css, etc.) of the the web app.

Add recAptcha V3 to all pages of your web app. It runs on the background without any user interaction required and provides a score from 0 to 1, where towards 1 we are more confident that is a human making the request.

Quoting Google:

We are excited to introduce reCAPTCHA v3, which helps you detect abusive traffic on your website without any user friction. It returns a score based on the interactions with your website and provides you more flexibility to take appropriate actions.

That score will allow to have some degree of confidence in blocking non human traffic. If you need more confidence then you may want to use also User Behaviour Analytics(UBA) solutions, that may use machine learning and artificial intelligence for further analysis of the incoming traffic and detection of abusive traffic. Due to the way the web works, both reCaptcha V3 and UBA are not able to provide bullet proof solutions to authenticate requests as legit ones.

For Mobile Apps:

Use a Mobile App Attestation solution to enable the API server to know is receiving only requests from a genuine mobile app.

The role of a Mobile App Attestation service is to guarantee at run-time that your mobile app was not tampered or is not running in a rooted device by running a SDK in the background that will communicate with a service running in the cloud to attest the integrity of the mobile app and device is running on.

On successful attestation of the mobile app integrity a short time lived JWT token is issued and signed with a secret that only the API server and the Mobile App Attestation service in the cloud are aware. In the case of failure on the mobile app attestation the JWT token is signed with a secret that the API server does not know.

Now the App must sent with every API call the JWT token in the headers of the request. This will allow the API server to only serve requests when it can verify the signature and expiration time in the JWT token and refuse them when it fails the verification.

Once the secret used by the Mobile App Attestation service is not known by the mobile app, is not possible to reverse engineer it at run-time even when the App is tampered, running in a rooted device or communicating over a connection that is being the target of a Man in the Middle Attack.

The Mobile App Attestation service already exists as a SAAS solution at Approov(I work here) that provides SDKs for several platforms, including iOS, Android, React Native and others. The integration will also need a small check in the API server code to verify the JWT token issued by the cloud service. This check is necessary for the API server to be able to decide what requests to serve and what ones to deny.

Possible Solution

So my problem is, how should I implement secure authentication for both apps?

For the web app:

  • OpenID or OAUTH2 for both mobile and web app and maybe using a cookie to store the auth token, scoped by url path, with httpOnly and the secure flag enabled.
  • Further use strict CSP policies along side with CORS, CSFR, Strict Transport Policy and any other I may be missing now.
  • Google reCaptcha V3.

For the mobile app:

  • OpenID or OAUTH2.
  • Mobile App Attestation solution.
  • Certificate Pinning.

The API will only accept a request that contains a header with the reCaptcha V3 token or with a mobile app attestation JWT token. Any other requests should be denied.

In the case of a web app to proceed further with the request the recCaptcha V3 score(0 to 1.0) should give the confidence that the request comes from an human, maybe greater than 0.9? You will need to play with the value and monitor it in order to find the correct balance.

For a request from a mobile app to be able to continue being processed, the API server must verify that the JWT token have a valid signature and is not expired.

While the web requests are verified in a best effort basis the requests coming from a mobile app protected with the Mobile Attestation service have only 2 possible outcomes, valid or invalid.

Hermelindahermeneutic answered 14/1, 2019 at 12:23 Comment(6)
Thank you so much for an elaborate answer!!Pentyl
@Pentyl welcome to Stackoverflow and thanks for your kind comment. Please always remember to upvote the answers you like and later to mark the answer that best solves your question as the accepted one.Hermelindahermeneutic
@Hermelindahermeneutic A well documented answer/solution. A quick question what if a user changes his/her password? Should all tokens be invalidated on all devices. And does the mean keeping track of tokens on per a device level?Nyeman
Yes, you should invalidate all current tokens for user authentication once he changes his password, and for that you need to keep track of all issued tokens in your backend .Hermelindahermeneutic
@Hermelindahermeneutic thanks. With the approach you discussed does having a short live access and a long live refresh token apply here as well. Where the access token can be used to authorize a user to certain resources and when it expires use the refresh token to retrieve an access token?Nyeman
The propose of refresh tokens is to keep access tokesn with very short expiration times. Let's say you have your access token expiring every minute, and your refresh token expiring in hours. So when the refresh token expires the user is prompted to enter the credentials again.Hermelindahermeneutic

© 2022 - 2024 — McMap. All rights reserved.