redis string looks like a hash, getting the value - node.js

I am trying to access session data from node.js that is stored in redis.
In the redis-cli I ran Keys * and returned
1) "sess:ZRhBJSVLjbNMc-qJptjiSjp8lQhXGGBb"
2) "sess:6p1EcGvJukTT26h88NqoTGdz2R4zr_7k"
If I then run GET I get back what looks like a hash
redis 127.0.0.1:6379> GET sess:ZRhBJSVLjbNMc-qJptjiSjp8lQhXGGBb
"{cookie:{originalMaxAge:null,expires:null,httpOnly:true,path:/},userKey:a92ca307-b315-44bc-aadf-da838d063c78,
authenticated:true,clientKey:1ccb5308-2a7e-4f49-bcdf-b2379de44541}"
If I try to get the value in userKey using
hget sess:oq6RW1zP7sfcZZc4wb1RHbti390FxL7- userKey
it returns
ERR Operation against a key holding the wrong kind of value
so I ran a TYPE check and found that it's not a hash but a string. I am a bit confused now as the whole thing looks like a hash and I cannot figure out how to return the values that I need as just calling get returns the whole thing.
Is there some other command I need to use to get the values?
Thanks

If you can GET aganist the key then it is not a hash because you would get ERR Operation against a key holding the wrong kind of value. And it was confirmed by yourserlf doing HGET and getting the error.
Probably that keys looks like a hash because (it is a hash but not redis hash datatype) it is the unique token that was issued to user in his session cookie in client. When user send this cookie to the server in every request the server can retrieve session info from redis using the cookie value as redis key.
The value is a string in JSON format. You have to retrieve the whole value and parse it; ussing JSON.parse in node.js could do the job. Once the value is parsed you have a JavaScript object which attributes can be access in standard way:
var sessionData = JSON.parse(JSONString);
console.log(sessionData.userKey)

It's a string
You can't get some session value directly, because it's serialized to some format(in this case, JSON)
If the session is written by node.js, you should use the same API to read.
If the session is written by other system, and you must parse it with node, you should just GET it, and json parse it(JSON.parse)

Related

how to verify plain text key vs hashed API key

A user can generate an API key by pressing a button, and I save the API key in the database. However, I don't save it as plain text, but rather hash it. I thought this was wise until I started trying to verify the API key.
I am hashing like this:
const saltRounds = 10;
const key = crypto.randomUUID();
const hashedToken = await bcrypt.hash(key, saltRounds);
The problem now is that in the other application, the user doesn't send any user details like email address for example. So, if they had I could have done a findOne({email: email}) or if there was a user ID I could have done findById etc. but now only the API key is sent.
So, I am receiving the plain text version of the API key and need to somehow compare it.
I would have done the below but I don't actually have user.apiKey.
const validKey = await bcrypt.compare(key, user.apiKey);
So, if all I have is the plain text API key, how can I find it and compare it in the database?
I came up with a solution but not sure if it is any good. Basically what I ended up doing is creating a prefix for the api key and concatenating it with the actual API key.
const concatKey = keyPrefix + "." + token;
So, I save keyPrefix in the database and use that as the unique ID. I also save a hash of concatKey.
Then when I send requests to the API, I split the api key and get the prefix before the . and look that up in the database ie: the unique identifier. I then compare the hashes and if all okay the request can proceed.
It's not pretty, but it works.

How do I validate the Hmac using NodeJS?

I can successfully create an Hmac via NodeJS using the following code:
(slightly altered example from : https://nodejs.org/api/crypto.html#cryptocreatehmacalgorithm-key-options)
Crypto.createHmac('sha256', Crypto.randomBytes(16))
.update('I love cupcakes')
.digest('hex');
That results in a value like the following (hex-based string Hmac signature):
fb2937ca821264812d511d68ae06a643915931375633173ba64af9425f2ffd53
How do I use that signature to verify that the data was not altered? (using NodeJS, of course).
My Assumption
I'm assuming there is a method call where you supply the data and the signature and you get a boolean that tells you if the data was altered or not -- or something similar.
Another Solution?
Oh, wait, as I was writing that I started thinking...
Do I need to store the original random bytes I generated (Crypto.randomBytes(16)) and pass them to the receiver so they can just generate the HMac again and verify that the result is the same (fb2937ca821264812d511d68ae06a643915931375633173ba64af9425f2ffd53)?
If that is true that would be odd, because the parameter for Crypto.randomBytes(16) is named secret (in the official example)*. Seems like that needs to be kept secret??
Please let me know if there is a way to verify the signature on the receiving side & how I do that.
Official Documentation : A Bit Confusing
Here's the function as it is defined in the official docs:
crypto.createHmac(algorithm, key[, options])
In the function definition, you can see the second param is named key.
However, in the example they refer to it as secret
const secret = 'abcdefg';
const hash = crypto.createHmac('sha256', secret)
.update('I love cupcakes')
.digest('hex');
console.log(hash);
Just posting the answer so if anyone in future sees this they will be able to have the definitive answer.
As the commentor (Topaco) pointed out, the simple answer is that:
The receiver who want wants to validate the Hmac simply needs to use the same key value & data and apply it to the method and retrieve the hash value.
const secret = 'abcdefg';
const hash = crypto.createHmac('sha256', secret)
.update('I love cupcakes')
.digest('hex');
console.log(hash);
The original Hmac-creating party must provide three things for the verifying party:
data : (could be encrypted data from AES256, for example)
key : original key passed into the createHmac() method -- note: this item is called secret in the sample code by NodeJS (above).
hash :the (clearText) hash which the original creator generated when calling the createHmac() method.
With those three things the verifying party can now call the createHmac() method and determine if the hash they get matches the hash that the original hmac-creating party generated.
Doing this validates that the Data which was sent has not been corrupted or altered.
Additional Note On Key (secret)
I've come back after thinking about the Hmac a bit more.
It is required that both parties know the key (aka secret) but it does not mean that it should be exposed to others.
This must be kept secret (as the code implies) because if a nefarious type knew the value and could alter it, then they could also alter the data and generate a new key (secret) and pass it along as if the original creator sent it along (MITM - man in the middle attack).
So, the point here is that yes, both parties have to know the key (secret) value, but it should not be shared where it might be discovered by nefarious types.
Instead, it will have to be agreed upon or based upon a secret password, etc.

Redis Compatible Reversible Data Structure for a Binary Search?

I have a chat module coded in Nodejs and Redis, which loads all the DB users into Redis and later retrieve them with the Key and Value, As what redis server is expected to do,
To store them I used "Key" as the User_ID with prefix and values in json as below,
entry.user_id = rows[i].user_id;
entry.uname = rows[i].uname.toString();
client.set('chat_userid_' + entry.user_id, JSON.stringify(entry));
This works fine, as long as we do searches for the user's data using only the User_ID. Sometimes I have to find user with the "name" as well, In this case, when we want to search via name, I had to do another key to the value list just for that search.
entry.user_id = rows[i].user_id;
entry.uname = rows[i].uname.toString();
client.set('chat_uname_' + entry.uname, JSON.stringify(entry));
As you can see above Data structure is very low performance and redundant, Is there a better data structure to store the user data in the Redis server, that we can get the same result as per the above use-case?

Node Js SHA 1 multiple times

Hello I have this Java code which uses the following encryption method to encrypt password.
MessageDigest digester = MessageDigest.getInstance("SHA-1");
value = digester.digest(password.getBytes());
digester.update(email.getBytes());
value = digester.digest(value);
This returns base64 encoded string like qXO4aUUUyiue6arrcLAio+TBNwQ= This is sample not exact.
I am converting this to NodeJs not sure how to handle this. I have tried like
var crypto = require('crypto');
var shasum = crypto.createHash('sha1');
var value = shasum.update('hello');
shasum.update('abc#xyz.com');
value = shasum.digest(value).toString('base64');
console.log(value);
The string base64 I get in node js is not similar to get from java. Not sure why?. I need to have same encoding as java as its old system migrated to new one cant lose old details.
Can someone help me how I can achieve same base64 string.
In Java you're calculating the first value as the hash of the password alone, then overwrite it with hash of the email alone. (digest gives the result and resets the hash, in Java).
In Javascript, on the other hand, you're having an undefined value, then overwrite it with the hash of (password concatenated with email).
PS that hash is conceptually wrong: you should always put a separator between two fields, to avoid ambiguity and, thus, possible attacks.

Retrieving values from keys after hmset command

I have a Node.js application with stores posts written by authors in a Redis database. The post is given a unique ID (the variable messageId), and two values are given: the message and the author. The first part of the code looks like so:
redisClient.hmset(messageId, "message", req.body.message, "author", req.body.author);
So far, so good. What I want to do is be able to retrieve the value of a specific key. I know I can use hgetall() to get all the keys and values for messageId, and I know I can use hkeys to get all the keys for messageId. If I know there will be a key called "message", how can I retrieve the value from message A) from the Redis client, and B) through Node.js?
NOTE: I have seen this: Redis + Node.js - how do I retrieve the values. It does not tell me what I need.
There is hget to retrieve a single value, and hmget to retrieve multiple values.
See Redis commands for hash. The node.js client should support them all.
Something like:
redisClient.hget(messageId, "message");
redisClient.hmget(messageId, ["message", "author"]);

Resources