Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sign JWT?

I'm trying to secure a Sinatra API.

I'm using ruby-jwt to create the JWT, but I don't know exactly what to sign it with.

I'm trying to use the user's BCrypt password_digest, but every time password_digest is called it changes, making the signature invalid when I go to verify it.

like image 452
Hugo Avatar asked Feb 04 '15 03:02

Hugo


People also ask

Do JWT need to be signed?

They are defined by the open standard (RFC 7519) and they're composed of three segments: a header, a payload, and a crypto segment in general. JWT's are signed when generated, and the same signed JWT is validated upon receipt to make sure that it hasn't been modified in transit.

What is signing key in JWT?

The signing key is a JSON web key (JWK) that contains a well-known public key used to validate the signature of a signed JSON web token (JWT). A JSON web key set (JWKS) is a set of keys containing the public keys used to verify any JWT issued by the authorization server and signed using the RS256 signing algorithm.


3 Answers

Use any kind of application secret key, not a user's bcrypt password digest.

For example, use the dot env gem and a .env file, with an entry such as:

JWT_KEY=YOURSIGNINGKEYGOESHERE

I personally generate a key by using a simple random hex string:

SecureRandom.hex(64)

The hex string contains just 0-9 and a-f, so the string is URL safe.

like image 65
joelparkerhenderson Avatar answered Nov 06 '22 17:11

joelparkerhenderson


For RS256 public and private key strategy you can use Ruby OpenSSL lib:

Generating keys:

key = OpenSSL::PKey::RSA.new 2048

open 'private_key.pem', 'w' do |io| io.write key.to_pem end
open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end

Load key from .pem file to sign token:

priv_key = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
token = JWT.encode payload, priv_key, 'RS256'

Load key from .pem file to Verify token(Create a middleware for this):

      begin
        # env.fetch gets http header
        bearer = env.fetch('HTTP_AUTHORIZATION').slice(7..-1)
        pub_key = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
        payload = JWT.decode bearer, pub_key, true, { algorithm: 'RS256'}
        
        # access your payload here
  
        @app.call env

      rescue JWT::ExpiredSignature
        [403, { 'Content-Type' => 'text/plain' }, ['The token has expired.']]
      rescue JWT::DecodeError
        [401, { 'Content-Type' => 'text/plain' }, ['A token must be passed.']]
      rescue JWT::InvalidIssuerError
        [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid issuer.']]
      rescue JWT::InvalidIatError
        [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid "issued at" time.']]
      end

To use RSA key in your .env instead of loading a file, you will need to use gem 'dotenv' and import the key as a single line variable with the use of newline '\n'. check this question on how to do it. example:

PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nmineminemineminemine\nmineminemineminemine\nmineminemine...\n-----END PUBLIC KEY-----\n"

as an .env PUBLIC_KEY variable, loading the key will change to this:

key = OpenSSL::PKey::RSA.new ENV['PUBLIC_KEY']
like image 24
Marcelo Fonseca Avatar answered Nov 06 '22 16:11

Marcelo Fonseca


According to wikipedia, a secret key used in cryptography is basically just that, a key to open the lock. The key should be consistent and reliable, but not easy to duplicate, just like a key you would use on your home.

As stated in this answer, secret keys should be randomly-generated. However, you still want the key to be retained for use across the application. By using the password digest from bcrypt, you are actually using a hashed key that was derived from a base secret key (the password). Because the hash is random, this is not a reliable secret key to use, as you stated.

The previous answer using SecureRandom.hex(64) is a great way to create an initial base application key. However, in a production system, you should be taking this in as a configuration variable and storing it for consistent use across multiple runs of your application (for example following a server reboot, you should not invalidate all of your user's JWTs) or across multiple distributed servers. This article gives an example of pulling in the secret key from an environment variable for rails.

like image 42
Control Complex Avatar answered Nov 06 '22 15:11

Control Complex