7

Is it safe to use the following stateless authorization mechanism between a client (iOS & Android) and server?

Sign up

  1. The client provides an email and password and saves the clear password on the Keychain of iOS and using some alternative for Android.

  2. The server checks the password strength if it's deemed strong enough the user is created on DB.

  3. The server generates a JWT token and returns it to the client. The token has an expiration time of 15 minutes.

  4. The client stores the token (maybe on the Keychain itself) and includes it for each following request on the Authorization header.

  5. For each request, the server checks the token provided (checks the signature and the expiration time). If it's ok the request is processed, otherwise, an HTTP 401 is returned.

Sign in

  1. When the client receives an HTTP 401 from the server it means a login is required. So the app accesses to the Keychain and gets the email & password and sends it to the server (no user intervention needed).

  2. The server validates the credentials provided and if they're valid it will repeat the Sign Up steps from 3 to 5.

Thanks to expiration time on the token, if a token is compromised it will be valid during a short time period.

If a user is logged on multiple devices and she changes her password from one device, the other devices will keep logged only for a short time period, but the clear password stored on the Keychain will not be longer valid. So a new manual login will be required, which I think it's fine.

Which drawbacks do you see?

I've been thinking on using refresh token procedure to avoid store the clear password but this adds complexity and other drawbacks (for example: how to guarantee the refresh token is only used once). And as far as I've seen, storing the clear password on the KeyChain is secure enough:

KeyChain Services Documentation

What is the best way to maintain the login credentials in iOS?

But I also have seen other questions that do not recommend storing passwords on the device.

So I would like to hear opinions from others on this.

hft
  • 4,910
  • 17
  • 32
Ignasi
  • 123
  • 5

2 Answers2

1

This is a well-asked question, and as the question itself lays out, even experts will have different opinions in the context of absolute/abstract framings of safety.

The nuanced reality of course is that there is no "absolutely safe," only varying degrees of unsafe, ranges of which may be more or less appropriate in any given specific situation.

In the absence of additional information, some starting point questions to help brainstorming/threat modeling:

  • What data do these credentials protect?

    At one end of the spectrum, the app could be a game, the credentials protecting non-sensitive game state. On the other end, credentials could be protecting sensitive medical information.

  • Who are your users?

    Are your users unsophisticated folks who may use the same password across many services, including your own?

    Might your users be members of an identifiable group that may be more or less subject to having their devices compromised/searched/cloned (e.g. journalists, high level corporate staffers)?

  • What might happen to individual users and your community as a result of a compromise?

    Can an actor who compromises one user's account under this app compromise other users, or steal money, or perform some other permanent, expensive damage?

You get the idea.

The one additional thought is that a password is fundamentally an especially powerful credential that is expected to work anywhere from any device at any time.

One alternative to storing the password on the device is to generate a long term, device-specific token, to be saved on the device instead of the password, and used to re-authenticate only from that device. This token doesn't have to be a single use "refresh" token- sometimes applications hash the password some large number of times and use that.

Certainly support for any additional authentication scheme requires additional complexity. Circumstances can help determine whether it's worth it. In more sophisticated situations, compromise scenarios can have likelihood estimations, and loss estimates, along with development, maintenance and support cost ranges, and Monte Carlo methods can be used to help improve the intuition on whether individual bits of complexity offer protections- by reducing risk and loss- that may be worth the long term costs.

Good luck!

Jonah Benton
  • 3,359
  • 12
  • 20
  • I was expecting more specific answers like "Do not store password on the client because this and that" or "A drawback of using JWT tokens is: all the session authentication depends on a secret key". But If I understand you, I should not expect answers like this because there is no "absolutely safe", so any alternative will have its own problems. Is that right? – Ignasi Jun 25 '18 at 13:22
  • 1
    That's my view- plenty of people will provide absolute rules, but then, as the well-asked question frames, these absolutes- from appropriate authorities- turn out to be seemingly in conflict. So the nuanced answer unfortunately is- it depends- both on the problem- the nature of the app/data and the impact of loss/compromise/etc- and on the capabilities of the solving team. The fundamental issue is about accepting risk. Some teams should not accept any risk and just use the simplest available APIs, vs some teams/projects are actually about shifting risk from customers onto themselves. – Jonah Benton Jun 25 '18 at 13:55
1

Which drawbacks do you see?

The drawbacks I see have mainly to do with what is not explicitly stated in the question rather than what is explicitly stated. In general, you have not stated what kind of attacker you are interested in protecting against? An attacker snooping on a local network? A local attacker?

The client provides an email and password and saves the clear password on the Keychain of iOS and using some alternative for Android.

Ok, so let's assume we are not worried about a local (same machine) attacker/process who has already pwned our phone. If we were worried about this type of attack we could also use a physical MFA device (RSA fob or whatever). In this case I'm not particularly worried about the data at rest on the phone...

The server checks the password strength if it's deemed strong enough the user is created on DB.

How did the password get from the client to the server? If it going from client to server over https then things should be fine. If it is sent unencrypted then that is a bad thing.

How does the server do the checking? What is the password being checked against? The server should not store a plaintext copy of the password, rather the server should only store a cryptographically strong hash of the password (just use bcrypt).

Also, is the server doing the strength checking every time? Why? Just hash and compare to the stored hash.

The server generates a JWT token and returns it to the client. The token has an expiration time of 15 minutes.

Again, all data in transit should be encrypted, including of course the JWT token. As per the update, we are using HTTPS so as long as the TLS channel is really secure there should not be a problem (since we are ignoring a local attacker as above).

On the other hand, if there is the possibility that the JWT token can be stolen then some other considerations apply. For example, you should make sure that the key being used to sign the JWT token is sufficiently complex so that it can not be brute forced.

Sign in

When the client receives an HTTP 401 from the server it means a login is required. So the app accesses to the Keychain and gets the email & password and sends it to the server (no user intervention needed).

The client only connects to one known server? How is the server identified? Is it just via a domain name? What it someone highjacks the domain or messes with the hosts file? Also, what if there is a man-in-the-middle who sends back 401, will he then receive the email and password as the server would?


Update: Per the comments, HTTPS is being used.

As such, the client and server communicate using a channel secured via TLS. The server (set up by you, presumably) sends a certificate to the client. This certificate contains information identifying the server as well as the server's public key. However, there is no reason you should trust this certificate unless it is signed by a party you trust (and whose certificate you already have embedded in the client device trust store, which is used to check the signature). For example, the server certificate could be intercepted and replaced by a MITM attacker.

Therefore, in order to really have a secure HTTPS channel you need your server certificate signed by a third-party that is already known and trusted by the client machine. For example, for Apple there is a list of trusted parties (https://support.apple.com/en-us/ht204132).

Since you are also (presumably) writing the client app, you could also embed the known certificate or public key in the app itself and confirm the server certificate against the embedded certificate. This is called certificate pinning.

You can also consider using HTTP Strict Transport Security to further increase the security of the application.

hft
  • 4,910
  • 17
  • 32
  • All communication is through `HTTPS`. The password is stored on our database hashed using Bcrypt. The password strength is checked during registration not during normal login. The server is identified via domain name (I don't know if the certificate used during the `HTTPS` handshake also identifies the identity of the server). I haven't though about the kind of attackers that I want to protect, @Jonah Benton has suggested something similar so I get the idea, thanks for your help – Ignasi Jun 26 '18 at 07:50
  • make sure you use the certificate (a trusted certificate) to verify the identity of the server. Don't just rely on the hostname. You can use certificate pinning as well. You can also use HSTS. – hft Jun 26 '18 at 19:13
  • added some JWT token consideration to the answer as well. – hft Jun 26 '18 at 19:43