-
-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide a way to load resources without running scripts #4925
Comments
Hm, so I guess the pragmatic way to implement it would be by adding a Alternatively, we could implement this into the The not so pragmatic way would be to implement secure casting to resource for every object that could be loaded. But as this is only about .tres resources (if I got everything right), we maybe better implement it on a |
We've been discussing it a bit, and it should load scripts, because otherwise you lose the benefits of scripted resources. It should ignore bundled scripts and external scripts loading from the outside of Another option would be to validate the loading resource against some sort of schema. I can imagine you can refer to your internal type as a schema and the engine would strictly validate serialized members against it. This way you can avoid extra fields being added and extra types being used, preventing the possibility of a scriptless attack. |
The schema would not work that great in my context because it's variable for each game designed with the framework so adding a schema would put that burden on every developer using the framework. Maybe instead something like an |
A schema would be resource based. So ideally it would require zero work from the developer beyond designing their resources.
Scripts are not the only attack vector here. And it shouldn't be a resource decision, it should be a loader decision. Because otherwise the bad actor can still replace the script in your resource that allows it with their script. Which is why it should filter out scripts based on their quality while loading: if they are bundled or if they are external and not from |
@YuriSizov good call |
I'm all for it, and actually, if this proposal gets accepted and an implementation idea vetted, I'd like to offer to sponsor someone to implement this new feature. |
Just to bring this over from twitter: It is my opinion that the correct way to solve this, would be to completely disallow embedding script within any kind of godot resource format. Be that resource, scene, or others. Yes, this would include removing the entire embedded script functionality. (Which already lost most of its usefulness due to lack of feature support for things like class names.) (I been theorizing a way to inject code through the icon.png and default audio bus files present in all projects. Completely outside of the control of the developer.) |
I personnally use them sometimes, at least when prototyping.
Do you mean, corrupting a pck file? You can litterally inject anything you want inside it really easily. |
No, this wouldn't touch the PCK at all. But it's irrelevant in any case. |
I'd also lean towards removing embedded scripts, which could be part of solving this, for all their limitations. The way they come to bite you when your project grows and you get errors in embedded scripts, for instance, you quickly lose the little time they saved you initially. But that likely depends on finding some consensus, I remember quite a few people liked them. |
Not sure how relevant this might be, but https://twitter.com/reduzio/status/1549655657183416320 - @reduz here talks about a "JSON-like" format? |
This was proposed in the past and rejected: godotengine/godot#54553 Since then, built-in scripts have been improved significantly, so I wouldn't remove them (even though I don't use them myself).
I think reduz is talking about ConfigFile here (whose syntax is closer to TOML than JSON). |
Is this also potentially an "abuse" of The use of |
I think it doesn't matter that it wasn't initially meant to be used that way if it's possible to support the use case. Resources are a great tool for modding, bringing in user-created data into Godot in general, they have the potential to save you a lot of code and coupling (because of how there's this built-in load() system that'll load resources only once), they can work as text in debug builds for quick bug fixing, and binary in release builds for smaller file sizes and faster loading... They have pros, they're just lacking several small improvements, in this case for user safety. |
Makes total sense, yeah, if the community uses them then built-in scripts are here to stay. |
Maybe it could be useful to not allow embedded scripts on load? |
Yes that's already an idea in this conversation, see these two comments by @YuriSizov: |
Personally, I don't see a problem with the In my opinion, And as mentioned above, you can safely use At the same time, I am completely in favor of having an analog of the |
Custom resources (in 3.x) require C++ modules, which may be beyond the scope of some people's intended usage, or skills. |
That's pretty arbitrary. Resources are just scriptable objects with built-in serialization capacity. In C# land you can serialize any class with XML in the same way, and it's used in games all the time. I think, that's the go-to way in Unity, at least from my experience with digging into game saves of games made with it. So from the ease of use perspective and the level of integration into the engine, custom resources are perfect for storing user data. |
That's also exactly what the docs say. 😉 |
No, you can just extend the Resource class and you get a custom resource. I heavily make use of that in EgoVenture. |
This may be somewhat addressed by #5010 |
One of Godot's big strengths is streamlining common gamedev tasks, so you don't have to spend time reinventing the wheel. Saving and loading the game is one of the most common functions that devs need to implement (up there with having collision detection), but currently it isn't as straightforward as it could be. And of course, from the game developer perspective, as far as the player's experience goes, the only real question that needs to be answered is "What data goes into the savefile?". Whatever method the developer uses to cram the data into a save file, and then pull it back out unchanged, will function equally well. So, since this is a common feature where the exact algorithm doesn't matter to the end product, the developer should be able to just trust a built-in Godot feature to do it properly, and not worry about what's going on under the hood. As is, resources are almost the perfect way for Godot to handle saving and restoring states. It uses Godot's unique strengths to handle savegames easily, and doesn't require futzing around with special cases to handle unsupported datatypes (like Vector2s in json). It's unfortunate that this method is currently unsafe, but if inputs can be sanitized, it's a perfect way to never need to think about the underlying savegame/loadgame algorithms ever again. |
Or just, save the resource using File, not the ResourceSaver, and encrypt it. Or, since your savegame boils down to just being a dictionary anyways. (And lets be honest, custom Resources are just fancy typed dictionaries.) Just use File.store_var() Resources as savegames only blipped up recently as a consideration, since I think people have finally caught on that json text files are a poor idea. But the next step is raw binary data dumps. Saving and loading a resource is actually more (or at least identical) work than making a dictionary and saving/loading that. But that isn't even the topic of this issue. The topic is the glaring security problem caused by Godot automatically loading resources that could contain malicious code. Which is only a problem in two cases: A developer using resources where normal files would be sufficient. Or a malicious agent placing resources where it will override the pck contents to inject code. (Which requires the developer to have omitted the res.// in the file path.) |
I agree that
|
Prevents arbitrary code execution: godotengine/godot-proposals#4925
Is there any way to use unique tokens for custom resources. That way it will be safer to use. |
Are you referring to encryption or signing of custom resources? That's a topic for a different proposal. Godot already offers way to save/load encrypted files, but I doubt there's a built-in signing mechanism you could make use of. |
Hi, sorry I never replied with the implementation we use in V-Sekai since last year. @fire pinged me to reply to this issue. If you're interested you can find the solution we use here: This feature was rejected from core due to security considerations, but I think another discussion is needed. I'll explain. I suspect this patch is sufficient for the types of resources that a game, for example, might use to store a save game or a level, usually a custom While we (the V-Sekai team) suspect the above patch solves the pertinent problem to resources in particular, it does not make all types of resources entirely secure. It is critical that scenes in particular be further validated in terms of node paths or animations, and possibly other cases. Also, once the resource is loaded, most of the other security risks can be handled in script through a combination of recursion and property validation. [Here is an example of one such possible implementation.])https://github.com/V-Sekai/v-sekai-game/blob/main/addons/vsk_importer_exporter/vsk_map_validator.gd) Note that this type of validation logic is not secure in general, and must be adapted to your game. That said, because of its applicability to |
It would've been great to have an ability to granularly restrict what loaded scripts can and what they can't do, something that was proposed in here: #4642 in conjunction with #5010 This would enable the creation of games that can be modded safely without resorting to any extensions or custom solutions. |
I have created a small library that should solve this problem for saved games. It basically works similar to the whitelist @lyuma posted, but it will flat-out refuse to load any resource file that contains resources of type GDScript. This will probably not be good enough for user generated content where you may want to allow some sandboxed scripting, but it should work decently for saved games which usually will not contain any inline scripts, so it's a nice 80/20 solution I think. |
A resource is entirely capable of pointing towards a script definition outside of itself, or even outside of the project. |
Yes it is. But for this to work, the player would have needed to download and place this script in addition to the save game file on their computer, so I wouldn't think this is a practical attack. That being said, if you have comments regarding the library it may be better to open up an issue there instead of using this discussion thread. |
I'm sorry I forgot to link this here. It's a bit scary in that it's security critical code, but on the plus side, in this form it would be fairly easy to audit and ensure it functions identically to the resource loader. The risk of this approach is if Godot's resource parser ever diverges from this code (one if statement is different, for example), then that difference in code path could be used to create an exploit. My expectation is Godot would increase FORMAT_VERSION if it changes the format, but if its parsing code is not careful and they add the new features to the old format, it would create an exploit. I think this approach would be safe if it explicitly checks for a specific Godot major and minor version (e.g. 4.2) and human review is done on each version to ensure it continues to match parsing logic. Personally, I'm willing to take this approach because I think it is less error prone than the failure case of accidentally using an unpatched engine, or having validation built into Godot and having security critical code decay by mistake during an unrelated change. Let me know what you think about this design. |
Since nobody seems to have mentioned it, I'll point out that if loading resources can skip scripts it would also address godotengine/godot#65393 |
I don't have a very deep understanding of what can be done or not, And we could have ResourceLoader.safe_load that would take something like a class as an argument and load only if what is loaded is of that class, and then follow the behaviour for the loading of the scripts provided by the loaded Resource |
PackedScenes separate loading from instantiation, and I believe a similar approach should be possible with Resources as a whole. I suggest that the ResourceLoader can have an optional parameter to load resources (and by extension, PackedScenes), but while prohibiting/delaying script initialization/execution. It's not quite the same, but I think PackedScene's GenEditState design concept could be used for this purpose. If you can load/instantiate items, and modify them before any script is allowed to run, you could filter out any scripts or unwanted content, before any execution has occurred. The usage would be like this:
the This also naturally opens the door for scripting to filter/process resources and scenes before they can effect anything within the system. |
I'd like to pop in for a minute to give my suggestion. probably stripping down scripts could be kind of a bad idea for some use cases. The optimal way would have been having gdscript work more like javascript and having a sandboxed language for the most part. unfortunately this seems to be impossible. So the laziest but most effective solution would be to sign every single script that is part of the project (or have scripts be a resource that does not get directly packed into the save but just linked). this can be as simple as a list of hashes separated by the script (unique) name on even on a text file possibly ordered just for the sake of binary search. This way you would not lose any benefit coming from resources but you would prevent arbitrary code execution (since only a set amount of very specific scripts can be ran). also by having the thing be a list any mod manager script could add their own scripts to such list. obviously this relies on the user's intelligence that he won't run any malicious script which would add itself to such signature registry. but at this point if signing is compromised in such a way it becomes the user's problem, because he ran something untrusted where the attack vector could have been a thousand other things. though if we know the user is extra dumb we may want to have a few options where to put maybe an encryption key (like the registry in windows) just so everything breaks whenever a user arbitrarily replaces the signature registry file beside the game without putting much thought into the process, it's fine if a willing user or script willingly ran by the user can retrieve the key from the registry or whatever other place itself. This is not 100% foolproof but should prevent the least determined fools from getting in trouble. |
I'm not entirely sure if this is even remotely possible but would it be possible to have a parameter that would not allow loaded resources to use certain libraries like OS, HTTPS, or Filesystem? This definitely wouldn't be fool hardy in the slightest and I'm unsure if it's even possible in 4.x but it would likely deter a non zero amount of bad actors. |
Yes, but this is not the topic of this proposal (which is only about not running scripts at all). See #5010 instead. |
Describe the project you are working on
I'm the core developer of EgoVenture, a first person adventure game framework and a contributor to Escoria, a third person adventure game framework. Both projects are using Resources to provide a savegame functionality.
Describe the problem or limitation you are having in your project
As recommended in the official Godot docs, Resources should be used to store and retrieved structured data.
This can be used e.g. to persist the current game's state (aka savegame).
As many godot developers (GDquest, among others) has pointed out: Using resources to store data can be dangerous as resources can also hold scripts which are executed when loading the resource later.
That way, malicious people can send seemingly valid savegames and embed malicious code.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
As it can be required to load scripts from Resources, Godot should feature a
SecureResourceLoader
, which specifically skips loading/executing scripts from resources so developers can still rely on resources as the most optimised method for storing structured data.Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
Very simply put,
SecureResourceLoader
should extendResourceLoader
, override theload
/load_interactive
methods and not load and execute any scripts in the given Resource.(this might sound way easier than it is to actually embed it, I still have to dig through the code)
If this enhancement will not be used often, can it be worked around with a few lines of script?
No, because loading resources is a core functionality.
Is there a reason why this should be core and not an add-on in the asset library?
Loading resources is a core functionality and security should be a core responsibility as well.
The text was updated successfully, but these errors were encountered: