SecretService - A service for securely storing application secrets

Please excuse my lack of sugar-coat as I read all of this.

I see people on here suggesting a new datatype. You can’t attach a datatype on a key that’s already encrypted in the first place. That’s like putting more patties on a burger until it’s no longer able to be eaten. There comes a point where too much security on an object that’s meant to be secure by design becomes worthless and/or a regression because it has been abstracted past what common sense dictates as feasible.

I also see people on here suggesting a new API. You don’t need a new API to handle one task. That’s too much work for something that can be attached to a pre-existing API that already exists. I’ll explain more as you read through this post.

First and foremost before I begin my lengthy essay, I believe that this functionality is very much needed. Storing tokens on a game that can be exploited, leaked or both is a thought that should not be attempted nor thought about in the first place. It defeats the purpose of what authentication tokens/keys do and why they’re meant to be stored in a secure place. Storing authentication data in a lua script (as a module, not a textfile… this is roblox so, for the sake of you and my sanity… I’ll use roblox terminology) is not how people do it in the industry. Authentication tokens are almost always (unless you’re naive and new to this field) stored in a secure database and fetched when needed. It is never stored freely within a game, a file on your desktop, etc.

For some odd reason, this has never been addressed and I feel like it only furthers the point that roblox wants you to use their tools over any third party. Rightfully so. I mean, who wouldn’t want a full-all-in-one solution to all their game-making needs. What roblox doesn’t realize is that their toolchain is subpar at best. You cannot force tools on developers (even more so seasoned ones) and expect them to love it. You cannot re-invent the wheel time and time again only to find failure and public outcry. Third-party tools allow us to complete tasks that the roblox platform can’t do in a feasible manner and provides experience to industry-leading technologies that can be used outside of roblox. Blocking these technologies through subtle yet prevalent means to indirectly or directly push sub-par tools only leads to the regression of this platform and the mitigation to features where users can and will benefit from.

Any who, I don’t know why I bothered to create a feasible solution to this since it’ll either be ignored like better support for plugins or stolen and rebranded into something that doesn’t help but, here we are. I have created the most roblox-istic mockup / prototype I personally thought would work. Excuse the somewhat ambiguous naming. I’m not getting paid and I did this to prove a point. Petty or not lol.

As you can see by the image, I’ve attached it to the Configure Game menu and named it Storage with its own little tab. The visualization is quite standard as most platforms that allow you to add keys use table-tags to display data. Personally, I thought that Identifier was a bit excessive but, +1 for ordered lists. The Edit button under the Actions tab allows you to edit Name or Key Data in case you generate a new token. Delete allows you to remove the key entirely from the game.

If you were in a game and you wanted to use the items you just created, I propose the following:

-- Can only be called in a server script, not a module or local script
local keys = game:GetStorage() -- Returns an array in { name = key data } ordered
print(keys["Foo"]) -- Prints out the key data value for the key named "Foo"

If you really want to create an entire service for one functionality because you feel that it’s absolutely needed, I propose the following:

-- Can only be called in a server script, not a module or local script
local keys = game:GetService("TokenService"):Fetch() -- Returns an array in { name = key data } ordered
print(keys["Foo"]) -- Prints out the key data value for the key named "Foo"

Thanks for bringing this up and I hope it gets implemented so that we can finally see support for third-party and not forced to use tools that barely meet industry standards.

Edit: It’s been added. Secrets Store General Availability

22 Likes

I just realised a really neat solution to this issue - Data Stores.

local KeyStore = game:GetService("DataStoreService"):GetDataStore("Secrets")

local ApiKey = KeyStore:GetAsync("Web_ApiKey")

Perhaps worth encouraging this as best practice?

9 Likes

I agree 100% with the points you’ve bought up. The only thing I would argue is that secrets shouldn’t be visible on the website. It would make more sense to hide the keys with **** (forgot the technical term).

@EllipticCurve_DHE
Datastores aren’t designed for storing secrets. This is, technically, the best way as of now to store secrets. However, it is far from ideal. For one, you have to use a third-party plugin to actually insert and modify secrets.

It is also impossible to rotate secrets on a recurring basis. If this feature was implemented, I still find it highly unlikely that there will be native support for rotating secrets. However, it would have an API. You could use that API to rotate keys on your end via a Cron Job.

2 Likes
function _G.AddKey(KeyName, Key)
	local Success, Error = pcall(function() game.DataStoreService:GetDataStore("SecretsData"):SetAsync(KeyName, Key) end)
	if not Success then
		warn(Error)
	end
end

function _G.GetKey(KeyName)
	local Success, Value = pcall(function() return game.DataStoreService:GetDataStore("SecretsData"):GetAsync(KeyName) end)
	if Success then
		return Value
	else
		warn(Value)
	end
end

I wrote these real quick, it’s not too bad.

My point is that datastores are not designed for storing keys in the first place. You shouldn’t have to use any code to store a key.

As a Roblox developer, I see a conflict of interests in the recent issues around code safety, as seen in this post.

I want to help mitigate one main issue with the review system currently in place, the security and secrecy of important API and other security Keys fellow developers use for their games.
My suggestion is very simple, but should help relieve developer’s concerns about having their keys bring seen by strangers, if their game code becomes under review.

I would like Roblox to create a ‘secure’ store, similar to that of the current data store service, that allows developers to store API Keys and retrieve them using server code when required.


TLDR
Provide a way to separate API Keys from visible game code so that Roblox moderators cannot see secret keys.

If Roblox is able to address this issue, it would go a long way to easing concerns with the current review system.

If you are a fellow developer and agree with this request, please reply below with what you would expect this feature to look like, and why you believe it is necessary.
I will be honest and say I don’t use this platform enough now to get use out of such a feature, however I’ll push this request for those concerned.

6 Likes

I think that code review should only be invoked when the code in question is proven to be malicious. Moderating codebases based on words, phrases or anything of that nature seems rather stupid as a normal user wouldn’t be able to view the contents of your codebase unless they were to exploit. Even then, they’re willingly breaking into your game for the sole purpose to gain an advantage.

I agree that a datastore should be provided to help correct this issue. However, datastores are maintained by Roblox and can still be viewed.

1 Like

A way to manage secrets better should be added regardless of technical staff getting flagged to malicious code – the point is that this is basic security when working with third party apps, we shouldn’t be forced to put application secrets in our developer code, or devise unintended workarounds for this to avoid having to put it in the source (e.g. uploading to a private model inserted in the game, or in datastores, etc).

The focus for this feature request should be on that and not hinging on the code review argument because the latter is not a strong enough one for Roblox to consider implementing this. Roblox staff are not the people you should be worried most about seeing your app secrets.

5 Likes

Yeah looking back at this thread I definitely misjudged what it’s main arguments should be. I’ll be sure to edit it later with stronger arguments.

Thanks!

1 Like

Our team relies heavily on 3rd-party hosting and having our API keys easily readable in our codebase is a major security flaw due to the number of people who have access to the game. Someone who gains access to the game via a compromised account could easily wreak havoc with this kind of full access, and a proper, secure solution to storing private keys is absolutely necessary.

6 Likes

My current solution is using DataStores - unfortunately without PKI / Key rotation no solution would be impervious to that. I’ve been trying to develop a key rotation technology that would integrate with something like HashiCorp Vault, but don’t count on it :stuck_out_tongue:

6 Likes

I want to bump this feature request again as it is now even more pressing for me. The following is not the same as the example I put in the OP. It is a completely separate game that relies on external services and application secrets even more.

Currently, I am developing a game which relies heavily on PlayFab. Before going on, I need to make it clear that this is not Roblox’s integration of PlayFab but the full thing; I must manage the API secrets myself. The entire game’s economy, datastore, matchmaking, analytics, and leaderboards run off of PlayFab. It is the backbone of the entire game, and a potential compromise could be catastrophic.

If a malicious group or individual were to get ahold of the secrets, they could damage and destroy numerous aspects of the game beyond repair. For example, an attacker could easily manipulate the prices of in-game items, adjust user currencies, batch remove items from players, or give items to players. Alongside that, they could permanently delete players from all records (a mechanism for GDPR Right To Erasure, absolutely no way to revert), manipulate player data at a huge scale, or completely reset player statistics. All of which have little to no way to recover; the possible scale of damage here should not be underestimated.

As of now, I have not settled on the most viable solution to this problem, and because Roblox does not provide an official, first-class, way of safely storing business-critical secrets, these are all crude workarounds.

  • I could store the secrets in a datastore. However, the problem with datastores is that they aren’t designed with secrets in mind. Once secrets are in a datastore, they can be easily read back, making them ineffective at storing business-critical secrets. So yes, while an improvement on simple plain-text variable strings, they still do not provide a level of security that most developers should be comfortable with.

  • I could store secrets in a private module & and then require them in-game. While definitely an improvement upon datastores, it suffers from the same problem. In the event of a developer account breach or backdoor, an attacker can require the private module and retrieve the secrets. It should not be possible to read secrets once they are set unless the code is running in a real Roblox server, even in the event of an account breach. While you can argue that if you have an account breach you have worse things to worry about, the damage of an account breach is almost always reversible (with help from Roblox). However, the possible scale of damage if a secret were to be leaked varies massively on a case-by-case basis. It can, oftentimes, be far more catastrophic than an account breach, like in my example above.

  • I could run a deployment of HashiCorp Vault that would utilize a custom authentication mechanism for Roblox that doesn’t rely on secrets. The problem with this solution is that the authentication method is very volatile. It relies on internal Roblox APIs and could change at any moment without warning, breaking the entire game. Without using that authentication mechanism, I have to, again, rely on secrets; defeating the entire point of this solution. Moreover, maintaining a deployment of Vault is not something that I have the time, resources, or knowledge to do, let alone the average Roblox developer.

As you can see, there are currently no truly viable options right now. All methods have significant drawbacks that cause them to not fully meet the goal of protecting secrets. With the risk involved with using external services in the previously discussed way, I am forced to invest far too much time into investigating, testing, and implementing methods of protecting secrets.

This is an essential feature that should be supported ASAP. Not providing a way of protecting application secrets is an absolute disaster waiting to happen.

22 Likes

I support something environment variable style, so we can do something similar to process.env.API_KEY just so the actual api key itself isn’t built into the code. I often see instances of Discord webhooks being leaked in #help-and-feedback:scripting-support, mostly because of users not being informed about the dangers of leaking api keys or they just forgot about it. If Roblox encouraged the use of the proposed service or even my idea, then this would happen much less frequently.

And being able to read the secrets of a place should be a configurable permission by the owner.

8 Likes

Going to bump this since this is desperately needed. The lack of having a secure way to store & retrieve API keys for various backends that allow for much more control that datastores or the API lacks is incredibly frustrating as a developer. Having to invent, sorry for being blunt, dumb / hacky solutions without any guarantee or premise for security is limiting in terms of my own workflow.

I get that you [Roblox] want datastores and the rest of the API to be used over third-party tools but, so far, in terms of literal functionality with datastores, it’s incredibly hard to do simple things as I would do normally. As I am writing a game’s backend, not having the ability to purge keys, a sane way to migrate my own datastore or a representation of what is in my datastore (as a whole) in a clean and visual manner makes using datastores a lot harder than it has to be for no reason.

This is the primary reason why I personally use API keys. This is the reason why I have to go out of my way to create an external database, a web-based API and use better tools that provide me a better ideas as to what I am doing & why. When developers are given a database solution that scales but, is so basic to the point where you literally have to invent sane methods to use it correctly (think ProfileService, Datastore2, etc) is honestly a slap in the face and doesn’t make me, as a developer, want to use it. When it’s purposely forced and you have to use all these user-created wrappers (which create bloat) all because datastores can’t work or function like it’s supposed to, it pushes a lot of experienced developers away.

Although this is one use case out of a bunch mentioned here, it’s unfortunate that the basic freedoms that developers naturally have are alienated into forced tools, lack of functionality and assurance. If you [Roblox] are going to force stuff on us, at least provide functionality that allows us to actually work without having to worry about issues on your end. At the bare minimum, please allow for us to properly store secrets without having to worry about it getting leaked. Datastores are not a valid solution to this and quite frankly, work as a hacky and unprofessional way to store a key that can give an attacker a portion of your game’s backend.

This whole notion of making third-party tools hard to use is the primary reason why industry teams do not use this platform. Third-party tools and having access to said tools with a proper, secure way to store API keys are integral to most teams in the industry. As an experienced developer myself with actual experience in the industry, the differences between the two and having to literally dumb things down for no actual benefit because of this is more pain than actual worth.

This should be taken much more seriously than it is now; especially when almost every other platform other than this has this functionality built in or at the very least, supports it. Sometimes as a developer on this platform, you see a lot of people say that “Roblox isn’t a kids platform” and then, when you start to develop on the platform and realize how limiting it really is in terms of third-party functionality, it starts to feel like you’re being treated as a kid. Begging for the bare basics just to complete something that should be relatively straight forward.

9 Likes

You don’t need modules as complicated as this to have sane datastore interactions. The datastore handler for an older game of mine is only like 50-100 lines of code (primarily to handle the case of “do not accidentally save default data over existing data”), and it works just fine, never received any reports about issues with player data. It would be nice if Roblox could provide a layer on top of datastores for easier handling of player data or versioned data with user-defined migrations, but the picture you sketch here isn’t correct in my experience.

Something along the lines of SecretService is definitely needed so we don’t need to expose secret values in our code or in a datastore key, but most of the arguments in your post are better suited for other feature requests specifically about datastores or official clients for external tooling, and they are not all that relevant for this feature request.

13 Likes

Given the immense support and gravity of this issue, I believe it deserves to be bumped.

I also have a use case:

For my Polaris-Nav plugin, I’ve implemented a form of O-Auth which works by having users visit a game. They then receive a code to enter into their plugin, and the plugin receives a refresh token which is just being stored in the plugin settings. Plugins need access to the secret service as well.

In addition, the Roblox places themselves use a refresh token which I have saved into the settings module script.

The refresh tokens are a base 64 encoded string. It would be nice if binary encoding were allowed though.

13 Likes

This is probably the biggest issue I have when storing API keys, Would be really nice if this was a feature. But right now I think saving the key in a datastore might be a possible alternative for now as if someone gets the code, they still need to be running it in your game to access the exact datastore save.

2 Likes

Bumping this. We are in dire need of some sort of system similar to the datastore service that allows developers to store a secret that cannot be easily read by anyone. The implementation of a “SecretService” would allow for things like API keys and other secrets to be securely stored with peace of mind.

Here’s another bump. This is critical for any game that wants to reach external websites.

6 Likes

After all these years, it has finally arrived.

5 Likes