I have a difficult to nail down/solve problem.
I am creating a system that has two parties: The User wants to perform and authorize an action , and the second party that needs to confirm this action has been authorized.
The problem is, only the user that wants to perform the action will have an iphone (in many cases) and thus the ability to get authorization from our server. The second party does not have access to an internet connection, and will not be able to directly confirm that the first user's request was authorized.
So, my problem is... what sort of system/item could I use to ensure that the second party can confirm that the first party's mobile phone action was authorized by our server?
So far, all of the ideas I have come up with can be subverted by a clever hacker that just duplicates what the second party expects to see as a confirmed request. Things like... giving each second party a unique code that needed to be entered on the first party's mobile app in order to receive confirmation. But the clever user could just create a hacked app that mimics what the real app would do. Obviously, that would be a problem because if the second party believes that the action was authorized when it actually wasn't (due to hacking), they would lose out on money/time/etc...
Here is a diagram that may make my question a bit clearer:
Any ideas would be greatly appreciated.
***Edit: I wanted to add - Party 2 does not have access to a mobile device/computer in some cases, so I need to figure out some piece of hardware that could be deployed cheaply but also perform the role of verification.
If I understand your problem correct; you have a trusted server that can authorize an action for Party 1. That is, the server can decide if Party 1 should be allowed to perform an action. You may call this an authorization service. Party 1 then needs to prove that he was authorized to do this action for Party 2. Party 2 cannot contact the authorization service directly to verify this claim.
The natural solution to this is to let the authorization server issue a digitally signed authorization claim to Party 1. This claim can be i.e. an xml that describes what kind of authorizations Party 1 has. The claim must be digitally signed with a private key that only the authorization server knows. Party 1 recieves this claim from the server and send the claim to Party 2. Party 2 verifies the digital signature using the authorization server's public key. If the digital signature is correct, then Party 2 knows that the claim is valid and what kind of autorizations Party 1 got from the authorization server.
To do this, you must generate a key pair on the authorization server and distribute the public key to each instance of Party 2. This only has to be done once. The public key is not a secret, but it is very important that it is the correct key and that no one with malicious intentions can replace the public key at Party 2 with a fake key.
You will need some kind of device at Party 2 that is able to validate a signed claim. Without that, I don't think this scenario is possible to solve.
Related
The Stage
I'm working on a web application to enable trade of assets between different parties.
The different parties are known to me, and are not too many.
I record each transaction of an asset from one party to another in a database (sql server). I've stripped down database access as much as I can, and parties have to identify themselves by usernames and passwords to initiate transactions. Communication from web to api will of course be secure (https).
The Problem
These measures leaves me pretty confident that the stored transactions are authentic, but of course the parties of the transactions does not necessarily trust that I (or anyone else with sa password) haven't tampered with the data or created fake transactions.
My thinking
So I figure I need something to be able to prove the authenticity of such a transaction.
Maybe a signature from the party who is giving away their asset? A signature that I can store along with the signed transaction? In that case, the signer needs a private key and I think certificates is a way do this, right? I don't know of any way to access certificates installed on a computer from a web page though so the web page would have to launch a locally installed application to do the actual signing, because the locally installed application could access the certificate, and the signing party have trust in this application to sign the actual untampered-with transaction.
Is this a feasible solution?
How should certificates be distributed? I guess they should be issued from a CA that both I and the party trust?
In the OpenID Connect spec the azp (authorized party) claim seems to have a contradiction.
In the ID token definition section 2 it says:
azp
OPTIONAL. Authorized party - the party to which the ID Token was issued. If present, it MUST contain the OAuth 2.0 Client ID of this party. This Claim is only needed when the ID Token has a single audience value and that audience is different than the authorized party. It MAY be included even when the authorized party is the same as the sole audience...
But in token validation section 3.1.3.7, one of the steps seems to say the opposite:
If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present.
Could anyone shed some light on this apparent discrepancy? Only the second instance uses declaratory language, so I'm leaning towards favoring that in my implementation.
You're correct, the whole azp situation in OIDC is confusing. For what's it's worth, they have an open issue associated with it; see OIDC - Issue 973 (azp claim underspecified and overreaching).
From the definition of "aud" in JWT and its use in Connect's ID Token (relevant spec text is copied below), it seems that that the client id of the client/RP that made the authentication request has to be one of the values, or the only value, of the "aud" claim in the ID Token. That's logical and consistent and provides reliable and interoperable guidance to implementers about producing and consuming the ID Token. I think that the client id of the RP/client that made the authentication request should always be represented in the aud of the returned ID Token.
The text around "azp" in the ID Token section and the ID Token Validation section seems to maybe suggest something different, however. Like perhaps that the client id of the RP/client that made the authentication request could, in some totally unspecified circumstance, be the value of the azp claim and that the aud would not identify that client as an intended recipient. Am I misinterpreting things?
Personally, from the client application developer perspective, the best course of action seems to be honoring the ID Token validation rules which always imply that the value within azp will also be present as an aud. However, according to what's available online Google seemed to use it a bit differently so you could have a value in azp not listed within aud, so there may be situations where you play by Google rules and not (just) OIDC.
If you're implementing an OP a probably good option is also to entirely stay away from including azp in your issued tokens if possible or only include it when using multiple audiences with one of them being the value also in the azp.
I think the two sections are not conflicting each other on the concept level (a bit confusing on the wording though).
if there is a single audience which is different from azp, it makes senses to have both azp and audience to show that difference. In a multiple-audience case, at least one of the audiences will be different from azp and hence azp must be present to make it clear.
azp usually equals to client id (the application requests the token). audience of the token can be the app itself or another application which receives the token for validation.
Preface
I'm developing several web services and a handful of clients (web app, mobile, etc.) which will interface with said services over HTTP(s). My current work item is to design an authentication and authorization solution for the product. I have decided to leverage external identity providers, such as Facebook, Google, Microsoft, Twitter, and the like for authentication.
I'm trying to solve the problem of, "when a request comes to my server, how do I know who the user is and how can I be sure?". More questions below as well...
Requirements
Rely on external identities to indicate who I'm dealing with ('userId' essentially is all I care about).
The system should use token-based authentication (as opposed to cookies for example or basic auth).
I believe this is the right choice for scaling across multiple clients and servers while providing loose coupling.
Workflow
Based on my reading and understanding of token-based authentication, the following is how I imagine the workflow to be. Let's focus for now on Facebook in a web browser. My assumption is that other external identity providers should have similar capabilities, though I have not confirmed just yet.
Note, as of writing, I'm basing the following off of Facebook login version 2.2
Client: Initiates login to Facebook using the JavaScript SDK
Facebook: User authenticates and approves app permissions (to access user's public profile for example)
Facebook: Sends response to client which contains user’s access token, ID, and signed request
Client: Stores user access token in browser session (handled by SDK conveniently)
Client: Makes a request to my web service for a secure resource by sending along the user’s access token in the authorization header + the user’s ID (in custom header potentially)
Server: Reads user access token from request header and initiates verification by sending a request to the debug_token graph API provided by Facebook
Facebook: Responds back to the server with the user access token info (contains appId and userId)
Server: Completes verification of the token by comparing the appId to what is expected (known to itself) and the userId to what was sent on the client request
Server: Responds to the client with the requested resource (assuming the happy authorization path)
I’m imagining steps 5-9 would be repeated for subsequent requests to the server (while the user’s access token is valid – not expired, revoked from FB side, app permissions changed, etc.)
Here's a diagram to help go along with the steps. Please understand this system is not a single page application (SPA). The web services mentioned are API endpoints serving JSON data back to clients essentially; they are not serving HTML/JS/CSS (with the exception of the web client servers).
Questions
First and foremost, are there any glaring gaps / pit falls with the described approach based on my preface and requirements?
Is performing an outbound request to Facebook for verifying the access token (steps 6-8 above) per client request required / recommended?
I know at the very least, I must verify the access token coming from the client request. However, the recommended approach for subsequent verifications after the first is unknown to me. If there are typical patterns, I’m interested in hearing about them. I understand they may be application dependent based on my requirements; however, I just don’t know what to look for yet. I’ll put in the due diligence once I have a basic idea.
For instance, possible thoughts:
Hash the access token + userId pair after first verification is complete and store it in a distributed cache (accessible by all web servers) with expiry equal to access tokens. Upon subsequent requests from the clients, hash the access token + userId pair and check its existence in the cache. If present, then request is authorized. Otherwise, reach out to Facebook graph API to confirm the access token. I’m assuming this strategy might be feasible if I’m using HTTPS (which I will be). However, how does performance compare?
The accepted answer in this StackOverflow question recommends creating a custom access token after the first verification of the Facebook user token is complete. The custom token would then be sent to the client for subsequent requests. I’m wondering if this is more complex than the above solution, however. This would require implementing my own Identity Provider (something I want to avoid because I want to use external identity providers in the first place…). Is there any merit to this suggestion?
Is the signedRequest field present on the response in step #3 above (mentioned here), equivalent to the signed request parameter here in the ‘games canvas login’ flow?
They seem to be hinted as equivalent since the former links to the latter in the documentation. However, I’m surprised the verification strategy mentioned on the games page isn’t mentioned in the ‘manually building a login flow’ page of the web documentation.
If the answer to #3 is ‘Yes’, can the same identity confirmation strategy of decoding the signature and comparing to what is expected to be used on the server-side?
I’m wondering if this can be leveraged instead of making an outbound call to the debug_token graph API (step #6 above) to confirm the access token as recommended here:
Of course, in order to make the comparison on the server-side, the signed request portion would need to be sent along with the request to the server (step #5 above). In addition to feasibility without sacrificing security, I’m wondering how the performance would compare to making the outbound call.
While I’m at it, in what scenario / for what purpose, would you persist a user's access token to a database for example?
I don’t see a scenario where I would need to do this, however, I may be overlooking something. I’m curious was some common scenarios might be to spark some thoughts.
Thanks!
From what you describe I'd suggest to use a server-side login flow as described in
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2
so that the token is already on your server, and doesn't need to be passed from the client. If you're using non-encrypted connections, this could be a security risk (e.g. for man-in-the-middle attacks).
The steps would be:
(1) Logging people in
You need to specify the permission you want to gather from the users in the scope parameter. The request can be triggered just via a normal link:
GET https://www.facebook.com/dialog/oauth?
client_id={app-id}
&redirect_uri={redirect-uri}
&response_type=code
&scope={permission_list}
See
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#login
(2) Confirm the identitity
GET https://graph.facebook.com/oauth/access_token?
client_id={app-id}
&redirect_uri={redirect-uri}
&client_secret={app-secret}
&code={code-parameter}
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#confirm
(3) Inspect the access token
You can inspect the token as you already said in your question via
GET /debug_token?input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
This should only be done server-side, because otherwise you'd make you app access token visible to end users (not a good idea!).
See
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#checktoken
(4) Extending the access token
Once you got the (short-lived) token, you can do a call to extend the token as described in
https://developers.facebook.com/docs/facebook-login/access-tokens#extending
like the following:
GET /oauth/access_token?grant_type=fb_exchange_token
&client_id={app-id}
&client_secret={app-secret}
&fb_exchange_token={short-lived-token}
(5) Storing of access tokens
Concerning the storing of the tokens on the server, FB suggests to do so:
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#token
(6) Handling expired access tokens
As FB doesn't notify you if a token has expired (and if you don't save the expiry date and compare this to the current timestamp before making a call), it's possible that you receive error messages from FB if the token got invalid (after max. 60 days). The error code will be 190:
{
"error": {
"message": "Error validating access token: Session has expired at unix
time SOME_TIME. The current unix time is SOME_TIME.",
"type": "OAuthException",
"code": 190
}
}
See
https://developers.facebook.com/docs/facebook-login/access-tokens#expiredtokens
If the access token becomes invalid, the solution is to have the person log in again, at which point you will be able to make API calls on their behalf once more. The login flow your app uses for new people should determine which method you need to adopt.
I dont' see any glaring gaps / pit falls, but I'm not a security expert.
Once your server has verified the given token (step 8), as you said:
The accepted answer in this StackOverflow question recommends creating a custom access token after the first verification of the Facebook user token is complete. The custom token would then be sent to the client for subsequent requests. I’m wondering if this is more complex than the above solution, however. This would require implementing my own Identity Provider (something I want to avoid because I want to use external identity providers in the first place…). Is there any merit to this suggestion?
IMHO is the way to go. I would use https://jwt.io/ which allows you to encode values (the userId for example) using a secret key.
Then your client attach this token to every request. So you can verify the request without need to a third party (you don't need database queries neither). The nice thing here is there is no need to store the token on your DB.
You can define an expiration date on the token, to force the client authenticate with the third party again when you want.
Let's say you want your server be able to do some action without the client interaction. For example: Open graph stories. In this scenario because you need to publish something in the name of the user you would need the access token stored on your DB.
(I can not help with the 3 and 4 questions, sorry).
Problem with Facebook is that they do not use OpenId connect on top of Oauth (https://blog.runscope.com/posts/understanding-oauth-2-and-openid-connect).
Thus resulting in their custom ways of providing Oauth authentification.
Oauth2 with OpenId connect identity services usually provide issuer endpoint where you can find URL (by appending ".well-known/openid-configuration") for jwk's which can be used to verify that JWT token and its contents were signed by the same identity service. (i.e access token originated from the same service that provided you jwk's)
For example some known openid connect identity providers:
https://accounts.google.com/.well-known/openid-configuration
https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
(btw it is not a coincidence that Attlasian provides only these two services to perform external login)
Now as you mentioned, you need to support multiple oauth providers and since like Facebook not all providers use same configuration of oauth (they use different JWT attribute names, toke verification methods, etc. (Openid connect tries to unify this process)) i would suggest you to use some middleware identity provider like Oauth0 (service not protocol) or Keycloak. These can be used with external identity providers (Social pages as you mentioned) and also provides you with custom user store.
Advantage is that they unify authentication process under one type (e.g both support openid connect). Whereas when using multiple oauth providers with not unified authentication workflow you will end up with redudant implementations and need for merging different information's under one type (this is basically what mentioned middle-ware identity providers solve for you).
So if you will use only Facebook as identity provider in your app then go for it and make implementation directly for Facebook Oauth workflow. But with multiple identity providers (which is almost always case when creating public services) you should stick with mentioned workaround or find another one (or maybe wait till all social services will support Openid connect, which they probably wont).
There may be hope.. This year, Facebook have announced a "limited login" feature, which, if they were to add to their javascript sdks would certainly make my life easier:
https://developers.facebook.com/blog/post/2021/04/12/announcing-expanded-functionality-limited-login/
At the time of writing, I can only find reference to iOS and Unity SDKs, but it does seem to return a normal JWT, which is all I want!
I'd like to make requests from an iphone app to a web service I've built. How can I verify that requests made to the web service come from my iphone app (or indeed any authorised source) and are not forged?
I have looked at basic auth over HTTPS but is baking credentials into an application secure?
This question isn't really iphone specific; I'd like to know how to protect and authenticate requests in general.
Authentication can be asserted by presenting something you know, something you have, something you are or a combination of the three.
The iPhone doesn't have retinal or fingerprint scanners, so there are no "something you are" options available.
Client certificates work well as a "something you have" token. Most smartcards work by signing a message with an embedded certificate. When a certificate is compromised, it can be put onto a Certificate Revocation List (CRL) referenced by the webservers. Obviously, you wouldn't want to put your app's embedded certificate in the CRL -- that would deny access to all your users. Instead, you'll want users to download individual certificates to their iPhone.
After that, it's a matter of monitoring for unusual behavior to find the bad actors and adding those certs to the CRL. Two dead giveaways would be clients who send too many requests at once or from too many different IPs in too short a time.
Login/password is a simple "something you know" token. Like certificates, login/password combinations can be compromised and similar monitoring can be set up to find inappropriate behavior. The difference is compromised accounts would be marked "blocked" rather than added to a CRL.
By requiring both a client certificate and a login/password you increase the amount of effort needed to compromise an account.
Of course, you must ensure only valid accounts are added to the database. If there is an automated way to create new accounts and corresponding client certificates, then that account creation server/process becomes the easiest way for bad actors to create viable, unauthorized accounts. Requiring a real person to sign-off on accounts removes the automation process, but means a disgruntled or corrupt employee could create invalid accounts. Requiring a second person to counter-sign the account makes it harder for a single person to be an inside threat.
In short, ensuring high integrity of the clients is a process that can be made arbitrarily complex and expensive. What tools and processes you decide to deploy as the authentication scheme has to be balanced by the value of what it is protecting.
In theory, if you want the connection to be secure, the best is to have the client sign their request using a certificate. There are multiple resources about this. Look for "client certificate" on Google.
This example from Sun is in Java, but the concept is similar whatever the language.
PS: obviously, this doesn't prevent you from using other authentication methods such as passwords, etc...
PPS: Keep in mind that if someone manages to extract the certificate from your application, you are screwed either way ;-). You can imagine a store providing an individual certificate to each app and invalidating the certificates that are compromised.
I'm building a solution consisting of an app and a server. Server provides some methods (json) and the app uses them. My aim is to make those API methods inaccessible to other clients. What is the best way to do so?
Should I take a look at certificates (to sign every outgoing request)? If yes, where do I start and what is the performance impact of doing so?
What are alternatives?
Put another way, you need a way to distinguish a valid client's request from an invalid client's request. That means the client needs to present credentials that demonstrate the request comes from a valid source.
SSL certificates are an excellent way to assert identity that can be validated. The validity of an SSL certificate can be confirmed if the certificate contains a valid signature created by another certificate known to be secure, a root cert. As noted in other answers an embedded certificate won't do the job because that certificate can be compromised by dissecting the app. Once it is compromised, you can't accept any requests presenting it, locking out all your users.
Instead of one embedded app cert, you need to issue a separate certificate to each valid user. To do that, you need to set up (or outsource to) a Certificate Authority and issue individual, signed certificates to valid clients. Some of these certificate will be compromised by the user -- either because they were hacked, careless or intentionally trying to defraud your service. You'll need to watch for these stolen certificates, place them on a certificate revocation list (CRL) and refuse service to these compromised certificates. Any web server is able to refuse a connection based on a CRL.
This doesn't solve the security issues, it just moves them out of the app. It is still possible for someone to create what appears to be a valid certificate through social engineering or by stealing your root certificate and manufacturing new signed certificates. (These are problems all PKI providers face.)
There will be a performance hit. How much of a hit depends on the number of requests from the app. The iPhone NSURLConnection class provides support for SSL client certificates and client certificates can be installed in the phone from an e-mail or authenticated web request. Managing the infrastructure to support the client certs will require more effort than coding it into the app.
Incidentally, voting down any answer you don't like creates a chilling effect in the community. You're not nearly as likely to get advice -- good or bad -- if you're going to take a whack at everyone's reputation score.
I will now freely admit that it's an interesting question, but I have no idea how it could be done.
Original answer:
Interesting question. Assuming people can't reverse-engineer the iPhone app, the only solution that comes to mind would be to sign requests with a public key, or some other secret known only to the application. By that, I mean adding an extra argument to every API call that is a hash of the destination URL and other arguments combined with a secret known only to your server and application.
To expand upon this: suppose your API call has arguments foo, bar and qux. I would add a signature argument, the value of which could be something as simple as sorting the other arguments by name, concatenating them with their values, adding a secret, and hashing the lot. Then on the server side, I would do the same thing (excepting the signature argument) and check that the hash matches the one we were given in the request.
Consider authenticated HTTP.
For a cheaper alternative, there's shared secret/hash scheme. The client and the server have a shared secret string of text. Upon request, the client hashes together (using MD5, or SHA1, or SHA something else - you choose) the request fields and the secret. The hash value is attached to the request - say, as another POST field.
The server does the same operation with the request and with its copy of the secret, then compares the hash values. If they don't match - service denied.
For added security, you may encrypt the hash with a RSA public key. The client has the public key, the server keeps the private key. The server decrypts the hash with the private key, then the same. I did that with a C++ WinMobile client and a PHP-based service - works like a charm. No experience with crypto on iPhone, though.
UPDATE: now that I think of it, if we assume that the attacker has complete control over the client (ahem jailbroken iPhone and a debugger), the problem, as formulated above, is not solvable in theory. After all, the attacker might use your bits to access the service. Reverse-engineer the executable, find the relevant functions and call them with desired data. Build some global state, if necessary. Alternatively, they can automate your UI, screen scraper style. Such is the sad state of affairs.