REBOL 3.0

Hidden variables - cracking under pressure?

Carl Sassenrath, CTO
REBOL Technologies
5-Apr-2009 23:06 GMT

Article #0189
Main page || Index || Prior Article [0188] || Next Article [0190] || 31 Comments || Send feedback

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

REBOL 3.0
Updated 30-Apr-2017 - Edit - Copyright REBOL Technologies - REBOL.net