Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

Protecting a table in _G?

Asked by 7 years ago

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:

  1. Can I fully prevent overriding?

  2. 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.

0
By overrinding it I meant prevent from overriding Access since my API table is in the script, not _G. fireboltofdeathalt 118 — 7y

2 answers

Log in to vote
3
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
7 years ago
Edited 7 years ago

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"
0
I don't understand what exactly newproxy is, so if you could reply that'd be nice. But I'll accept this since it is what I was looking for, thank you. fireboltofdeathalt 118 — 7y
0
But this is just going to be a temporary until I get BindableEvents/RemoteEvents set up. fireboltofdeathalt 118 — 7y
0
newproxy(true) is more or less equivalent to setmetatable({}, {}), except that the object is a userdata instead of a table. BlueTaslem 18071 — 7y
0
Long time later, but. I switched this over to newproxy, works perfectly fine, thank you. Also I no longer remove new keys, and instead Access isn't inside the table, it's only retreived if you try to index "Access" in which case it'll return the raw function fireboltofdeathalt 118 — 7y
Ad
Log in to vote
1
Answered by 7 years ago

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.

0
Okay thank you. I'd accept this answer but, I already accepted BlueTaslem's and also I feel he gave me a solution higher than what I already had fireboltofdeathalt 118 — 7y

Answer this question