In a game I am making I have a Script in the ServerScriptService to construct a string representing a mathematical formula and calculate it. In order to calculate the constructed formula, I have two options:
Program a function that takes as a parameter one string with the format of a mathematical formula and evaluates each part of it recursively until it has all been calculated and return the final result number.
Load the constructed string with the format of a mathematical formula using the Loadstring() function.
Ignoring the security risks for a moment, when comparing the two options the Loadstring() is exceedingly better in every way - I can use the math library, I don’t need to spend hours upon hours to implement a string calculator, and faster calculation times.
But when making a game I cannot ignore the security risks Loadstring() is so known for, which leads me to my question:
If the player has no way to directly change the string that will be loaded into the Loadstring(), and even when changing the string indirectly via progressing in the game (and making the game construct a different string to load) it is still always strings that are already set in the code as variables and cannot be edited in any way other than by me from Roblox Studio. The Loadstring() function will be used in my game only in that one specific place, in only one line, in a Script running on the server and parented to ServerScriptService. This script also has no interactions at all with other scripts and only outputs its results by setting NumberValues in a folder inside the ServerStorage. Will it pose any security risks to enable Loadstring() solely for this purpose?
So you should definitely still use some sort of blacklist to filter out keywords to prevent that from happening; especially for, while, and repeat since they can infinitely loop to freeze the server.
Also, always override the loadstring environment.
local f = loadstring[[return game]]
setfenv(f, {})
print(f()) --nil
If the player has no way to directly change the string that will be loaded into the Loadstring()
You should be fine. Do not put any text the player controls into loadstring, anything from a remote event should never be used, hackers can send any text they want through remote functions and events. Even their username could be a exploitive payload.
You could just get unlucky where it accidentally creates an infinite loop. As unlikely as that may sound, you can never be too safe and should still use a blacklist filter to block certain keywords.
I am always very selective and cautious with what I send over remote events/functions, but I guess scripts with loadstring require a totally different league of cautiousness.
Any idea for keywords I should make a blacklist for? Of course any word that has something to do with the DatastoreService and MemoryStoreService will go in it, but is there anything else I should add?
I already gave you a few in my previous post. You can just scan through the Lua manual and add anything that sounds dangerous:
This will not be necessary if you override/delete the loadstring environment (also mentioned in my previous post) so that the function is incapable of accessing any Roblox-related stuff, even builtin functions like print.
You can also use the setfenv function to limit what the function that was returned has access to. I believe that this is what the game lua learning did also. I could be wrong though.
As for the setfenv(), I’ve never used it before. In the case formulaStr is the calculation string and env is the environment level, what should I set env as for the code below?
env should be a dictionary where the variable name is the key, and the variable value is the value. For example, here’s how you “spy” on the print function:
local env = {
print = function(...)
warn(`there are {select('#', ...)} parameters passed to print`)
print(...)
end,
}
local code = [[
print('hello', 'world')
]]
local f = loadstring(code)
setfenv(f, env)
print(f())
Enabling loadstring does not pose any security risks unless you have a RemoteEvent that directly lets someone access the loadstring function on the server from the client. A way you can make sure that a string passed to loadstring doesn’t contain anything that could damage the server is by checking if the string contains any alphabetical characters.
local function IsSafe(String)
String = string.gsub(String, "x", "*") -- Replace x with * since people tend to use it for multiplication
return string.find(String, "%a") == nil -- checks if the string has any letters, if it doesn't the function returns true and the string should be safe to use in loadstring()
end
print(IsSafe("(1 + 2) x 5")) -- true since it doesn't contain any letters besides x, which is whitelisted for multiplication.
print(IsSafe("workspace:ClearAllChildren()")) -- false since it contains letters
In the code I will load with loadstring() there are going to be functions from the math library, so instead I will make a copy of the string with all the math function words (I will make a list of allowed words) removed and then check for any remaining letters.
Shouldn’t be a problem if I do it like that to whitelist a few words, right?