Google cloud storage is it ok to expose API key? - upload

I'm developing an application which lets users upload pictures. I'd like to use Google cloud services to store these pictures. I am creating a unique GUID for each image in database and would like to store the images in the cloud with that name. It makes sense for me to make an ajax request for a GUID and then upload the image from the same page directly to google cloud services.
https://github.com/GoogleCloudPlatform/storage-getting-started-javascript/blob/master/index.html
Like shown in this example.
My first question is, should I be sending this to my back-end(C# code) and uploading it from there? Or is this the correct approach?
And my second question if this is the correct approach is, wouldn't exposing my details like that in javascript allow other people to upload from outside my application as well?

An API key, by itself, identifies a call as being associated with a certain project for purposes of billing. It's only necessary for anonymous calls. An API key does not grant any sort of authorizations. If there's an object in a bucket in your project that only your project members can see, the API key won't give anyone permission to read it.
That said, it's not a great idea to share your API key if you can help it, and if you need to share it, you should lock it down as much as possible. API keys can be limited to use with only certain IP addresses, only with certain web referrers (for instance, it will only work with JavaScript clients on www.yoursite.com), or only when run from a particular iPhone or Android app. These precautions aren't cryptographically fool-proof (there's no reason a hacker couldn't spoof a referer header), but they do make them pretty much useless for someone else who just wants to paste an API key somewhere to enable a web app and doesn't want to pay for it themselves.
The problem with using the javascript client's approach for your application is that individual users would either end up uploading objects completely anonymously or with their own Google accounts. Neither is super great, since the anonymous option would basically require you to create a bucket with anonymous writes enabled, and you don't want to do that.
There is a great approach to letting users upload pictures, though: signed URLs. Signed URLs allow your server to securely sign, in advance, a request to upload an object with your credentials. This is your best option for letting anonymous end users securely upload objects to your buckets.
Documentation on signed URLs: https://cloud.google.com/storage/docs/accesscontrol#Signed-URLs

Related

Use CloudKit Web Services' authentication flow in combination with Zapier

I'm developing a Mac app that uses CloudKit as its back-end. Some of my users are requesting the ability to ingest and extract data via an automation/integration service such as Zapier. For this, I need to introduce a web API.
I am planning to use CloudKit Web Services to access the app's data. This data is user-specific and hence, resides in a private database. As a result, CloudKit requires user authentication as described here.
Essentially the user needs to be redirected to an Apple-hosted authentication page. After successful authentication, an authentication token is provided that can be used for data operations. Similar to how OAuth2 works, but different enough to not work with Zapier's (or probably any other similar services) supported authentication schemes.
Who has done something similar? What are my options? I want to keep things as simple as possible and make my web API's implementation as thin as possible.
Thanks.
Niels
This is definitely doable and you are on-track with your thinking. Here's how I envision it working:
You could do all of this with a front-end web app (no server-side app needed). I personally prefer Vue.js but you probably have something in mind already.
Your app will need to authenticate the user to CloudKit using the flow you mentioned. I highly recommend you use the Web Services API and not try to wrestle Apple's neglected CloudKit JS API. For this, you are going to need to generate an API token in the CloudKit Dashboard.
You app would then prompt the user to authenticate to Zapier.
You should now have user credentials for both CloudKit and Zapier in place in the user's browser cache (you can save, for example, the CloudKit token to sessionStorage and likewise with Zapier).
Make API calls to Zapier, pull down the data, and then save it to CloudKit all within your JS app. It's all API transactions at this point. I'm a fan of Axios for making the HTTP requests.
If you are downloading files, transacting huge amounts of data, or doing processor-intensive stuff, you might consider using a server for that work. But if you just need a place to pull and push reasonable chunks of data, I see no reason why you can't do it all in a front-end app.
Alternatively, if you don't want a web app at all, and want to only have the user work in the Mac app, that can be done, too. Just make API calls directly to Zapier from within your Cocoa app. Whether or not this is feasible depends some on how you want it to work.
If you have more specific questions or need help with any of the implementation details, feel free to add a follow-up comment or ask a new question.
Good luck!
I think the other answer is mostly correct. I don't know much about CloudKit, but we can talk through what you'd need for it to work.
Let's say you had a simple iOS app that stored contacts. On the iOS side, Apple presumably abstracts the upload and download operations.
If you wanted to make a web viewer for synced contacts using CloudKit, you'd need an endpoint to fetch all rows belonging to the authenticated user (each of which would have a UUID, name, and a phone number). I believe that's possible with CloudKit code Apple provides (but let me know if I'm off base).
Now, we want to integrate with Zapier. Say, a "New Contact" trigger. You make some sort of authenticated HTTP request from Zapier to Apple on behalf of an authenticated user. It gives back a list of contacts and Zapier can trigger on the ones it hasn't seen before. To do that, Zapier needs some sort of user token.
That's where the little front-end page the other answerer mentioned comes into play. If you've got a web page that can surface a user's token to them, they can paste it into Zapier and all of the above becomes possible. I'm not sure what the lifespan of the token is, but hopefully it can be automatically refreshed as needed (rather than the user needing to take any manual action).
I'm not sure if what I've described is possible, but do let me know if it is. It would be huge if it were possible to integrate Zapier and the iOS ecosystem!
Edit to respond to comments:
Zapier won't be able to interact with CloudKit in a way sufficient for me (some minor business logic is needed)
I'm not sure what that entails for you, but it's common to put logic in the Zapier integration to structure data in a way Zapier expects. There's a full Node.js execution environment, so the sky's the limit here.
I don't think Zapier can authenticate against CloudKit as it uses a non-standard authentication scheme
Once you've got a user's token (described above, which is unusual), you will almost certainly be able to use it in requests to cloudkit. Zapier provides a "custom" authentication scheme which lets you do basically anything you want. So unless Apple uses something that fetch can't handle (unlikely), it should be fine (once you get the token).
I would like to push data directly from my app into Zapier and have it done whatever magic the user has configured
This is also probably possible. Zapier ingests data in two ways:
polling, where Zapier frequently makes a web request, store the IDs of items we've seen before, and act on the new ones. You can read more about that here. Assuming you can work your business logic into the integration, this is doesn't require an external server besides Apple's
webhooks, where Zapier registers a subscription with you and you send new data, on demand, to that address. This would probably require a webserver on your end to handle. It's optional though - you can do polling instead.
Hopefully this cleared it up a bit. Feel free to reach out to partners#zapier.com and reference this question to talk more about it.

Prevent attacker from decompiling iOS app and getting database access

According to this post, it's possible to decompile an iOS application.
How can I prevent an attacker from gaining access to my AWS DynamoDB database? Just having the access keys out in the open like shown on the Amazon developer guide doesn't seem like it would be very safe.
I would think that I could use keychain to store the keys, but I feel like there would be an easy way to get past this for a motivated attacker, given they have the app's assembly source code.
Currently, I connect using Amazon Cognito. All I have to use to connect are the identity ID and the role name. I don't see anything stopping an attacker from simply getting those values and connecting to the database.
For example, what stops an attacker from decompiling the Facebook iOS app code and deleting all of the users?
How can I prevent attackers from decompiling my iOS application and getting access to the database access keys, or at least prevent them from doing any major damage, such as deleting users?
Based on my admittedly limited experience, I'd say that a really motivated attacker will always be able to retrieve the credentials you use to access your database regardless of what you do to your executable. I would, however, question why you application needs to have direct access to your database in the first place.
The usual way to safeguard your serverside data is to use a web service to access it. App contacts web service with request, service contacts db, gets data, sends it back. Since the web service and the db are both hosted on your server and only the web service needs direct access to your db, there is no need to store db access info in your app. Problem solved.
It's impossible. In order for your program to do something, it must contain the instructions that allow the computer to follow to do that thing, which means anyone else can also follow those instructions to learn how to do the exact same thing.
You can use SQLCipher and use your auth's userToken and/or userId as cipher keys.

Is it possible to substitute a server backend using Dropbox?

I am developing an iOS application (to be deployed on the App Store) that requires content updates on a weekly basis.
I understand that the best way to achieve this would be to use a server, where the app would query for new data and download responses in JSON. However I am not knowledgeable when it comes to HTML, PHP or MySQL and therefore am endeavoring to find an alternative.
Here's an idea: using Dropbox to substitute for a server backend. My app connects to one central Dropbox account, checks for new files, and downloads them if present.
Is this idea feasible? If not, are there any alternatives?
Dropbox cannot be a dependable substitute for your server/backend for following reasons:
Dropbox uses OAuth for authentication, which needs user interaction. You do not want your app users to go through Dropbox authentication with your 'common' credentials.
Users who have a Dropbox account or the app installed, will most likely use their own credentials to login which completely breaks your flow.
Drobox, although a good way of sharing and syncing files is not meant for more meaningful data like web services etc or user/database interaction etc. Just syncing JSON file may suffice your app's needs for now but from a long term perspective you want a proper back end.
As suggested in the comment by #tkanzakic you can use one of the substitution services if you don't want to get too technical on the backend.
I am pretty late to the party, but this is possible and not necessarily a stupid idea (though this depends on what you need). You might want to have a look at remote storage for example, which allows you to use Dropbox among other providers as backend.
For sure, you can use the Dropbox Sync API to achieve this (https://www.dropbox.com/developers/sync).

Make authenticated requests to YouTube from iOS always with same user

Recently, YouTube decided to make video tags unavailable publicly. So to get the tags for a given video, I need to make an authenticated request to the API as the owner of the video. This is not a problem in my case as I'm fetching my own videos.
However, I'm confused about the authentication flow since YouTube strongly recommends to use OAuth2. Since I'm always going to authenticate as the same user (the owner of the video, aka myself), I definitely don't need to have any browser page for the actual user of the app to do anything. I see how I could have done it using ClientLogin (hardcoding login and password into the app) but I'm not sure how to approach this using OAuth2.
One last detail - that is not necessarily relevant since a high-level answer would be enough - is that I'm developing on iOS. Also I looked at this https://developers.google.com/accounts/docs/OAuth2 and particularly the web server case which seems closest to mine but was not able to get a clear idea from it.
Thanks in advance for your help and don't hesitate if you need me to be more specific.
There is no OAuth flow that supports your use case.
In general, you should not be distributing your YouTube login as part of your application. Even if this were available via ClientLogin, after a certain number of logins, you would likely be presented with a challenge because the authentication servers would detect a strange usage pattern.
OAuth is not for distribution a single user's login to a large N, where N is the number of users of your application. OAuth is meant for your application to act on behalf of an end user, and because tags are no longer exposed to end users through the UI, it does not make sense to expose them to users via the API either. More details can be found here:
http://apiblog.youtube.com/2012/08/video-tags-just-for-uploaders.html
How many videos do you have? What is the purpose for needing the tag metadata? From a pragmatic perspective, here are a few alternative implementations that would be easier and would not require users to log in as you:
Store a single file mapping video IDs to tags on a server somewhere and fetch this periodically. Google App Engine is a good place to do this.
Put the tag data in the description in a predictable format (you host the videos), and generate the metadata from this.

Storing remote database credentials safely in iPhone app

I'm developing a simple iPhone app where users register, and sign in with their email/password. These values are stored in a remote database.
I'm using Cloudant to store this information (CouchDB is great), and have granted read-only privileges to a new user (created API key/pass). In order to communicate with Cloudant, you obviously need a URL to access it (eg https://user:pass#db.cloudant.com), which is stored in the app as a string.
Now, while I know this is pretty unsafe, I can't think of any other alternatives in order to keep the db URL safe (specifically the username/password for it). I've seen people talk about using another server to proxy through to obtain the credentials, but it seems a little awkward.
Any help or thoughts would be really appreciated!
Are you trying to make a connection from your iPhone app directly to the database? You shouldn't give your app read access to the whole remote user table / database. Sooner or later someone would find out and would have read access to your data. No matter how you try to obfuscate it, the user/password combination would need to be stored somehow in your app.
What you should do is build a web service that connects to your DB and verifies your users. The database password stays on a server. This proxy-approach is not awkward, it is the only way to keep your database logins away from your users.
One option is to create your own service in the cloud that abstracts away your storage. That also has the benefit of allowing you to change your storage without updating all your devices.
In that model, the service stores the credentials to access the storage and you implement user security in your application layer. I also wouldn't think of it as a proxy layer - that implies that it's a thin pass through. If you develop a service, you should define a web interface (rest, soap) that's agnostic to the storage. In that case, it's a service, not a proxy.
EDIT:
Typically the web service authenticates the user (don't write your own). Basic Auth with SSL is typical. Then, in that services context API, you get access to the username. From there, you do you what you need. Your storage is accessed with the one storage account that has full access to all content.
Another auth option is OAuth which allows them to authenticate with someone like google - you never get the password - just a token from google letting you know they authenticated and they are who they claim to be. For example, that's how stack overflow works.

Resources