Okay so, I am using _G for use so that I (and or other people) can use my admin through another script.
I've set a metatable in to it and a loop to prevent people from changing it, but. Is there any way to fully protect the table from people overriding?
I've done this:
__index = function(self,i) -- (in a metatable) return self.AccessRequestFunction end; __newindex = function(self,i,v) self[i] = nil return nil end; --(doesn't fire due to only "new" values I think) --I also added __call to execute AccessRequestFunction, but that's not for security.
And it secures it but I have a few questions:
Can I fully prevent overriding?
Can I protect it from rawset or rawget (Not 100% required since my loop still helps it)
The reason I am asking is because I am worried someone could loop it to be a nil value.
If you are wondering whether or not you can protect a key in _G
, the answer is no, you can't. Use ModuleScripts instead.
There is basically no good reason to use _G
for anything whatsoever and scripts wantonly interfering with each other is a good reason not to use it.
I'm not sure why your __newindex
does anything at all; it could be dangerous to remove arbitrary keys.
You should not rely on a loop to preserve invariants. That's just sloppy, unsafe, and a waste of CPU resources.
If you just want to protect the state of some object...
You can use newproxy
to create a userdata. A userdata cannot be accessed directly in any way (including rawget
and rawset
).
You also ought to prevent the metatable itself from being tampered with, so set the __metatable
metaproperty:
-- newproxy(true) is essentially setmetatable({}, {}), local p = newproxy(true) -- expect that `p` is a userdata and not a table: -- print(type(p)) --> userdata -- This means functions like rawset, rawget, etc. don't work on p getmetatable(p).__index = myindex getmetatable(p).__newindex = mynewindex getmetatable(p).__metatable = false -- no more access through "getmetatable"
If you are willing to ignore rawset
and rawget
(which override metatables), here is an example of protecting a "key" in _G:
local myTable = { hi = function() return "hi" end, value = 5 } setmetatable(_G, { __index = myTable, __newindex = function() error("You cannot modify _G") end, __metatable = "You cannot modify the metatable of _G" }) --The rest of this script can be put in a separate script and so long as there's a "wait()" before it, it will still work the same way print(_G.value) --5 print(_G.hi()) --"hi" print(getmetatable(_G)) --"You cannot modify the metatable of _G" _G.value = 3 --error, "You cannot modify _G" --or setmetatable(_G, nil) --error, "cannot change a protected metatable"
This assumes that _G is empty when the script runs (which it is by default). You can just as easily protect a table within _G, rather than forcing all of _G to the purposes of this script, but other scripts could simply remove the table from _G. Note that this particular implementation means that no other script can add *anything* to _G. You can improve the script above to allow additions to _G so long as the key isn't in 'myTable':
__newindex = function(t, key, value) if myTable[key] then error("You cannot modify _G." .. tostring(key)) end rawset(_G, key, value) end,
Note, however, that none of this will prevent rawget
and rawset
from messing up _G, since those functions ignore metatables.
I would recommend using a ModuleScript instead of _G. If your script will be used by someone else in their own place, it's smarter to let them mess up your API if they want (it won't impact your original script or your own place, so it shouldn't be a big deal). A malicious script can do tons of damage if it's on the server-side (ex delete the contents of the workspace or flood it with tons of new parts) - there's no point in trying to protect from every possible bad thing a script could do, especially when it's not even your own place. If these potentially malicious scripts are a part of your place, simply remove the ones that make a mess of things, or read over the potentially malicious scripts to make sure they don't do anything stupid. The above solution for _G will also work just as easily for ModuleScripts, so consider using that instead.
The legitimate use of preventing other scripts from tampering with an API is to communicate to users of the API that the change they just attempted is illegal. For this case, using metatables should be sufficient.