10
\$\begingroup\$

Does this code create sufficiently hard-to-guess session tokens, assuming the server and client are communicating over HTTPS?

Take 2 (thanks to this crypto.SE answer):

(ql:quickload (list :ironclad :cl-base64))

(let ((prng (ironclad:make-prng :fortuna)))
  (defun new-session-token ()
    (cl-base64:usb8-array-to-base64-string
     (ironclad:random-data 32 prng) :uri t)))

As an additional note, the previous version was using the incorrect encryption mode (ecb, instead of ctr which is preferable for this task), and using an insecure method of generating random keys ((sha256 rand)).

If not, what else is required for that goal, and why?

Are there attacks I should be worried about other than guessing and sniffing session tokens?

\$\endgroup\$
2
\$\begingroup\$

Your question doesn't give enough information about exactly what you want to do to know if this might work in your case. The most important answer is simple though: "don't do that; use a web framework instead". Session management should almost always be done in the standard way of your framework; then you won't have to do anything other than check if your framework thinks your user is authenticated.

Let's now look at the specifics of the code. You have used a cryptographic PRNG. That should be fine. You used 32 bytes = 256 bits of random data. This should be no problem (128 bits is normally enough against brute force attack; 256 is overkill, which is likely good in this case) your main fears should probably be seeding and leaking bits.

Seeding: the PRNG documentation says that if you don't give a random seed the PRNG will be unseeded. In real life it seems that it actually does get seeded from /dev/random. I deeply don't like this and would probably seed it explicitly. Better would be discuss with the author and send a patch to the documentation for the library so that it promises to do what it actually does. I don't have an OS without /dev/random to test on, but I think your function simply won't work there (which is good because it's safe).

Leaking bits1: If the attacker can find a situation where response time depends on the contents there might be a problem (e.g. force regeneration of the session). Looking up about timing attacks on Ironclad I don't think it's resistant (see e.g. https://www.mail-archive.com/pro@common-lisp.net/msg00977.html). You need to check or protect against this. If the contents of the message are true then you should probably switch to a better library such as LibreSSL when it comes out (possibly GNU TLS?). If someone attempts to use the function on e.g. Windows I would simply exit with a warning.

Leaking bits2: your output is constant size but includes some special characters. If the attacker can find a situation where your message length depends on the authentication token then you have a problem. Using Base64 encoding (a second time) should be fine. Using maximal HTML encoding for your tokens will not be (n.b. normal HTML would be fine; but some encoders go too far).

Leaking bits3: Going further, there are standard attacks against SSL/TLS which I think you should consider; BEAST, CRIME and BREACH which leak bits https://en.wikipedia.org/wiki/Transport_Layer_Security#CRIME_and_BREACH_attacks. Increasing the size of your random token beyond 256 bits might marginally protect against such things. However "In the current form, the exploit uses JavaScript and needs 6 requests to extract one byte of data" (https://community.qualys.com/blogs/securitylabs/2012/09/14/crime-information-leakage-attack-against-ssltls) so I think you may find that effort would be better invested in turning off compression on all sites and user web browsers. Personally I would go for 384 bytes just to be more conservative than the rest ;-)

Summary: probably your function is basically the same one used in many frameworks. Because of the risk of timing attacks and bit leakage I wouldn't personally want to release it for public use until I understood the whole surrounding situation of usage much better than I currently do.

(edited lots since I misread bytes as bits in the first response)

\$\endgroup\$
  • \$\begingroup\$ That's 32 bytes, not bits. Does your assertion that it's too small still hold? If so, how big would it have to be to provide sufficient security? If there's a non-trivial decision function, could you at least outline it in the abstract? What kinds of specifics do you need to know in order to decide whether a session token is hard enough to guess? How would a framework implement this procedure? \$\endgroup\$ – Inaimathi May 26 '14 at 17:26
  • \$\begingroup\$ Hmm.. I did misread the bits vs. bytes. (evil editor keeps closing the comment ... hmm). The "golden" number you are normally aiming for is bigger than 128 bits. That's standard. AES is e.g. 256 bits, which is reasonable. The basic point is that we don't think it's reasonable to brute force a number which is much bigger than that. Then the question is; does your function or use reduce that number by providing a way to cheat when guessing. E.g. RSA functions do and for those you need numbers bigger than 1024 bits. I will add a bit about how frameworks work to the answer. \$\endgroup\$ – Michael May 26 '14 at 22:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.