#How to secure your API Key

105 messages · Page 1 of 1 (latest)

pliant olive
#

Keeping your API credentials a secret is a well known fact among all developers, but we have seen a fair share of new developers that may not be familiar or do not understand this concept to its full extent.
Every tutorial always cites the fact the you need to use your API key but it always let it for the developer to figure the best way to secure it.

A good start is from taking a look at this guide by OpenAI on best practices: https://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety Thanks @sonic raptor for pointing that.

This is a tutorial that will cover the most common occurrence of a grave mistake: packing the API Key on the client side.
No matter what you do, the access to credentials of your OpenAI Account should never be part of the client package. Doing so allows to malicious people to steal your APIKey and cause all sorts of problems to you, ranging from wasting your money to intentionally misusing your account, risking getting you banned.

A lot of people who just want to share their fun creations / side projects don't have a real concern for the usage limits, this is easily solvable by setting a quota on the usage panel: https://beta.openai.com/account/usage
Some may want to go even further in the security and have a well defined whitelist or authentication methods to prevent anyone from ever using your OpenAI access from any methods that are not your account.

For any use case, you can take this as a basic example of how to set up a proxy on Cloudflare for free.

Lets get started!

#

There are many way of doing this that are completely free, but for this example, it will require that you have a domain of your own.
If you don't have one, you'll not be able to use Cloudflare Workers, but stay calm, there are other methods and I will post about them in the future.

#

Ok. So you have it working and your domain is now on Cloudflare. Lets get started.
On the nav bar of your site's management page on cloudflare you will find "Workers Routes"

#

Click on Manage Workers and on the next page click on Create Service

#

Give it a name. Choose any template, and then click "Create service".

#

Now, you'll edit the service to transform it into a proxy. Click on "Quick edit"

#

You are now on the browser editor. This editor contains a simple IDE on the left and a REST client on the right.
Replace the example code of the editor with this example:


/*
 * This is a proxy script that redirects every request to OpenAI API
 * It adds your Apikey to the request header, so you don't have to add it on the client side
 */

const Apikey = 'sk-xxxxxx';

export default {
    async fetch(request, env) {
        return await handleRequest(request).catch(
            (err) => new Response(err.stack, { status: 500 })
        )
    }
}

async function handleRequest(request) {
    const { pathname } = new URL(request.url);

    const url = `https://api.openai.com${pathname}`;

    const headers = new Headers(request.headers);
    headers.set('Authorization', `Bearer ${Apikey}`);

    const response = await fetch(url, {
        method: request.method,
        headers,
        body: request.body
    });
    return response;
}

Remember to replace sk-xxxxxx with your own APIKey

#

This is the most basic proxy, I don't even use features such as environment variables which would be the correct way of storing the API Key.
When you get a request this code will add the API Key to the headers, then, it will make a request to OpenAI API, and after that, reply the original request with the data it got from the API

#

you can test this by using the REST client on the right.
Chage it to POST, and on the URL, it should already be filled with the URL of your worker. Add then the path of the API /v1/completions it will look like this:

#

click on "add header" and add the ContentType application/json like this:

#

and then, you can pass this parameters to the request:

{
    "model": "text-davinci-003",
    "prompt": "Tell me a good Joke",
    "temperature": 1,
    "max_tokens": 1300,
    "top_p": 1,
    "frequency_penalty": 0,
    "presence_penalty": 0.6,
    "stop": null
}

If you are doing requests manually on your app, this is the kind of data you already know how to use.
If you are not doing it manually, don't worry, this is the kind of data that the lib generates for you when you use it.

#

it will look like this:

#

if you do it right, you will have a response like this on the right after you click "Send":

#

Why did the chicken cross the road? To get to the other side!

#

Your proxy is now working. Anything anyone sends to your worker url will be sent to OpenAI API without the need of knowing the API Key.

#

Now you may want to add some extra measures such as your own Key pair authenticaiton system, rate limiting, or anything you can imagine. use this example and the CF Workers docs to customize your proxy: https://developers.cloudflare.com/workers/examples

Documentation for Cloudflare Workers, a serverless execution environment that allows you to create entirely new applications or augment existing ones without configuring or maintaining infrastructure.

#

You can directly use the URL of this worker on your app, but, if you want to be fancy, assign it to a subdomain.
On the DNS page, create a cname for the subdomain, you can name it api or openai-api, and set the content to your own base domain. Be sure that this cname has the "cloudflare" icon turned on.
Now, on the Workers Routes page, add a rule like this: https://subdomain.your-domain.com/* it has to end with /*
it will look like this:

edit: the Route should end on *. it should have been https://openai-api.lugui.in/* on the screenshot

#

you should now be able to use your own domain as a the OpenAI API, where this proxy will inject the API Key on every request

#

We are done!
If you use any of the OpenAI libs, you just have to set the base path when instantiating it on your code like this:

#

replace with your own domain

#

Now it is all done!! Enjoy your app that doesn't leak the API Key =D

#

How to secure your API Key

stray copper
#

For Swift / iOS developers as well, using CloudKit is a good option for cloud storage and user tracking.

#

Also same with Firebase

mossy smelt
#

any idea what firebase service would be used to hide the api key from the client side? would it be functions?

stray copper
stray copper
#

I may also post my own tutorial on using CloudKit to store the OpenAI key in the future too.

#

It could be helpful for any iOS / Apple platform developers here.

stray copper
#

Alright, so I'll provide the full instructions on how to use CloudKit to store the API key off of Apple's own server so that it doesn't end up getting stolen. This is going to also implement my own OpenAIKit as the example use case on using the key, though using raw URLSessions is perfectly fine too.

#

I'll assume you already have the following:

  • Knowledge in Swift and utilizing the concurrency methods in Swift.
  • An Apple account
#

So more or less, create a new app project, and set it up as a normal SwiftUI project using Swift.

#

Next, go to your Target scheme and go into the Signing & Capabilities tab. And there, you'd want to add the iCloud capability and check off "CloudKit". From there, make your own container, and make the name the Bundle ID of the project. The name will be red at first, but just press the refresh button, and it should turn white.

#

Next, head to "icloud.developer.apple.com", sign in with your Apple ID, and click on "Databases". Once you do, you'll be at a page that looks like this (Don't mind the error, if nothing loads, give it a couple minutes to build the database):

#

Go to your Record Types page, and click on the blue plus sign, and then you'll be taken to a page like this:

#

Enter the name of the Record Type, and choose a name for the string field (make sure it's a string field); and then press save.

#

Then, go to the Indexes page under the same header, and click on the record you just created. There, you will need to enter a Basic Index as the "recordName" so that the function mentioned later on will be able to fetch the record. And then press save changes.

#

Go back to your Records page, and press the blue plus button next to "Records", to add your API Key, as shown here, and then press save:

#

From there, go back to your project, and make a new file. Call it CKManager, or whatever you'd like. Add the CloudKit import to your file, and then add this class to the file:

#
class CKManager {
    static let shared = CKManager()

    private let publicDb = CKContainer.default().database(with: .public)
    
    private init() {  }
    
    func getApiKey() async -> String {
        let recordName = "<INSERT-RECORD-NAME-HERE>"
        let fieldName = "<INSERT-FIELD-NAME-HERE>"
        
        let tokensRecordID = CKRecord.ID(recordName: recordName)
        
        return await withCheckedContinuation { con in
            self.publicDb.fetch(withRecordID: tokensRecordID) { record, err in
                if let record = record, let key = record[fieldName] as? String, !key.isEmpty, err == nil {
                    con.resume(returning: key)
                } else {
                    con.resume(returning: "\(err?.localizedDescription ?? "Unknown Error Has Occured")")
                }
            }
        }
    }
}
#

This is basically making an async concurrency function that returns your API Key as a String. So now to use this function, use whatever async method you have integrated into your project, and more or less just call the function from the singleton, e.g.,:

#
let ky = await CKManager.shared.getApiKey()
#

From there, if you'd like to use my library, it's as simple as adding the key to the Config object, initializing an OpenAI object (don't forget to import OpenAIKit), and using whatever function you'd like to use:

#
let prompt = "An Avocado Chair"
let config = Configuration(organization: "MarcoDotIO", apiKey: ky)
let oi = OpenAI(config)
let moderationResult = try await oi.checkContentPolicy(parameters: ContentPolicyParameters(input: prompt))
guard !(moderationResult.results[0].flagged) else {
    // ... Insert whatever method to handle error
}
let imageResponse = try await oi.createImage(parameters: ImageParameters(
    prompt: prompt,
    numberofImages: 1,
    responseFormat: .base64Json,
    user: "MarcoDotIO"
    )
)
#

So yeah, enjoy not having your API Key stolen when publishing your app using OpenAIKit :D

#

If any of you guys have any questions, leave them here.

stray copper
kind cedar
#

Code c++ to move in unity

lime apex
#

good

sacred eagle
#

What does the OGUC say about fire protection?

keen chasm
#

hi

#

bruh

clever mauve
#

Hi

stray copper
#

Hi, I'm new to APIs and programming. Just want to make sure I understand correctly, if that's okay?

  1. The API key is generated and provided by the ChatGPT service, correct?
  2. The API key is unique to your account and your API usage, correct?
  3. Is the reason you want to secure your API key, so that others don't abuse it? I'm assuming maybe ChatGPT will ban it or something if it is deemed malicious?
  4. How does ChatGPT manage all the API key released and ensure they are not used maliciously?
dawn linden
#

if you are using JS use npm install dotenv, add the appKey in the .env file like this: OPEN_AI_APPKEY: XXX-XXX-XXX-XX and do not share what file

pliant olive
hoary cedar
#

/

wheat crow
#

Does anyone know if there's an ETA on chatGPT API access?

balmy glen
#

HI

hollow sky
#

cup

devout fern
pliant olive
#

Yes, all OpenAI services are paid

devout fern
fluid terrace
stray copper
# fluid terrace i mean...doesn't this eventually grab the api key and throw it into your process...

Regarding that, that would be a whole lot more to go through when it comes to someone very knowledgable with the iOS architecture. Kind of equivalent to a padlock, sure you can easily crack one open if you know how; but 95% of people who would want to don't know how. So really it's way more of obfuscating the key easily that no one sniffing through the binary initially would want to go through the hassle with. Also that iOS encrypts process memory last time I've heard.

#

Even if you were able to get the key, iCloudKit makes it easy to just swap out the key for a new one without any issue from the end user's endpoint.

pliant olive
fluid terrace
#

need to make the requests server-side -- then you can change the key when you need to, and it never touches an Untrusted(tm) device

pliant olive
#

thats what the first post on this thread does

fluid terrace
#

ah, i was commenting on the one that used CloudKit not the one b4, my bad

stray copper
fluid terrace
stray copper
unkempt rose
#

a man flying with beautiful baby

errant bolt
#

Sap

zenith grove
pliant olive
#

there is no way to get mroe APIKeys AFAIK, but if it is possible to get an extended limit on that it might be on the same contact as the "Request limit increase"

#

let me know if you get it =P

#

but... well, if you do thois proxy I made in my example, you might be able to "subdivide" your apikey access

#

by making your own apikey to access the proxy

#

just add somethink like a hashed password list on the worker.. then pass it as a header

zenith grove
#

I need a lot of dollars for the bot

pliant olive
#

there is a buton for it on the limit settings page

jade phoenix
#

Thank you for taking the time to write this tutorial. I've been scratching my head on this issue. Question - If anything anyone sends to your worker url will be sent to OpenAI API, then doesn't that still have potential for abuse? Now someone can just start using your worker url instead of your key. How can I secure the worker url?

craggy field
#

it always crahes when I ask difficult question

#

or when I ask to code something for me

#

like code me a minecraft pvp bot

#

even when I said that it is only for my privat server

bronze elbow
#

I need tutorials for Learning

pliant olive
shell geode
heavy nexus
pliant olive
rocky siren
#

@pliant olive what are some other options? You mentioned posting them in the future.

pliant olive
#

that would be to make the request to the OAI API on the server side of your app

rocky siren
#

Do you happen to have a good tutorial reference for how to do that?

pliant olive
#

that completely depends on how the server side of you application is built