Sending Device Token Safely for APNs - ios

For iOS applications that require push notifications, it must first request the user for permission to do so. After that, a device token is generated and with this, the remote server may communicate to the user through this token.
I have read a similar question here and I do not feel it is enough. The picture below is a trusted certificate, it allows me to view all traffic that happens on this device.
With Fiddler2 as well as CertMaker, I can sniff HTTPS traffic, which means the client can probably know what data they are sending, and to where.
My question is, knowing that SSL is not secure from protecting my clients from seeing what I send to the remote server, should I simply encypt with a secret key found within my application?
Such as encrypt("device_token","secretkey_a0a0a0a") (pretend this is Objective-C)?
Couldn't someone just find that key within my application? I also read this question, and it seems that it would be possible to get back the secret key.
My plan for this goes like this:
Within the iOS application, Generate a random string named activate.
Encrypt (not hash), the token by the random string and a secret key that I only know. (secretkey_a0a0a0)
Send the encrypted string along with the generated randomly generated string (active).
Within serverside, I check if I can decrypt a valid token from using the active and my secret key.
I save the token in my database if it is valid.
This prevents people from random entering tokens yes, however, secretkey_a0a0a0 is a string literal. It's very possible to get this within the application binary itself.
My question is, how do I protect this secret key? The answer can also be, how can I prevent people from sending invalid tokens to my server as well.
I have heard of encryption, but doesn't that only apply to resource files?
How should I approach this?

If you do SSL-Pinning ( AFNetworking has this implemented ) you won't be able to (in a reasonable timeframe) sniff the https traffic between the client and server if you don't have the servers private key.

If your fear is that man in the middle can steal your token and send fake push notifications to users of your application, be sure that this cant happend. Since requests to apple apn servers must be signed with pem file, the main concern should be how to keep certificate file secured, and not apn token. If you want to prevent writing invalid tokens in your database then you should implement some CRC or odd/even bit mechanism.

You might want to check the security section in the Push Notifications Guide, in particular the section titled "Token Generation and Dispersal".
The device token is generated by the device connecting through the Apple's APNS. My guess (they don't say in the docs) is that it's unique for a given app identifier.
The APNS then will probably match those identifiers with the pem certificate you use to communicate with it thus validating that the push notifications are actually originating from your app.
Encrypting the device token seems overkill in this scenario.

To prevent someone maliciously spamming your server with tokens, I would hash the token when a secret key and send both the token and the hash to the server. You can then hash the token again on the server, with your secret key, and check that the request is valid.

Related

How to authenticate Ios Server-to-Server Notifications

When we configure Server-to-Server Notifications, we Specify our secure server's URL in App Store Connect and the apple server communicates on that URL. but is there a way to authenticate this request?
It is not safe to keep url open without authentication
in case of PlayStore we can use GOOGLE_DEVELOPER_API_KEYFILE_JSON for authentication, but how to do this for iOS server-server notification?
As the comments have already clarified that there is no built in way.
So, here is my work around of this problem.
Apple sends password in the notification which is App secret key which ideal should only be known by API and Apple.
And to verify receipts coming from the App this password must already be stored somewhere (configuration?) in the API.
So I suggest to check whether the password in request matches with the one stored in our API?
If yes then this is a valid request.
If not then it may be sent by a hacker.
My only concern is that does this App shared secret key aka password change? by Apple or developer? If not than this is the solution.
One way to do it is to use Basic auth. As you cannot specify a header you can use the url format: https://username:password#SERVER_ENDPOINT. This will automatically encode the username:password and construct a basic auth header with the encoded string.
Source:
https://en.wikipedia.org/wiki/Basic_access_authentication

Apple Sign-In: How to use it for custom server endpoint authentication?

My use case is that once I have a user signed into my app, I use the Oauth token, resulting from the sign-in, when I make endpoint calls from my app to my custom server-- to authenticate the caller. E.g., I use Google Sign In in this way.
This method (e.g., with Google Sign In) has several useful properties:
Updated tokens are created automatically on the client app.
My custom server can easily verify the validity of the token, using Google's endpoints.
Initial token verification can take place early in the endpoint request processing-- without access to the custom servers database (as in the style in https://github.com/IBM-Swift/Kitura-Credentials).
My question is: Given that we're being told we have to incorporate Apple Sign-In into our iOS apps (if we offer general purpose sign-in facilities), how can I do endpoint authentication with my custom server?
I see two alternatives, neither of which I like very much.
First, I can have my client app send an Apple Sign In id_token to my server and ignore the exp (expiry) field. I can regenerate the id_token periodically (apparently, no more than once a day) and send it back to my client. I don't like this idea both because of ignoring the expiry of the token, and because of the need to periodically send the token from server to client. (My app uses multiple sign in systems and this just creates extra difficulty).
Second, I could have my client send an Apple Sign In refresh token to my server. My server would need, of course, to initially generate that refresh token and send it back to the client. I like this idea even less than the first idea. My initial token verification in my custom server would need to access its database to look for a match this token. I can't generally use an Apple endpoint -- because, again, Apple is apparently going to throttle this verification.
Additionally, I don't really like the idea that my custom server can, at best, check on token validity once a day. If the user revokes the app's credentials, I would hope my custom sever would stop being able to operate on behalf of the user relatively quickly.
Thoughts?
10/5/19-- update to the first alternative above. Upon actual use of https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens for refresh token validation, I find that it is not actually generating an updated id token. It is generating an access token (but Apple doesn't define a use for that), and is validating the refresh token. And so, there is no way to send an updated id token to the client iOS app. Thus, using the first alternative, the expiry date of the id token cannot be used.
10/10/19-- update: I've written a blog article on this subject-- https://medium.com/#crspybits/apple-sign-in-custom-servers-and-an-expiry-conundrum-d1ad63223870
8/6/20-- update: Follow on blog article with possible path forward, pending details from Apple: https://medium.com/#crspybits/part-ii-apple-sign-in-custom-servers-and-an-expiry-conundrum-b3e9735dc079
In Get the most out of Sign in with Apple in WWDC 2020, at 11:30 in their presentation, they introduce server-to-server notifications to enable your server to monitor user account state changes on a real-time basis.
So far, few details on this though.
----------------- UPDATE (12/23/20) -----------------
I now have these server-to-server notifications working in a testing environment with my server. Some notes:
I decided on the endpoint to use, on my server, to allow Apple to send my server these REST endpoint requests.
I pasted that into developer.apple.com > Account > Certificates, Identifiers & Profiles > Identifiers > Select your app identifier > Click 'Edit' next to 'Sign In with Apple' > Server to Server Notification Endpoint
This endpoint is effectively unauthorized. E.g., it is made by Apple with no OAuth credential access to your server. How this is setup will depend on your server. I had a means to set up a new endpoint/route for my server that was unauthorized.
I have the client side and other parts of my server set up to allow creation of accounts using Apple Sign In. So, using one of those accounts, I now started taking actions that would cause Apple to invoke their server-to-server notification endpoint on my server. I wanted to reverse engineer the details of the endpoint request Apple is making, since details are scarce.
This provides some ideas on how to cause the notification events to occur:
How to revoke Sign in with Apple credentials for a specific app?
You can revoke credentials, but it's easier (because you can do it repeatedly) to enable and disable the email relay. Of course, to do this, you have to initially sign-in with Apple using the private/email relay.
I next learned two things:
a) After you take the action (e.g., revoke the email relay), the server-to-server notification endpoint is accessed on your server within about 30 seconds. I had added various log output into my server, so could watch my server log and see this happening.
b) The endpoint request Apple makes to your server has body data containing JSON in the following format:
{"payload" : "-- SNIP -- JWT"}
I'm using the following Swift structure to decode this.
struct ApplePayload: Decodable {
let payload: String // JWT
}
As Apple has indicated in the WWDC 2020 video (https://developer.apple.com/videos/play/wwdc2020/10173/), the main content of the body data is a JWT. Above, this is the value of the key "payload" in the JSON.
The next step is decoding this JWT. I just guessed that it would use the same mechanism for decoding as with the JWT in other parts of the Apple Sign In server-side process. And specifically, in decoding the identity token (a JWT) passed up to your server by a client using Apple sign in. See https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple
I had some code that did this JWT decoding, so I factored that out and put it in a common place:
https://github.com/SyncServerII/AppleJWTDecoder.git
Integrating that into my server-side processing of Apple's server-to-server notification requests, I found that indeed this JWT can be decoded in this manner.
Another aspect that became evident is that the structure indicated by Apple in the WWDC 2020 video isn't 100% what is present in the JWT, after decoding. Specifically, in my tests so far at least the events field is not an array, rather it has a single value. See https://github.com/SyncServerII/AppleJWTDecoder/blob/main/Sources/AppleJWTDecoder/AppleSignInClaims.swift for a Swift structure.
I am now successfully parsing the JWT. The next main step on my server is to actually utilize the different event types in my server to take actions. For me this is going to involve the two account (not email) related actions:
User decided to stop using their Apple Id with your application. And
should be treated as a sign-out by the user. E.g., when a user decides
to disconnect your application from Settings. (From
https://developer.apple.com/videos/play/wwdc2020/10173/)
Also considered a request from user to "delete their app account"
(broader context: "Server to Server Notification Endpoint Sign in with
Apple server to server notifications allow you to receive important
updates about your users and their accounts. Notifications are sent
for each app group when users change mail forwarding preferences,
delete their app account, or permanently delete their Apple ID. Each
group of apps can have one URL, which must be absolute and include the
scheme, host, and path. TLS 1.2 or higher is required to receive
notifications. Learn more.") To see these docs, go to:
developer.apple.com > Account > Certificates, Identifiers & Profiles >
Identifiers > Select your app identifier > Click 'Edit' next to 'Sign
In with Apple' > Server to Server Notification Endpoint
case consentRevoked = "consent-revoked"
User has asked Apple to delete their Apple Id. The user identifier will now no longer be valid.
case accountDelete = "account-delete"
My plan is to take both of these events as equivalent- and delete the user's account on my server. I'm then going to have to consider how to communicate this to my client (iOS app). It will need to know that the user has deleted their account.

How does a JWT token with an empty payload work?

I'm working with the Snapchat API to try and log into an app using their OAuth flow. Once the user is logged in via Snapchat, I'm trying to reverse engineer (since they have no documentation on this) how to obtain some sort of unique ID for the user so I can associate them with a local user in my database. This is how I have previously done this with things like Facebook. The user logs in and gets an access token via the Facebook Api, and I can extract some kind of unique ID for the user via the Facebook API.
The Snapchat API only allows you to access the user's display name and some "externalId", which I cannot guarantee won't change. So I decoded the JWT token that Snapchat issued to me and it challenged my understanding of how JWT tokens work. When I decoded the token at http://jwt.io, I saw that the payload was empty, yet the token works when calling the https://kit.snapchat.com/v1/me endpoint. How is the snapchat server able to identify who I am? I've always had the understanding that the JWT must include a claim such as sub which identifies the user. The server can then use that information to know who I am.
In this case, my JWT payload is empty, but the aforementioned endpoint still returns my user data. What is happening here? How does the server know who I am when my JWT token has an empty payload? To me they must be storing a copy of my JWT token on the server, which seems like the incorrect way to use JWT tokens. Perhaps my understanding is terribly wrong. Any thoughts?
The payload of a JWS (signed token) can be detached and transmited to the audience by other means.
This feature is described in the Appendix F of the specification.
With the JWS compact serialization mode (the most common format), a token looks like THE_HEADER.THE_PAYLOAD.THE_SIGNATURE. With a detached payload it is identical except that THE_PAYLOAD is an empty string: THE_HEADER..THE_SIGNATURE.
The verification of the signature is the same as with an attached payload. The receiver should have received the payload and must recreate the full input i.e. THE_HEADER.THE_PAYLOAD.
Regarding the identification performed by snapshat, a reference to the detached payload may be set in a header parameter of the token (first part of the token) allowing Snapchat to fully verify the token.

How to protect JSON API from being accessed by anyone but my iOS client?

I have an iPhone app that uses a Rails server HTTP API. The API is public at this point - no authorisation is required to get the data.
Currently anyone can go to API's URL and download the data.
http://server.com/mydata
The data is not very sensitive. But I still want to prevent people from easily getting it. What are the ways of doing that? I do not want iOS app users to log in either.
Current solution I have
iPhone app adds a secret token to the HTTP header or query of the request. The data goes over HTTPS.
https://server.com/mydata?secret=my_secret
Is there a better approach?
You could try an approach where the client is only allowed X number of requests per time period (based on IP address or username)
HTTPS is extremely easy to man in the middle on a device you control. You can do SSL cert validation, but there is always someone out there with more time, so best off to handle it server side.
Distribute and use your own SSL certificate.
Apps that transfer sensitive customer data, like credit card and payment information, must be protected from man-in-the middle attacks. The best protection is a mutual authentication scheme, where certificates are exchanged to make sure the app is connected to a trusted server and to make sure the server is connected to a trusted app.
Then only individuals (who have presumably installed your application) have access. If someone digs through the code and gets the public certificate then they can impersonate the client; but at that point they win anyway and two-factor authentication should be explored.

iOS creating secure token based communication between application and server

For an ios 5.0 application connecting to a rest webservice, the customer wants to implement a token based security to ensure that the data being sent over the network is not intercepted and altered in any way... Doesn't https over ssl ensure that the data is not intercepted? and I thought that this would be enough. Pls advise
However, The way the client wants it to work is that starting with the first client authentication request the server would return a token id that would be used to send the next request. In the response for this next request another token id would be sent back that needs to be used for the next request and so on. The problem is of concurrency. Eg when the apns token comes back and the app has to send that to the server and if at that time the iOS application is already making a data request to the server, then the tokens to be used will not match. also since the app has to regularly poll the server for new items, then there are more chances of such concurrency issues to occur.. Any ideas what efficient solutions I can put in the app to counter this?
Or if anyone can suggest better ways of implementing security over the network data, as a possible alternative to the above approach.. solutions that would work for an iOS app and is not battery consuming?
Help in this would be greeeeaaatly appreciated! :-)
Ps. Jfyi Am already doing md5 security on the token being sent
Doesn't https over ssl ensure that the data is not intercepted?
It depends on whom you're trying to protect agains. Plain SSL will protect perfectly fine against anyone between the device and the server.
But it will be trivial for the device owner to create a man-in-the-middle against a client that trusts all CA's on the device. All he needs to do is install his own private CA-certificate on the device, issue a fake certificate for your server signed by this CA, and install this certificate on his proxy/MitM device. To avoid this attack you'd need to do certificate pinning in the App.

Resources