Note: Although I raise this issue in the context of an iOS app, I don't think it's confined to an app running on that specific OS.
I'm developing an iOS application that will back up user data to a server and I'm trying to figure out the best way to verify server-side that the user being updated is actually the real user. Each user will have an id (uid). If that's all I depended on server-side, then I imagine the process would go like this:
User runs app for the first time
Creates account in the app, which communicates with the server to both create the account on the server and to get a unique "user id" (uid)
App stores this uid so that it can identify the user in subsequent communications with the server
However, if someone were to hack the app on their iphone, they could change the user id value and then that would instantly give them access to/allow them to modify a different user's data.
The current solution I'm considering is that the user receives 2 unique ids, the uid (just an auto-incremented number) and a longer, more complex key string. All communication with the server will therefore have to send along both the uid and the key. The server will verify that they match in order to make sure that the user truly is who the app says it is.
So, my question is two-fold:
Is this the correct way to achieve this? Or is there some other standard method that I should pursue?
If this is the correct approach, what's the recommended way to generate the unique key?
First of all, you can use the more complex value as the user ID to begin with, if you like (e.g. a UUID). Monotonically increasing IDs get hard to manage as your service scales.
You have the same problem a secure web site does when it leaves secure cookies on the browser to remember a session. Those cookies do include the user ID, but must prevent tampering. This is generally done by signing the cookie on the server before sending it back.
So what you'd do is:
Generate the user ID on the server, and use it to create some sort of "auth token" for the client to have to sign in.
Sign the auth token on the server with a secret key that only your server knows.
Send the auth token to the client, where it is stored for all subsequent logins. Transfer the auth token over HTTPS to prevent someone else from snooping it on the network.
When the app goes to login, send up the auth token to the server. If it's been hacked, the signature validation will fail, and you'll know to reject the client.
Consider including a timestamp in the signed token as well, so it expires after some time, forcing the server to regenerate an auth token periodically, which protects you in case your key is compromised. It's hard to do this all fully unless the user himself has a shared secret/password he can use to authenticate periodically as well. Depends on how far you need to go.
Other considerations: If all you know about a user is their generated UID, you don't have any way for that user to come back later from a different iOS device and restore their account there, right? Generally, if the user will be creating anything "valuable" in their account that they'll want access to later, you'll probably want to create a more traditional user account backed by an email address and password or the like, so they can access the account again after reinstalling your app. (This may or may not be relevant to your case.)
I would recommend going the "standard web browser way" and just letting the user set an email (login) and password.
When the iOS device connects to the server (using HTTPS), it uses regular "basic authentication" to log in, and receives a cookie which is valid for a set period of time. As long as the device keeps requesting data from the server within the cookie's lifetime, the cookie is renewed, and when the cookie is expired the server will automatically challenge the client to log in using its stored information again.
This has some advantages;
The user can log back into his account with a new device with a regular password reset. Easy, straight forward solved problem.
There is no special solution on the server side, any server side script can require authentication just like it would for a browser - built in functionality.
You would not have to invent your own security scheme. This scheme is used by millions of browsers every day to authenticate to web sites.
Not tied to a special phone, if the user has several iOS devices, he can use the same account from all of them by just logging in. No special set up procedures.
In other words; no special solutions for you to develop, generally solved problems how to handle login information, proven security and ease of use.
According to me, you can't really beat that :)
Related
It appears to me that JWT based authentication is currently the state-of-the-art way of authenticating a user.
Spring security allows for an easy configuration of the security mechanism by overriding the configure(HttpSecurity http) method of the WebSecurityConfigurerAdapter. Inside this method, I can e.g. set the login method to formLogin. From what I understand, using the formLogin authentication, the user needs to specify a username and a password for every request sent to the server. An alternative is to use JWT based authentication, in which case an access-token needs to be send to the server for every request. I don't see any benefit of the later over the former, so I was hoping someone could explain it to me?
im not going to explain ALL the difference, there are simply too many, but lets look at some:
Session based login (FormLogin)
You present your username and password to the server. The server then creates a session token. This token is in general just a random string. But before we give this string to the client, we also store the string on the server and we also store a boolean flag next to it telling us that the session is "logged in" or we usually call it authenticated.
The client takes this session token and sends it to us everytime it needs to make a request. Everytime someone then sends this token to us we check in our little storage are that we have a record of that token, and that the flag is set to true so.
So we check:
we created the session token
that the token is still logged in
When the client wants to log out, they just send us a logout request, and we just remove that record from our little storage.
Everything works fine so far. Lets introduce another server.
So first server authenticates the user with username and password, and we create a session cookie and return this to the browser, we also store it and everything is the same as before.
But this time we present the cookie (our session token) to server number two.
Server two has no idea what this token is. It has no store to check since it wasnt the one that created the token in the first place. So the server must then call the server that created the token, or the server that created the token must send over the session information to server 2 before.
And with that it becomes hard scale, because then servers need to constantly talk to each other asking things like "what is this token?", "is it valid?" if you have 100s of servers and millions of users, this is a problem.
Token based (oauth2 with JWTs)
In a token based world you have someone that authenticates a user (perhaps using username and password) and that someone issues a token. This service is usually called an issuer.
A JWT is cryptographically signed, which means that if we have a key, we can check that the token hasn't been changed or tampered with.
If we make sure all servers have the key, all servers can independently check that each token they receive is authentic. They dont need to ask the issuer if its authentic, because they can check themselves.
This way the server doesnt need to constantly ask the server that created the token "What is this token?, is it valid?" etc.
And thus we can now scale as long as we can make sure that all servers that need to verify tokens has the proper key to do the validation.
In this scenario no own has a complete record of all the tokens, which means its harder to log someone out. So instead you have to have tokens that are short lived.
Each token contains a timestamap for how long it is valid, and when its not the client needs to refresh this token.
And this is where things usually gets complicated.
So no they are not alike at all. Session based and token based are very different, and i cant explain to you all the differences but here where a few. If you want more information i suggest you read more about it on the internet.
I am trying to convert a Drupal 8 site to a progressive web app. I have cached all pages visited by a user using service worker. Is it possible to cache user login so that the user can start using the web app from login page when offline?
Whichever way you implement it, there is always going to be a security risk.
Having said that, similarly to a native application, while you cannot cache the login service for obvious security reasons, you could keep the user's logged in. This means that if they are not logged in, they are unable to login, but if they have already logged in previously while connected, then you could keep them logged in.
If the application is working offline and we require authentication then the risks are that someone gets a hold of the device. Since there is no traffic over the network, then that minimizes the attack surface and there isn't the need to worry about MitM attacks or someone getting a hold of the authentication cookie by sniffing.
I think it would help to understand your exact use for authentication while offline. If we are talking about a shipping cart (or different user journey) i would suggest storing some form of encrypted token (based on the user ID + salt) that would be used to recognize the user. These would be added upon successful login while connected to the internet and used to distinguish which user is currently accessing the site.
If you require authentication to gain access to some confidential data, then I would recommend that you require a connection to view that data so that confidential data is never stored on the device. If it is stored locally, then there is a security risk irrelevant of the authentication you have in place.
I have an app which requires users to log in. Logging in is handled via a POST request to my back-end REST API, which generates a new access token if the login details are correct and returns it to the app (which is then saved in the keychain). My question is, how do I handle cases when a user logs in from multiple devices at once? The device who logged in first will not realize that their access token has expired, but will continue to try to send requests which will be denied (because the access token no longer matches the one stored in the database). I have thought about checking that the access token matches the database token in viewDidLoad in my AppDelegate, but this would only work when the app is first loaded. Is there best practice already established for this sort of thing? If so what is it?
The best solution I have thought of so far is to just re-issue the same access token each time the user logs in, but this seems insecure to me. Is this a safe way to handle it?
It's a good practice to issue a new token on each sign-in. Tokens are meant to be expendable, and differentiating between user sessions on different devices and browsers is useful (see Facebook's session management for an example).
There are two possible ways to handle the double login scenario:
Maintain multiple active tokens for each user in your database and invalidate each on sign-out
Allow only one token per user, invalidating the existing token on sign-in
The choice depends on the nature of your application. In the second instance, the first client won't know that its session is invalid until the app makes a network call. At that time, your server should return a custom error to indicate that the token has expired. The app should then alert the user and return them to the sign-in screen.
I am reading a great Rails tutorial and came across a passage that I had a question about:
Box 9.2.Sessions and cookies Because
HTTP is a stateless protocol, web
applications requiring user signin
must implement a way to track each
user’s progress from page to page. One
technique for maintaining the user
signin status is to use a traditional
Rails session (via the special session
function) to store a remember token
equal to the user’s id:
session[:remember_token] = user.id
This session object makes the user id
available from page to page by storing
it in a cookie that expires upon
browser close. On each page, the
application can simply call
User.find_by_id(session[:remember_token])
to retrieve the user. Because of the
way Rails handles sessions, this
process is secure; if a malicious user
tries to spoof the user id, Rails will
detect a mismatch based on a special
session id generated for each session.
For our application’s design choice,
which involves persistent
sessions—that is, signin status that
lasts even after browser close—storing
the user id is a security hole. As
soon as we break the tie between the
special session id and the stored user
id, a malicious user could sign in as
that user with a remember_token equal
to the user’s id. To fix this flaw, we
generate a unique, secure remember
token for each user based on the
user’s salt and id. Moreover, a
permanent remember token would also
represent a security hole—by
inspecting the browser cookies, a
malicious user could find the token
and then use it to sign in from any
other computer, any time. We solve
this by adding a timestamp to the
token, and reset the token every time
the user signs into the application.
This results in a persistent session
essentially impervious to attack.
I don't understand what this is saying. I take from it that a unique session ID is created and stored on the client in a cookie. Then when that cookie is sent to the server on a request, the server knows that is the user in question so that the login can be persisted. However, if a malicious user stole the cookie, I don't understand why they can't log in from another computer. The author says this is solved by adding a timestamp, but I don't see how that helps. Further, the author says that the token is reset every time the user signs in, but the whole point is a persistent sign in, so I don't understand. Please help!
You are correct—a "Remember Me" cookie can be used to steal a login. The issue that they're trying to resolve are if someone steals your cookie, containing your unique identifier, and hangs on to it—they'd then be able to log into your account at any point in the future.
The usual solution is to invalidate all previous cookies every time that you log into your account using either the username/password or the "Remember Me" cookie, so that a given cookie will allow you to login a single time. The timestamp is how they're ensuring the uniqueness of each cookie.
If you're worried about cookies being stolen, a typical solution is to also store the IP address that the request came from, and if the IP address that the cookie is coming from doesn't match the IP address that the cookie was created from, deny the login and force the user to sign in. This can be inconvenient to users who are behind dynamic proxies, or who carry their laptop to and from work/home/coffee-shop, since their IP address will change all the time.
"Remember Me" is a security hole by design. The goal is to limit how much of a hole it is, and if you're designing a system that requires absolute security, it's not a good choice. If convenience is more relevant than security, using timestamps and cookie invalidation limits the potential security issues.
If you're interested in more information on this topic, the Security Guide section of Rails Guides has an entire section on sessions.
Goal:
Allow a user to authentication with Facebook into an iOS application which requires access to a protected web service that I'm running.
Assumptions:
There is a native authentication (and registration) system in place for those users that opt not to use Facebook for sign in.
Details:
Assume we want to offer the option for a user to sign in with Facebook without creating a separate account/credential for our system.
Because we support our own native auth mechanism (username and password) we have our own user IDs and issue an authentication token that is used for subsequent interactions after the initial credential validation.
I'm surprised that Facebook doesn't have best practices for this in their developer documentation. All the existing documentation is either assuming you are building FB auth into a website, or a standalone mobile app with no service that requires authentication.
Here's my initial thoughts on how this would be designed but want validation on whether it's correct.
Client pops the Facebook iOS Login
UI User signs in with Facebook credentials and gets access token
iOS App passes access token to our server
Our server talks to FB graph API using access token to (a) validate the token and (b) get the FB user ID for that access token.
e.g. Our server would call https://graph.facebook.com/me/?access_token=XYZ which would return profile info in a JSON object
Assuming it's valid, our server extracts the User ID from the JSON object and checks whether the user already has an account. If so, we issue our own auth ticket to client to use for that session. If user doesn't have an account, we create a new one with the Facebook User ID, assign our own unique UserID and issue our auth ticket.
Client then passes auth ticket back on subsequent interactions that need authentication.
This seems like the right approach to me but not sure if I'm missing something insanely basic and going down the wrong (complicated) path.
I just dealt with this myself, and here's the part that bit me:
In your step 5... It's possible for a user to register for an account with you entirely separate from their Facebook ID, right? Then some other time they log in with Facebook.... And you just created them a second account and lost their first one.
There needs to be a way to be logged in to your web service, then log in to facebook, and capture the association between the facebook ID and the local account.
Apart from that, your plan sounds solid.
Update: Facebook has added a doc outlining such a scenario HERE
Use https to transmit the auth token to your server, as stated by Facebook
Sharing of Access Tokens
Our Data Policies explicitly prohibit any sharing of an Access Token
for your app with any other app. However, we do allow developers to
share Tokens between a native implementation and a server
implementation of the same App (ie. using the same App ID) as long as
the transfer takes place using HTTPS.
One problem I can see with this strategy, is that somebody can give you an access token obtained for a different facebook app. As far as I know, there's no way to verify that the access token is for your application, so you'll just go on and use it.
It doesn't sound very harmful, though. Generally people/apps try to protect the access tokens, rather than sharing them.
One possible exploit of this would be, for somebody to create their own site or mobile app, obtain access tokens for their users and try to authenticate them, using your API. If this succeeds (the user is has a facebook account in your site), the malicious site will be able to use your API impersonating the user.
It's a bit of a long shot, but I think it could work.
Edit: It looks like there is a way to validate the access token after all. See the answer by #Daaniel on question Get application id from user access token (or verify the source application for a token).
your solution totally works.
Maybe an alternative: why not just get the email on the client from the initial social service request and send to your web service? The web service could just store the email, and maybe a social_provider as well. I understand that your web service will not be able to validate where the email came from, but isn't there a high-trust relationship between your web service and your client? If there is, seems like you can depend on the email coming from the right place. Someone please let me know what obvious thing I'm missing that makes the email-based approach silly...