Hidden variables - cracking under pressure?
The protect function now includes /hide - a method for hiding
private values within an object or module context.
For example, you would write:
protect/hide 'password
to hide the variable password in your local context.
To hide multiple words, you can use:
protect/hide/words [username password email-address]
Quite simply, the /hide refinement causes the word(s) to ignore any
future binding operations. All bindings prior to the /hide are
valid. Any that follow it will either throw an error or make it
appear that the hidden words don't even exist.
An example
Let's take a look at an example. Here's an object that manages a
password. Our goal is to be able to keep the password secret.
We want to be able to set the password, but not let any code, other
than the manager, access the password, other than
getting a secure hash value for it. (That can be used for
authentication actions, without giving away the secret
password.)
manager: object [
pass: none
set-pass: func [pw][
print "setting password..."
pass: pw
exit ; return nothing
]
get-pass: does [
checksum/secure to-binary pass
]
protect/hide 'pass
]
Keep in mind this is just a simple example, and in real code,
we'd probably hash the password within set-pass.
So, we can now set the password:
manager/set-pass "bingo"
setting password...
And, get its hash:
print manager/get-pass
#{E410B808A7F76C6890C9ECACF2B564EC98204FDB}
How secure is it?
Ok, now we are going to try to sneak-a-peek at the actual
password. We start with:
probe manager/pass
** Script error: cannot access pass in path manager/pass
Ok. Doesn't seem to recognize it.
But, shouldn't the error say that it is hidden? For good security, no. You don't want to give away any information, even the existence of the name of the field.
Well, what if we try to set it:
manager/pass: "crack"
** Script error: cannot access pass in path manager/pass:
Same result. Makes sense.
Ok, let's up-the-level, and see if we can find other ways to
crack into the manager object.
Let's try the standard accessor functions:
probe get in manager 'pass
none
probe select manager 'pass
none
Just a NONE, which doesn't tell us anything, because:
probe select manager 'some-random-word
none
Reflection, of course!
Ok, let's get serious. We know REBOL is a reflective language
so let's use that powerful feature to get what we want.
We start with:
probe manager
make object! [
set-pass: make function! [[pw][
print "setting password..."
pass: pw
exit
]]
get-pass: make function! [[][
checksum/secure to-binary pass
]]
]
The pass field doesn't even show up!
Then:
probe words-of manager
[set-pass get-pass]
No pass.
And similar results for:
probe values-of manager
probe body-of manager
So far, access denied.
Oh.. wait! There's a special shortcut method for getting the
values of an object as a block. Let's try it:
probe get manager
** Script error: not allowed - would expose hidden values
There's an interesting error message. Maybe we're making some
progress? I can feel we've almost cracked it, right?
The obvious method
We forgot the most obvious approach...
unprotect 'manager/pass
unprotect in manager 'pass
unprotect/hide 'manager/pass
Poof. None of them work.
Up the level...
Let's up-the-level again - say, level 7?
We know we can copy and make objects to inherit values.
So, let's use that to clone the object and get to that pass field.
(Because, after all we don't care that we use some memory for
it.)
mgr: copy manager
probe mgr/pass
** Script error: cannot access pass in path mgr/pass
Nope. What about:
mgr: make manager [pass: none]
probe mgr/pass
** Script error: cannot access pass in path mgr/pass
Time for level 9...
Ok, time to ask: WWBD? (What would BrianH do?)
Maybe we need more than one line, and rebind back into our
local context. Yeah, that would work....
pass: none
probe get bind 'pass manager
** Script error: pass has no value
probe pass
none
I... will... not... be... defeated...
probe get bind 'pass in manager 'set-pass
** Script error: pass has no value
Noooo....
probe find body-of get in manager 'set-pass to-set-word 'pass
[
pass: pw
exit
]
Ah, now I've got you my pretty...
probe get first find body-of get in manager 'set-pass to-set-word 'pass
** Script error: pass: word is not bound to a context
WHAT!!!!??? Where did that come from?
Danger! Level 9.5.... the ultimate security hole, the old
"BrianH uses a functional argument trick..." (Because we notice
that set-pass didn't specify its arg type. Yes!)
manager/set-pass does [
probe get bind 'pass manager
]
** Script error: pass has no value
9.7: append to the context....
append manager reduce bind [
'get-it does [probe pass]
] manager
manager/get-it
** Script error: pass has no value
Noooo.... I'm melting... melting...
Conclusion
Well, a little dramatic. Sorry. I had to make the article
interesting, because it was getting just a bit long.
But... anyone got a 9.9? So, where's Mr. Bindology, Ladislav?
Protect /hide will be in A44. Think: WWLD?
31 Comments
|