We are developing an app for IOS.
Is there anyway I can check that the "identifierForVendor" that the device sends me in it's first connection to my server is actually valid?
If there isn't a way, how can I make sure someone is not just sending POSTS to my server and so making me create Device DB Objects that don't really exist?
The only secure way I have found is:
1- Make the App ask for a Device Token to APNs
2- Send it on it's first connection to my server.
3- Check with APNs Feedback Service
4- If token is ok, create the Device DB Object and continue from there.
Apple should let you know some Device-Vendor Id in a communication between Apple and your server every time someone downloads an app.
Thank you.
The simplest solution would be to append the "identifierForVendor" with something you can identify from your app. For example, if you append an alphanumeric string that looks like this: "A1B2C3D4E5F6G7" to be "A1B2C3D4E5F6G7-fromMyApp", then there is no way for someone to know what the custom appended string is, unless they have access to your code.
Of course there are more complex solutions, if you are genuinely concerned of people going so far as to monitor traffic from your app just to find the string.
Are you aware that registering for remote notifications prompts the user if they want to allow remote notifications for the app. If they choose no, there is no token generated.
Besides, they can sniff the token off the wire. Do you plan on tracking abuse and blocking users based on their token? Do you know that some actions cause a new token to be generated, such as resetting the device?
You can generate a unique ID (UUID) or use identifierForVendor, and store it in the user's key store and use that to track by device. It's still anonymous, and resetting the device resets this, but if you're tracking abusers, you can block them and they have to reset their device to try it again. This isn't much different from an APN token. It can still be sniffed, and they can still reset it. But at least the user doesn't have to say yes to allowing remote notifications.
If you're sending any kind of token, you should use HTTPS (SSL/TLS), not to protect the user from themselves (they can still sniff the token by doing their own man in the middle attack unless you are verifying the identify of the server), but this is to protect people from malicious users on the same network. You don't want to block some innocent user because they happened to use your app on a public network and inadvertently shared their token.
Of course, if we're talking a jail-broken device, all bets are off.
Related
I'm writing an application which will be used in an enterprise, no outsiders.
This application should fetch data from API response and display it.
Each user has his own device, Ipad and should see only the data he is the owner of.
Problem i'm facing is identifying the device/user, so that API responds with only the information the user is supposed to see.
brief example of how it should work:
App is opened -> get unique id -> attach ID to API call -> receive appropiate response -> display data
As i imagine this ID should be static and not made upon installation of the app or generated.
I've tried getting UDID, Serial, MAC,- no luck, they're deprecated. Only managed to get .IdentifierForVendor, which is unique not in the way that i need.
So here is my question, are there any other options left?
Like fetching appleID name,email or should i make unique deployments for everyone separately?
Or a Log-in screen?
You could create a GUID for every App instance. However, apart from that you will have a hard time doing what you want.
These ways of identifying a device have been deprecated to ensure Advertisers and other malicious Apps cannot fingerprint a device easily.
If you don't want too much hassle authenticating everyone, you could apply a simpler scheme such as using a pin code, QR code, NFC tag or whatever you prefer.
However, if someone were to steal one of these enterprise devices and it would contain any secret information I would rather rely on something more secure as username and password, or even better something multi-factor.
Unique id's will have to be set by deploying the app from MDM. For example:
https://docs.jamf.com/9.9/casper-suite/administrator-guide/In-House_Apps.html
How should the application accept those variables, i dont know. Maybe it modifies .plist when deploying.
Solution i did was enforcing device name from MDM, so that users are unable to change it - and using that as the unique identifier.
Apple provides an endpoint to validate receipts:
https://buy.itunes.apple.com/verifyReceipt and warns against calling the endpoint from the app
It is not possible to build a trusted connection between a user’s
device and the App Store directly because you don’t control either end
of that connection, and therefore can be susceptible to a
man-in-the-middle attack.
It is stated, that the safe way is to send the receipt to "my own" server firstly, then communicate with Apple endpoint from the own server.
Honestly, I do not understand how does it help to increase the level of security. Yes, I do not control the /verifyReceipt endpoint, but Apple hopefully does. And why
phone <-> my server <-> Apple server is better than phone <-> Apple server?
Could you elaborate on this from point of view of the hacker (or man-in-the-middle)? How would he tamper the receipt/response in the latter case and what makes it difficult for him in the former case?
It's relatively easy to change an app binary to alter a string within the app. If your binary contains a string "https://buy.itunes.apple.com/verifyReceipt" then an attacker can change the string to "https://example.com/verifyReceipt" (a server that's under their control) and have the server return the receipt JSON that makes it seem like a purchase was successful. This is an easy attack to automate, so you just run a binary through a script which replaces all instances of the Apple URL with another URL.
When you communicate with your own server to Apple you can be (relatively) sure that that connection is secure. There are also ways to secure the communication between the app and your server such as a certificate in the app binary with a public key, the private key on your server. (I'm not an expert at this part so the details about keys might not be 100% correct).
All this being said there are other ways an attacker can make it look like a purchase went through. They can search your app binary for common receipt parsing libraries and flip known booleans (e.g. a userCompletedPurchase boolean flag). These attacks are easily scriptable.
The best way to make sure that your app isn't attacked is to remove the low-hanging fruit and make it that much more difficult that the common scripts won't open your app to attack. One way to do that is to not verify receipts directly with Apple.
I think that the key part to the section from Apple documentation that you cited, but didn't include, was the sentence before.
Using your own server lets you design your app to recognize and trust
only your server, and lets you ensure that your server connects with
the App Store server.
In particular the first half of that sentence (emphasis mine). I see this as a bit of nudge from Apple that the app should be designed as such to "recognize and trust only your server".
In the simplest case (ie. minimal), one could use their server as a proxy to reach Apple's server. The server could simply pass Apple's payload back to the app, or maybe send some simple JSON like
{
success: true
}
Or maybe even it's something as lazy as just sending a 200 response.
Using MITM, it's easy to change the response as needed. So even though you are using your server, the only barrier at this point is the attacker would need to determine what a valid response looks from your server.
They may be able to figure it out by simply sending a bad receipt and then seeing the response. Or maybe a hacker will TOFFT and just make a purchase to get the valid response and then provide others so they can profit.
This is "one step" above going to Apple directly, because everyone knows what the Apple payload is. Thus if the app goes directly to Apple it's easier for someone to be able to run a MITM and replicate a valid receipt response.
Going back to what Apple wrote, they are encouraging you to have a design that is more robust to help mitigate potential fake purchases from occurring. This robustness is what protects you more.
This can take the form in many ways.
For example, perhaps your server sends back the response in an encrypted format. Or replies with a hash. Even better if your app relies on your server through the course of usage, so the server can act as the truth. Or maybe the app periodically "phones home" to get a config of privileges. There are numerous ways one can make it more difficult for a hacker.
Apple is leaving to you just how you determine that trust with your server is established. It is after all, your "money".
As an example, the latest game I've made is specially built to use the server as the truth. So all moves go through the server (game is simulated on the server). While someone could certainly modify the app, in the end the server should theoretically correct any badness introduced.
Note, that it still is possible that someone can bypass even more robust security measures.
You can validate the iOS receipt on the iOS device. But you cannot be sure that the receipt is actually valid. The user could have hacked the device to make you think the receipt is valid.
Your users can edit the executable code of your app, and they can edit the operating system. With common/shared APIs like Apple's purchasing system, there are publicly available tools a user can run on their own phone to avoid paying.
Your server, however, is controlled by you. Your customers do not have physical access to it. And therefore your server (hopefully!) cannot be hacked. This means your server can be trusted, unlike the device. When your server establishes an SSL connection to Apple's server, you know you really are talking to Apple's server. Not one that your user-installed to bypass in-app purchasing.
UPDATE
Let's say you have got your Receipt after a user purchased your consumable product. So now you want to validate it and you have two options:
1. Receipt validation locally(from a user device).
2. Receipt validation through your server.
Local validation:
When you receive the receipt, you then read the contents of the receipt file by passing it through the verifyReceipt endpoint from your app. And you will get a response include a readable JSON body from this endpoints. Based on this response you can validate the receipt and control user action.
And all this happened in your app[user device]. After publishing your app you can't modify your app(if you need) without another release. Also, the user has more control of their device than you.
Validation through your server:
After getting Receipt data you need to encode the data in Base64. Send this Base64-encoded data to your server.
On your server, create a JSON object with the receipt-data, password (if the receipt contains an auto-renewable subscription), and exclude-old-transactions keys detailed in requestBody. Submit this JSON object as the payload of an HTTP POST request. In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt as the URL. In production, use https://buy.itunes.apple.com/verifyReceipt as the URL.
Now you will get a JSON object response from App Store's that contains the keys and values detailed in responseBody. According to the response, you can validate receipt and send a response to the app for further actions.
So, if the user buys something in your app you don't need to store the thing being purchased inside the app. You want to store it on a server, and that server only sends the purchased data to the device after it has verified the receipt with Apple's server.
And you have more control in your server than your app also you can change any mechanism about receipt validation at any time(don't need to update your app) and control your user from the server.
Hope you will get it. Now it depends on you which one you prefer for you.
I will try to put my understanding here as I am deeply involved in auto-renewable subscription workflows.
Why is that endpoint there? what is the purpose?
The purpose of that API is to verify the purchase that user made using your app. So why do you want to call that API from the mobile(phone). In that environment, you already know that user has made the purchase.
But if you want to call that API to decode the encoded receipt inside the app(to read important values), then consider the other recommended approach. That encoded receipt is PKCS7 encoded and Apple itself provide lib to decode that.
Hope this helps
After registration, our app prompts users to invite her friends (aka phone contacts) to use the app too. This allows us to send an email/sms to the useer's contacts with some sort of invitation key. Works fine for a web version app, just embed the key in the url you provide in the invitation.
I'm having trouble figuring out how to make this work smoothly with IOS only. It would be brilliant if I could send the invitee a link to appstore.apple.com/myapp?registrationKey=abcXYZ and have the key magically available to my app once it's installed, but I guess this is a lot to hope for?
The obvious way around this is to make the user manually enter their registration key on first launch, but this seems less reliable and (to my mind) adds friction to the UX.
Has anybody come up with something clever to get around this?
Here is what is flowing through my brain on how to solve this solution, please note, I have not vetted, psudeo-coded, coded, or applied this theory.
Since you will know who is being sent an invitation, save that data to your database with a relationship to the user sending and a unique id to the user being invited (email address if its in the contact's card). When new users sign up scan the database for invitations, if one is found present it to the user asking We're you referred by <existing user>? Once the new user selects their response continue through the registration process, updating the relationship table accordingly and applying any extra settings you need to for the referral.
This combines automatic referral tracking with referral codes for a basic, straight-forward, almost (but not quite) fool proof method to make sure referrals are linked to the right users.
As far as I can tell, the App Store provides an information firewall between an invitation and the installed app.
The closest workaround I've seen is the following:
email link sends you to your website
the website logs reference information in the URL and the IP address
the website instantly redirects you to the App Store (if iOS detected)
user installs the app
user loads the app
app contacts your website, IP addresses matched ... BINGO
Obviously not a secure method though.
There are many failure cases:
business networks commonly share IPs
home and mobile networks release and reuse IPs
The more is frequently used to resolve cases where its good enough to know that the user 'almost-certainly' was referred to download app by the email.
For example, it can be a good mechanism to prompt the user with a "who do you know" question in an app and limit the options based on the (IP+reference) data. If they pick the original poster, then maybe that's good enough, and then you can attach any other data that the inviter provided.
(Full disclosure, currently work at Branch)
The best solution to this is to fingerprint a user. This requires you to do the following steps:
For each user, using your own domain, generate a link for said user. So, right when they complete registration, generate their unique URL, that contains the invitation key.
For anyone clicking this link, they will redirect to Safari first. When they do, capture their IP address and iOS operating system version from the headers and user-agent.
Save this data on your server, and set window.location to your iTunes url.
If the user downloads and consequently opens, inside AppDelegate.m, send a message to your server with the IP address + major/minor/min version you collect upon app launch. If it matches with what you have on the server, you can now pass that invitation key back to the new user.
It's not perfect, and has the ability to misattribute. You could also use branch.io, where all of this is taken care of (link-generation, fingerprinting a user, attribution). Branch also drops a first party cookie and ties it with the device level ID, so attributions are much more accurate.
We are building a web based system where there are money involved and we want to avoid fraud implementing a system that is able to identify the IOS device from where the requests are sent.
The reason of this security is because we offer money for execute actions from a mobile and we only want the user to obtain the money once, if we are not able to identify the device the user can execute the action several times.
This unique identifier can use any HTML, JS, server side technique, but not any native IOS call due the application is web based and it runs in a normal Safari instance.
The unique identifier is not needed to be the official UUID.
The system doesn't need to be bullet-proof just a few more difficult to cheat than a normal cookie.
The system should works in separate sessions, like if the user comes back after one week.
Heuristic based systems are welcome, also any combination of LocalStorage with Cookies, ...
The only ways you can identify a unique user/device in a web application is to use cookies and or track the user's IP address.
Of course, the IP address of a device will change as the owner moves around and cookies can be cleared/disabled or will expire after a set time.
Letting web sites access a device unique identifier such as the UDID would be a huge security risk / privacy invasion. If you were to find such a way, I would say that you found a severe security hole in iOS.
If you are only interested in triggering i.e. an email alert when an account is suspected of being stolen, you could use a heuristic based on device type (user agent string) and geo-ip-lookup to detect if the user has suddenly changed device type and continent and ask the user to confirm that this is really the case. I believe this is what e.g. Google and Facebook does.
I have the following issue:
I've understood how to create a secure login between an iPhone app and a WebServer (SSL,Https).
My question is after creating the session token, how do I make sure that if a hacker intercepts it, in the subsequent POST requests I receive data from the same user?
I ask this because I would have to send the session token each time a request is made right? (to be able to identify the user).
I want to prevent multiple things:
Session hijacking where someone would sniff the users token and send data instead of him (like a highscore or something)
Data injection using data that would not be normally sent from my app like a 1.000.000.000 highscore (possible score but not easily attainable).
I have been looking at:
UDID
User Agent (if it's not from the app name of my app it's not good, the hacker would actually have to guess I do this check or download my php files somehow right?)
The app is from the AppStore. If the request comes from an app that hasn't been approved by Apple it's not ok. I'm not actually sure if you can test this or not. If this works a hacker would have to actually submit an AppStore and download it to insert faulty data into my database which I hope nobody has time for.
The MAC address. Not sure if allowed by Apple. The IP doesn't work because a valid user might change IP's.
Cookies from what I've seen can be easily traced and see what data is inside them.
Maybe I'm not asking the right question here so it could actually be how can I make sure the data I receive is from the correct user and the correct application?
The purpose of SSL around your POST requests is to prevent interception by a third-party in transit. If a hacker can get to it, it means the token was either leaked on the client (rooted device), server (insecure application logging/debugging) or they broke SSL. (unlikely)
You could perform some advanced checking by capturing device UDID (apple doesn't like this) or comparing to source IP, but it is going to be a lot of effort for questionable security improvement.
Just ensure everything sensitive is in SSL and you should be ok.