Forum: Nitro two way crypt function

Posted by George Moschovitis (Guest)
on 2007-11-09 22:24
(Received via mailing list)
I ve asked this before:

is there a two-way 'crypt' function in ruby?

ie:


a = encrypt(original, "mysecret")
b = decrypt(a, "mysecret")

# ===> b == original

thanks in advance for any help. I am looking for something fast (not 
super
secure)

-g.
Posted by chris (Guest)
on 2007-11-09 22:35
(Received via mailing list)
Here is blowfish

http://crypt.rubyforge.org/blowfish.html

and here is Rijndael

http://crypt.rubyforge.org/rijndael.html

On Nov 9, 2007 4:24 PM, George Moschovitis 
<george.moschovitis@gmail.com>
Posted by Reid Thompson (Guest)
on 2007-11-09 22:46
(Received via mailing list)
Posted by George Moschovitis (Guest)
on 2007-11-10 08:25
(Received via mailing list)
Thanks,

ezcrypto does the work!

I am thinking about encrypting the session data with a two-way method 
and
skip the diggest test. Anyone sees any problems?

-g.
Posted by Trans (Guest)
on 2007-11-10 11:37
(Received via mailing list)
On Nov 10, 2:24 am, "George Moschovitis"
<george.moschovi...@gmail.com> wrote:
> Thanks,
>
> ezcrypto does the work!

Thing I don't like about this lib -- it is for Rails. They've embedded
EzCrypto and ActiveCrypto in the same package. I don't think that's
good for Nitro. Maybe we can ask them to split the package in two?

> I am thinking about encrypting the session data with a two-way method and
> skip the diggest test. Anyone sees any problems?

How will that effect the cookie size? BTW, How were you doing the
digest to begin with? Were you using facets/crypt.rb? I would like to
add a two-way routine to that lib, to have available for simple cases.

T.
Posted by Trans (Guest)
on 2007-11-10 11:43
(Received via mailing list)
On Nov 10, 5:37 am, Trans <transf...@gmail.com> wrote:
>
> > I am thinking about encrypting the session data with a two-way method and
> > skip the diggest test. Anyone sees any problems?
>
> How will that effect the cookie size? BTW, How were you doing the
> digest to begin with? Were you using facets/crypt.rb? I would like to
> add a two-way routine to that lib, to have available for simple cases.

Actually, I just added one, and called it BiCrypt.

T.
Posted by George Moschovitis (Guest)
on 2007-11-10 11:51
(Received via mailing list)
Cool,

is this fast? This encryption/decrption will be done per request.

-g.
Posted by Trans (Guest)
on 2007-11-10 12:43
(Received via mailing list)
On Nov 10, 5:46 am, "George Moschovitis"
<george.moschovi...@gmail.com> wrote:
> Cool,
>
> is this fast? This encryption/decrption will be done per request.

Eeeewwww. I mean, I'm sure it's fairly quick. It's not that
complicated, but it is Ruby. If you are going to be doing this per
request, you should consider something C-based.

Actually, a little more research reveals what's is probably the right
answer:

  OpenSSL::HMAC

See. http://kylekochis.com/2007/5/5/making-hmac-hashes-in-ruby

T.
Posted by George Moschovitis (Guest)
on 2007-11-10 12:50
(Received via mailing list)
>
>  OpenSSL::HMAC
>

i think this is used in rails. Why is this better suited? Is this 
two-way?

-g.
Posted by Trans (Guest)
on 2007-11-10 13:52
(Received via mailing list)
On Nov 10, 6:45 am, "George Moschovitis"
<george.moschovi...@gmail.com> wrote:
> >  OpenSSL::HMAC
>
> i think this is used in rails. Why is this better suited? Is this two-way?

B/c it was designed to do pretty much exactly you are wanting to do.

  http://en.wikipedia.org/wiki/HMAC

Plus it's built into ruby.

T.
Posted by Timothy (Guest)
on 2007-11-10 23:54
(Received via mailing list)
HMAC is not appropriate for this! HMAC is for authentication over a 
network,
not for encryption. HMAC is not reversible. If you want to be able to 
store
data in cookies securely I would use a system like this:

In advance you need to select a constant global key for the application.

For each session:

1. Create a random session key, encrypt it with the global key. Store 
this in
a cookie with a constant name (e.g. NITRO_SESS_KEY).

2. For each cookie you want to store, encrypt it to the session key and 
put it
in a cookie. You could use one cookie for all the data or break it up in 
to
related groups.

3. To recover a cookie, decrypt the session key using the global key. 
You can
then use the session key to decrypt the cookie contents. Sanity checks 
or an
included digest should be used to detect invalid data.

Notes:

1. The separate session key is used to minimise the risk of somebody 
finding
the global key. If a person can compromise the session key then they can 
read
and alter the session data from that session. If they can get the global 
key
then they can read and alter the data from any session.

2. If the content of session data is predictable (size would be a good
predictor of this) it should probably have some random data prepended to 
make
it harder to use known plaintext attacks.

3. It is worth only decrypting the session when it is accessed to avoid
unnecessary CPU load.

For encryption in Ruby you want to use the OpenSSL library. The ruby 
bindings
are part of the standard library.

Cheers,

Tim
Posted by Trans (Guest)
on 2007-11-11 02:25
(Received via mailing list)
On Nov 10, 5:23 pm, Timothy <interfe...@gmail.com> wrote:
> HMAC is not appropriate for this! HMAC is for authentication over a network,
> not for encryption.

Currently authentication is all George is doing and that's what I was
suggesting it for. Also, HMAC is a part of OpenSSL.

I think if you go so far as to enrypt cookies, you should consider
carefully if you should be using cookies to begin with. Also don't
bother encrypting any cookie that doesn't really need to be. I
couldn't care less if anyone finds out my shoe size ;)

T.
Posted by George Moschovitis (Guest)
on 2007-11-11 12:10
(Received via mailing list)
My problem is that sometimes, for some stupid reason, the digest 
integrity
check fails for non altered cookies.
Ie you get AlteredCookie exceptions for now reason.
I really can't find the problem.

So I was thinking, I could just encrypt/decrypt the cookie with a fast 2 
way
encryption method and
solve this once and for all.

-g.
Posted by Trans (Guest)
on 2007-11-11 17:22
(Received via mailing list)
On Nov 11, 6:09 am, "George Moschovitis"
<george.moschovi...@gmail.com> wrote:
> My problem is that sometimes, for some stupid reason, the digest integrity
> check fails for non altered cookies.
> Ie you get AlteredCookie exceptions for now reason.
> I really can't find the problem.

What lib are you using to check the cookies?

T.
Posted by Bill Kelly (Guest)
on 2007-11-11 19:00
(Received via mailing list)
From: George Moschovitis
>
> My problem is that sometimes, for some stupid reason, the digest
> integrity check fails for non altered cookies.
> Ie you get AlteredCookie exceptions for now reason.
> I really can't find the problem.

Would it be feasible to add some temporary debugging, to store
the exact binary data of the original cookie alongside the
digest?  So that, later, when the integrity check fails, you
can compare the exact bytes of the old cookie against the new
cookie?

(Or if I've misunderstood the problem, I'm sorry.)

But it seems like there are three possibilities:

  - the cookie bytes really did change somehow
  - the stored digest bytes got mutated somehow
  - the digest code is broken and has randomness

It seems like it should be possible (by storing the original
cookie bytes for debug purposes) to have all the data needed
to narrow down the possibilities?


Regards,

Bill
Posted by George Moschovitis (Guest)
on 2007-11-11 19:07
(Received via mailing list)
I have added all the debugging aids. This bug is random and I still 
cannot
understand why it happens.
the offending code is:

lib/raw/context/session/cookie.rb

-g.
Posted by Trans (Guest)
on 2007-11-11 21:00
(Received via mailing list)
On Nov 11, 1:07 pm, "George Moschovitis"
<george.moschovi...@gmail.com> wrote:
> I have added all the debugging aids. This bug is random and I still cannot
> understand why it happens.
> the offending code is:
>
> lib/raw/context/session/cookie.rb

Add this to the end of that file:

 if __FILE__ == $0

  BEGIN {
    require "facets"
    require "facets/random"
    require "cgi"

    require "raw/context/session"
    require "raw/controller/cookie"
  }

  require "test/unit"

  class TestCookie < Test::Unit::TestCase

    class MockContext
      def initialize
        @cookies = {}
        @data = {}
      end
      def cookies
        @data
      end
      def add_cookie(cookie)
        @cookies[cookie.name] = cookie
        @data[cookie.name] = cookie.value
      end
    end

    def setup
      @cookie_store = Raw::CookieSessionStore.new
    end

    def test_simple
      context = MockContext.new
      input = "RABBIT DATA"
      @cookie_store.put(input, context)
      output = @cookie_store.get(context)
      assert_equal(input, output)
    end

    context = MockContext.new
    maxsize = 1000

    1000.times do |i|
      input = String.random((rand * maxsize).to_i, /[\w\W]/)

      define_method("test_#{i}") do
        @cookie_store.put(input, context)
        output =  @cookie_store.get(context)
        assert_equal(input, output)
      end
    end

  end

 end

I ran it a few times now and it's working fine. So that should narrow
the possibilities.

My guess as the moment is Bill's #1. The cookies must be changing
somehow when it moves through the browser.

Perhaps you are not the only one using the cookie name?

Also, going the encryption route won't buy you anything if you still
plan to have the JSON formatted data available to the browser.

T.
Posted by Bill Kelly (Guest)
on 2007-11-12 23:22
(Received via mailing list)
Hi George,

From: George Moschovitis
>
> I have added all the debugging aids. This bug is random and I still
> cannot understand why it happens.
> the offending code is:
>
> lib/raw/context/session/cookie.rb

It appears that while the encoded cookie data going out has been
URL encoded with %0A %2B etc...

WARN: --old--> "BAhJQzoRUmF3OjpTZXNzaW9uewciDm9yZGVyX29pZGlaIgpG
TEFTSElDOhlS%0AYXc6OkZsYXNoaW5nOjpGbGFzaHsABjoLQGRpcnR5ewAGOg1AZ
XhwaXJlc3U6%0ACVRpbWUNlukagFY%2BaVE%3D%0A--fae0818a4fea3a7a9285c46f101297ea6eb11d08781a45be7c25d46ef75a2ceb
8c25feb5f1a0e5744594f6ade3f0835956e5b1bca9ae5750fe99f859d47c86b2"

WARN: %%%%%%%% digest: Session.secret="public"
WARN: -input-> "BAhJQzoRUmF3OjpTZXNzaW9uewciDm9yZGVyX29pZGlaIgpG
TEFTSElDOhlS\nYXc6OkZsYXNoaW5nOjpGbGFzaHsABjoLQGRpcnR5ewAGOg1AZX
hwaXJlc3U6\nCVRpbWUNlukagFY+aVE=\n"
WARN: -hash-->
fae0818a4fea3a7a9285c46f101297ea6eb11d08781a45be7c25d46ef75a2ceb
8c25feb5f1a0e5744594f6ade3f0835956e5b1bca9ae5750fe99f859d47c86b2


...The encoded data coming back in has already had the %xx URL
encoding decoded into literal characters:

WARN: --new--> "BAhJQzoRUmF3OjpTZXNzaW9uewciDm9yZGVyX29pZGlaIgpG
TEFTSElDOhlS\nYXc6OkZsYXNoaW5nOjpGbGFzaHsABjoLQGRpcnR5ewAGOg1AZX
hwaXJlc3U6\nCVRpbWUNlukagFY+aVE=\n--
fae0818a4fea3a7a9285c46f101297ea6eb11d08781a45be7c25d46ef75a2ceb
8c25feb5f1a0e5744594f6ade3f0835956e5b1bca9ae5750fe99f859d47c86b2"

...And as such, when CGI.decode is called on these already-literal
characters, a literal '+' will be converted to a space ' ' by
CGI.unescape.

WARN: %%%%%%%% digest: Session.secret="public"
WARN: -input-> "BAhJQzoRUmF3OjpTZXNzaW9uewciDm9yZGVyX29pZGlaIgpG
TEFTSElDOhlS\nYXc6OkZsYXNoaW5nOjpGbGFzaHsABjoLQGRpcnR5ewAGOg1AZX
hwaXJlc3U6\nCVRpbWUNlukagFY aVE=\n"
WARN: -hash-->
859210ca5f0bfbaf57e94fbadc8c4eb9ac4c0deb6caf6ec02b664d675ea9150f
840c4dfaa41f646b51a836cd4c391bbe05a44c9dc7f495a6d9e001370181b958


Thus the digest was originally generated on data above that
ended in "...kagFY+aVE=\n".  But when the digest is later called
to converm the data, it is called on data that looks like:
"...kagFY aVE=\n".

(Where the + has been replaced with a space.)


Hope this helps,

Bill
Posted by Trans (Guest)
on 2007-11-13 00:22
(Received via mailing list)
On Nov 12, 5:16 pm, "Bill Kelly" <bi...@cts.com> wrote:

> (Where the + has been replaced with a space.)
>
> Hope this helps,

Good job Bill!

So how did you figure it? Did my test case come in handy at all in
tracking that down, or did you just "see it" when looking over the
code?

T.
Posted by Bill Kelly (Guest)
on 2007-11-13 00:53
(Received via mailing list)
From: "Trans" <transfire@gmail.com>
> tracking that down, or did you just "see it" when looking over the
> code?

Thanks :)  I didn't end up using your test case, because my
test suite for my online store controller already tended to
encounter the cookie problem every so often.  (I had switched
to the MemorySessionStore awhile back as a workaround.)

So I went low-tech and put in some print statements and kept
re-running my test suite until the error occurred.

But I haven't studied the rest of the code enough to understand
why the data in context.cookies[Session.cookie_name] is already
CGI.unescape'd (maybe it's supposed to be.)


Regards,

Bill
Posted by George Moschovitis (Guest)
on 2007-11-13 07:54
(Received via mailing list)
>
> But I haven't studied the rest of the code enough to understand
> why the data in context.cookies[Session.cookie_name] is already
> CGI.unescape'd (maybe it's supposed to be.)
>

It is supposed to be. But I cannot understand why the diggest is 
generated
on CGI.encodeded input in the first place.

I will have a better look, thanks!

-g.
Posted by George Moschovitis (Guest)
on 2007-11-13 07:55
(Received via mailing list)
data = Base64.encode64(Marshal.dump(session)).chop
    data = CGI.escape("#{data}--#{generate_digest(data)}")

as you can see the diggest is generated *before* escaping, ie it is
unescapped just like when it read back.
am I missing something?

-g.
Posted by Bill Kelly (Guest)
on 2007-11-13 08:23
(Received via mailing list)
From: George Moschovitis
>
>     data = Base64.encode64(Marshal.dump(session)).chop
>     data = CGI.escape("#{data}--#{generate_digest(data)}")
> 
> as you can see the diggest is generated *before* escaping, ie it
> is unescapped just like when it read back. 
> am I missing something?

I think the encode is fine.  But:

  def decode(data)
    data, digest = CGI.unescape(data).split("--")
    raise AlteredCookie.new unless digest == generate_digest(data)
    return Marshal.load(Base64.decode64(data))
  end

For whatever reason, 'data' passed to decode is already
unescaped.  So calling unescape again seems to convert
'+' into ' ' (space).

E.g.

>> x = CGI.escape("hey+there")
=> "hey%2Bthere"
>> CGI.unescape(x)
=> "hey+there"
>> CGI.unescape(CGI.unescape(x))
=> "hey there"


Regards,

Bill
Posted by George Moschovitis (Guest)
on 2007-11-13 09:18
(Received via mailing list)
>
> >> x = CGI.escape("hey+there")
> => "hey%2Bthere"
> >> CGI.unescape(x)
> => "hey+there"
> >> CGI.unescape(CGI.unescape(x))
> => "hey there"
>

AHA!!!!!


-g.
Posted by Timothy (Guest)
on 2007-11-13 11:30
(Received via mailing list)
On Sunday 11 November 2007 14:24:54 Trans wrote:
> couldn't care less if anyone finds out my shoe size ;)
>
> T.
>
> _______________________________________________
> Nitro-general mailing list
> Nitro-general@rubyforge.org
> http://rubyforge.org/mailman/listinfo/nitro-general

HMAC would be suitable for authentication of a message but how does 
adding it
to cookies improve over using a single, random session id cookie and 
storing
all sensitive data in a session store? Why would you want to 
authenticate
messages to yourself when you could just keep them in your sight?

I'm aware that for some small things it could be advantageous to avoid a
roundtrip to a database due to frequent use but it's these same, 
frequently
used pieces of data that will be expensive to verify repeatedly. It is 
common
to see user preferences in cookies because the user cannot do any harm 
by
changing them. Such applications should assume that the user might do 
so.
Anything more sensitive should be kept serverside.

Timothy
Posted by Trans (Guest)
on 2007-11-13 13:38
(Received via mailing list)
On Nov 13, 5:29 am, Timothy <interfe...@gmail.com> wrote:

> Anything more sensitive should be kept serverside.
I agree. I don't think it wise to use a cookie for anything that needs
to be secure, other then a session key.

T.
Posted by George Moschovitis (Guest)
on 2007-11-13 13:47
(Received via mailing list)
I think the cookie session store is a great idea because it keeps the
(small) state to the client. So i can redirect the client to different
servers in my cluster w/o worrying about synchronizing state.

btw, bills fix seems to have solved the AlteredCookie bug! :) :)

-g.
Posted by Trans (Guest)
on 2007-11-14 16:01
(Received via mailing list)
On Nov 13, 7:46 am, "George Moschovitis"
<george.moschovi...@gmail.com> wrote:
> I think the cookie session store is a great idea because it keeps the
> (small) state to the client. So i can redirect the client to different
> servers in my cluster w/o worrying about synchronizing state.

Sure, i think it's great too. But not for secure data.

> btw, bills fix seems to have solved the AlteredCookie bug! :) :)

Yea!

Shall we add the bug he found as a testcase? We can use the one I put
together as a basis if need be.

T.
Posted by George Moschovitis (Guest)
on 2007-11-14 16:05
(Received via mailing list)
>
> Shall we add the bug he found as a testcase? We can use the one I put
> together as a basis if need be.


A test case (rspec) would be great. Can you handle it?

-g.
This topic is locked and can not be replied to.