OpenAI API keys leaking through app binaries
twitter.com
twitter.com
If your application needs to call a 3rd party service like openAI, the only solution to safely not leak your API key is to have your app only communicate with a backend you own and call the openAI from there.
OpenAI allows revoking leaked keys. If you did include your API key in a client-side application, update your app to use a backend for openAI API communication, use a fresh key and revoke the old key when your update ships (or if you value security over functionality then revoke the key before you ship the update).
I'm a bit baffled anyone puts anything secret on software people are using. This service needs to be online anyway.
Anyway, seems like a lazy programmer thing.
I've also seen vendors do things like issue client-side keys for AWS IAM users that can access their backend (in AWS) with a super locked-down role. This would be more interesting as a solution if IAM stuff was interoperable between cloud providers (CSP), since this dependency means you can't move to another CSP without bothering your customers. It also doesn't help in the OpenAI case because there isn't a way to mint limited-permission tokens.
If you don't store any API keys in your binary, how do you handle crash-logging and analytics? How do you integrate with third-party log-in SDKs?
You _could_ vend some of those (not the crash-logging ones, etc) from your API, but then how do you authenticate to _that_, if you can't have any secrets?
You can't login-gate all of those, and many of those are not easily rotated.
3rd party log-in SDKs using OpenID connect can work entirely without client-side secrets using only your app IDs. Crash logging and analytics services API keys are also usually considered to be app IDs, not secrets.
They have some safeguards e.g. HTTP referrer restrictions but it's not bulletproof.
It actually can, I could create an app called com.yourbusiness.someapp, install the app directly without signing it, and use yourbusiness's API key.
For the JavaScript embed APIs I could create a fake root cert, fake DNS, fake HTTP cert signed by the fake root cert, serve https://yourbusiness.com/ from my in-house server to my own laptop, and then issue queries to the Google API using yourbusiness.com as the referrer. (Or hell, just create a Chromium fork that serves up fake referrer headers.)
It's just that it's not big enough of a threat that it has become an issue.
The genius and the craziness of GPT-4 is you can make whole app with a prompt like "now you're a clown painting custom faces on kids based on their favorite animals" and some glue-code. Needing to add a 3 layer network infrastructure with isn't appealing I'd imagine.
Far, far, far better than nothing.
Don't you still have the same problem? Your backend would also require a key for the client to communicate, which would still be embedded in the client binary.
You go through traditional auth channels (username / password) or you generate a key per app user to talk with your backend.
Or, you keep the backend you control open and implement controls to combat abuse, such as rate limiting and ip blacklisting.
Whatever chosen, the objective is to protect your API key and make sure the application is being used according to its purpose.
An API key directly to openAI allows for any use under the sun — botnets, new prompts, etc., and can drain money, put your account in bad standing, or even get you in (potentially serious) legal trouble. Using your own backend, you can do things like hit the openAI moderation endpoint, inject the correct prompts into whatever you’re sending to openAI, etc.
The main thing is you have a limited API specific to your app offering which significantly lowers the damage possible. You absolutely always want to give users the least privilege necessary for whatever use cases being provided for — this protects both you and your users.
Interesting that it's suddenly become so "controversial" to suggest circumspection with respect to this subject...
This might not be insightful from your perspective, because you've thought about it before, but it needs to be said. Just like a NO DIVING sign in the shallow end, most people already know, but some people who don't know might not know if it isn't stated.
I don't see, why ChatGPT changed that, I only tried it a little bit so far, but it doesn't usually give you a ready program, right? It gives you snippets, that might work, or not, but in my case required understanding of the domain. So I could adopt the scripts to my need, but I doubt a beginner could. At least not for anything non trivial. Also those beginners can ask a million stupid questions to the AI that just patiently answers. So yes, those answers can be wrong, but that can happen in a forum as well and even in university occasionally I was taught some BS.
So yes, ChatGPT changes the game a bit, but not that drastic. If you want to become a professional programmer, you still have to get your hands dirty and grind away the basics. But you cannot skip certain things, or you never manage to get even a mid sized project running performant and stable.
And if necessary, it would be probably trivial to weed out "programmers" that aren't really programmers by asking them some questions directly.
I agree with your assertion that non-trivial things will weed out "programmers", I just think that ChatGPT will get people further and that definition of "non-trivial" will shift a bit - possibly far enough people will have an easier time leaking their API keys through Frankenstein apps they post online.
Are you ok to name some map apps that are doing this, that you're aware of?
That'll let others dig into those apps, (hopefully) report the security issue, and (again hopefully) get the app makers instead using a better approach for their next releases.
That only applies for internal api calls, at which point the requests/binary won't contain the openai key?
You just need some very permissive pinning, where you require any publicly trusted CA, to prevent MITM attacks. Basically only trust the root CAs a phone already trusts by default. You don't need coordination between the server and your client to implement this. All you have to do is prevent your TLS calls from trusting any certs signed by manually trusted CAs that Proxyman/Charles/etc might have had the user add.
Of course, that'll only delay the API keys leaking. With a jailbroken iPhone and Frida you can effectively disable cert pinning checks. Or extract the keys from memory, or binary analysis, etc.
Yeah but I have certs signed by trusted root authorities a la letsencrypt?
If you're curious about this sorta stuff, I definitely recommend checking it out.
Or you could generate a per-user subkey with a quota if the upstream service supports that.
The only exceptions are basically some really hardcore DRM like denuvo and hardware certificated drm. Even those aren't safe from deteremined people.
- Unlimited, or at least a much higher limit - right now they are restricted to 5
- Ability to set a time limit on a key - I'd like to create a new key that's only good for the next hour when I try out a new thing that asks me to paste in an API key
- Abiliy to set a budget for an API key. Giving an app a key with a $5 total budget - or $10/month or whatever - would be really neat.
- OAuth support. Let me OAuth connect an app with my OpenID account - then I don't have to know what an API key is, I can grant it permission to spend my API credits, and I can revoke access later
- Let me see how much money each API key has spent
- An option to log everything an app does with my API key would be cool - I already have ChatGPT logs and rely on them all the time, but having that for other random applications would be excellent.
Nothing is going to change.
Store the key in your code but in a basic encrypted string, and then decrypt it at runtime.
Yes, it's still easy to get if someone is motivated, but it's a lot harder to read the machine calls figuring out what method was used to encrypt the string (make it a method that can't be figured out from only the encrypted string), than it is to read the plaintext key from the Plist.
Bad in theory, helpful in practice.
You're much better off proxying calls from your own server API, having proper rate limits and authentication, and a strict API surface that doesn't permit arbitrary calls to whatever APIs you depend on
1) Calling OpenAI directly is one less hop, so user gets lower latency
2) Not having to set up / maintain a backend server = get to market faster
There are some very popular GPT apps recently that are obviously putting their API keys on the client side - won't name them but they've been featured quite a bit.
The downside is not as bad as people think. Worst case, someone takes your key and what, plugs it into their own app, costing you a few bucks?
- OpenAI keys have a hard budget limit that requires manual approval by OpenAI anyway
- Not much privacy risk - unlike other API keys, OpenAI APIs don't allow you retrieve previous data AFAIK. There are some APIs to fine-tune models, but I seriously doubt any of these consumer apps are doing this now.
- You can just create a new version later and revoke the old key. And now you've broken the thief's app.
My guess is the developers were well aware of the tradeoffs. Just felt it was more important to get to market faster, than to batten down all the hatches. They're probably right?