I need to save user info when starting an oAuth login session. I've been doing this by saving a randomly generated string on the database with user data attached to it, then I attached that string on the browser session and used it as the state parameter.
I'm trying to prevent having to store data on a database as much as possible; so I thought about attaching the user info I need as a signed string.
I'm not entirely sure if this is a good practise, since oAuth.com states that the stateparameter should be a randomly generated string; but a signed string using RSA-SHA256 should look random enough to the client.
I'm also aware this may result on lengthy state parameters, but I don't think most of the oAuth services such as google really care about the state/URL length (or at least I don't see that listed on their documentation).
I'll be using a secure private key, of course.
So, the question is: is this a bad idea, or a good idea?
Refs:
https://auth0.com/docs/secure/attack-protection/state-parameters
Related
I am trying to implement expiring to the activation link I send to the user's email when they register a new account.
The link should expire after 24 hours if the user doesn't click on it.
Up until this point I am able to send the link to the user's email upon registration, the link looks like this 'http://localhost:3000/auth/activate/${verificationKey}', everything works smoothly.
But like I said, I want the link to expires after 24 hours and I just don't have any idea of how to do it.
From what I've gathered so far I think one way to do this would be to delete the verificationKey value from my User entity/model after 24 hours, then if the verificationKey value is falsy I need to send another link to the user.
Now my question is, how do I check if a value (in this case user.verification_key) has been generated for over 24 hours?
This is the relevant code to register a new user:
const user = new User();
user.username = username;
user.password = await bcrypt.hash(password, salt);
user.is_verified = false;
user.verification_key = cryptoRandomString({
length: 10,
type: 'url-safe',
});
Some people have suggested to use Redis for this, which I don't know anything about other than it's a in-memory store, and while I'm ok reading more about this tool, I would like to know if there are other ways to do this. I don't know if by installing Redis I would need extra configuration for the server when I host my app, I'd like to avoid that.
Since you already have some database set up, it makes sense to store some verification key and an expiration time for it. You don't need to actually delete that verification key... just need to store when it expires.
Perhaps you have a separate model for RegVerificationKey, with fields key (randomly generated string), expiration (set to a date/time 24 hours after you create it), and userId (the ID of the user this is associated with). Create this key. When you go to activate, just check to see if there is a key associated with the requested user that hasn't expired yet.
Some people have suggested to use Redis for this
No need here, you already have a database you can put data in.
I would like to know if there are other ways to do this
There's an alternative, where you cryptographically sign your URL. Basically, you would store the key and its expiration data in the URL itself, and include some calculated proof that you (the person with the private key) created this URL. When your system receives this URL, it can verify the URL was signed correctly without even having to consult a database. This method can be complicated and probably isn't useful in your case. I'm just mentioning it here as an alternative. Check out JWT for one possible implementation: https://jwt.io/
Recently I was needed to implement this kind of implementation in my web application. So I just followed the below points to achieve it.
1- Create the URL (web link) and append the current date and time along with an encrypted key which you would store in the database as mentioned below.
2- Create a column in the database table (the table where you store any user specific details) to store a randomly generated key which you have encrypted and appended in the URL.
3- When you would receive this URL on server you would check the encrypted date and time in the URL and would decide whether it is still valid depends on your criteria of retaining a link (e.g. 24 hours)
4- Next you would decrypt that key in the URL that you have appended in it at the time of creating it and would match it with what you have stored in the table.
So by implementing above logic you could achieve the desired functionality.
Hope its useful for any one who wants similar type of implementation
I understood that you already found a solution, by storing two fields in the database: one for the key and another one for he expiration timestamp. Everything depends on the use cases and it is definately one way to do it. However I will explain Redis and JWT as a solution in comparison to yours.
Redis is an in-memory datastore (that also allows persistence to disk) as you pointed out and I think the reason why people suggested it is, that you can define an expiration time for a record. Redis will remove that record automatically for you then. Reference: https://redis.io/commands/expire
Redis would take the work off of your shoulders to check if the 24hrs already passed. If you can’t fetch the key anymore, the key probably expired. Another benefit of Redis is, that is super quick compared to your normal database query. But if you only need it for the activation link, which is a one-time-action for the user, the time benefit is negligible. Also you would introduce a new technology just for that use case.
#Brad already suggested using JWT (Json Web Token) instead of implementing your own token solution and I would also suggest that for the following reasons.
A JWT is a self-contained token consisting of three parts: Header, Payload, Signature. The header contains the name of the algorithm that was used to create the token. The paylod contains some standardized fields (e.g. creation date, expiration date, subject the token was issued for like username) and you can also add custom fields. The third part is a signature that ensures that no one changed the payload after it was issued by your token service.
Self-contained means that the token contains everything to validate it, including the expiration timestamp. In your case the expiration time is not part of your token but stored in the database. If you create another microservice that needs to verify your token, that service needs to contact your main service which contains the logic to check the expiration database field.
With JWT the Microservice would only need to know the secret key that was used to sign the token and then you can just import some standard JWT library to verify the token. These libraries validate the signature as well as the expiration timestamp which is an optional field in the payload of the token.
By the way, the payload can be read without knowing the secret key from the signature. So it is even possible to read the payload for example on client side to check the expiration time.
With your solution you have additional database calls, which are potentially slow. For an activation link that is acceptable, but for tokens with recurring use within a short timespan (i.e. API requests that require authentication) additional database calls should be avoided. Also you need to implement token generation and verification yourself, whereas JWT provides standard libraries. This is a benefit when you want to have another Microservice in Java instead of NestJS for example. You can quickly knit them together by using standard libs instead of porting your implemtation or being forced to decide for a centralized token verification service.
One limitation of JWT you have to workaround yourself, is the use case where you want to have a „one time token“. You can only define an expiration date but you can not say that a token can only be used x times. Here you need a centralized service again, which keeps track of how often a token was used (by making use of some datastore) and all other services around need to contact that service.
A good starting point for JWT with NestJS is the official NestJS documentation.
I am making a social media type website, and I store user details such as emails, names and other personal details.
I will be encrypting the personal details using an Encrypt-then-MAC concept. When the user registers, a cryptographically secure string will be made to use as the private encryption key. When the user selects a password, the encryption key will be encrypted using the password.
The password will NOT be stored in the database, but will be the private key to decrypt the encryption key used to encrypt the personal details. The only person who knows the password is the user. My question is: how can I store the encryption key once decrypted?
I have thought of having a table with one column for IP and another column for the encryption key, but some people close the browser window without logging out, therefore there would not a possible way to remove the entry from the database when they have finished their session on the website.
Another way would be to store it in a cookie, but that could be intercepted when sent back to the server. I would like to know if there is a secure, nearly foolproof way to store the encryption key, client side or server side.
Thanks in advance.
EDIT:
In reply to TheGreatContini's answer -
The idea of a "zero-knowledge web application" (in your blog) is a good one, however, for zero-knowledge, even the key cannot be stored in the database, this complicates things a bit, as you would then have to use the user's password as the key. Using the password isn't as secure, as it is a bit harder to verify the password to prevent data which has been "decypted with the wrong key" from passing. There is the concept of Encrypt-then-MAC but that only verifies if the data is legit, and will assume that a hacker has messed with some data and data cannot be trusted, however, as you cannot actually verify the password (the hash would not be stored as it is "zero-knowledge"), so the password may just be wrong.
Not sure I have the answer, but a few considerations:
(1) Sessions need to be timed out. Perhaps you can do this by periodically running batch jobs that scan the database looking for sessions that have lacked activity. This requires storing in the db the date of the last action from the user.
(2) Generally keys are higher value than the content they protect because the keys have a longer lifetime than the individual data elements that the protect (because the data may change or additional data may be added). Rather than storing the key in the db, you can store the decrypted contents in the database for the length of the session. Of course, this is provided that you did (1).
Perhaps I am not adding much beyond what you already know, however may be worth considering a blog I wrote exactly about this topic. The low level details start in the section "A second line of defence for all the sensitive data." Prior to that it mainly motivates the concept. Glad to see somebody actually doing something like this.
I'm using the Javascript SDK to make a web page that is entirely static HTML and Javascript (i.e., it's not dynamically produced web markup via some web app). This web page occasionally uses Javascript to POST data to a server--data which should be tied to a particular Facebook user. I use FB.getLoginStatus to determine who the user is. This gives me authRepsonse JSON data which looks like this:
authResponse:
accessToken:"AAAC91..."
expiresIn: 3786
signedRequest:"Ws93YNGWQeOi..."
userID: "670..."
I can send the signedRequest to the server and decode it and validate it there (using my app's secret key), and then I know that the user is, in this case, "670...", so I can presumably safetly perform whatever operation is supposed to happen on the server. Here's the data I extract from the signed request:
{"algorithm"=>"HMAC-SHA256",
"code"=>
"2.AQAKT...|5hVFYWcu5a...",
"issued_at"=>1323403518,
"user_id"=>"670..."}
My question is, what prevents an adversary (who somehow got ahold of the encoded authResponse above) from just "replaying" the signedRequest data to my server at a much later time?
The "issued_at" param at first looked promising, but I don't have anything to compare that issued_at time to to see if I should accept this signedRequest or not. The "expiresIn" is another time related parameter, but it's not signed, so I can't trust it. Maybe "code" provides me with extra info, but I don't see how to decode that.
I expect I'm just thinking about this wrong, or using the API in a way I'm not supposed to. Any insights? Thanks.
First of all using an Message Authentication Code (MAC) is a fundamentally insecure approach to the problem of authentication. You should be storing this information as a server side state, so that this is never a threat. By using a cryptographic hash function as an HMAC you introduce the possibility of someone brute forcing your secret key. Cryptography should only be used when there is no other solution, instead you are using it to introduce a weakness. This is a gross misuse of cryptography.
That being said, you have an issued_at timestamp. Just take the current timestamp and subtract. make sure that value is greater than your session timeout.
So lets say i have a member base website and when the user signs in i put put a cookie (or a session) with a key value pair remembering who the user is. But its just come to my attention which information i should use to remember the user so that its secure. I cant use username=username or user_id = user_id (because my user_id will be 1), because people then can just simply guess what the cookie values are and logged in as that user. So what key/value pair should i use to be able to identify users and still connect their information to the database securely? Thanks.
Ben, there are a few different types of attacks you need to be concerned with. For example simply encrypting the identifier with a private key doesn't prevent someone who can intercept the encrypted value from simply replaying it to your server (and appear to be the user). Some common security risks are detailed here (and in associated links at bottom of this page):
https://www.owasp.org/index.php/Session_hijacking_attack
Session management can be quite complex and depending on the level of security you require, it is not something you want to tackle yourself, because likely your development environment / framework already has a solution that has been vetted moreso than a homebrew solution. Here is a link detailing some things to consider, unfortunately this topic has more to it than a simple Stack Overflow post:
https://www.owasp.org/index.php/Session_Management
If you dont prefer encryption for whatever reason, then a simpler solution could be to use a GUID to identify the user. This way, a hacker would have to launch a denial of service kind-of attack on your application to be able to run through even a very small fraction of the GUIDs.
If you want to do this properly, then you should have a look at http://jaspan.com/improved_persistent_login_cookie_best_practice also.
I'm definitely not an expert in security, but I have recently implemented user management tool and I have done the following.
Don't use encryption, its slow and most of the time for simple implementation its just a waste of time.
Here is what you do need to store on the server - in order to authenticate each request.
UserId (obvious)
CookieHash (made out of userId, some secret private key and crypto randomly generated number)
LastLogin
SessionRenewed (useful for when to cancel someone's session eg. renew cookieHash every 10 min, otherwise log out user)
LastIP
What I store in cookie is following
UserId
CookieHash
How to use this basic security
Simply when user logs in you check username/password etc. (just the usual) If everything is fine then log in user and generate new cookiehash and fill those values given above.
Every request check UserId against its hash. If someone gave UserId = 4 but hash didnt match then automatically drop a session and forward user to login screen. Possible log is good to see how often people try to play around with your hard work.
I hope this helps.
You can just encrypt the user id with a private encryption key that you keep on the server. There are a few things to watch out for with this approach:
Every call to the server will require you to decrypt the cookie to get the id of the user. This will add overhead to each request.
If the key is ever compromised, you will be forced to abandon the current name for the cookie you use and use another encryption key when assigning to the new cookie name; this will cause the user to have to re-login, of course.
While I don't think that these are major hurdles, they might be to you, and you would have to evaluate the impact on your site for yourself.
I've done a little googling but have been a bit overwhelmed by the amount of information. Until now, I've been considering asking for a valid md5 hash for every API call but I realized that it wouldn't be a difficult task to hijack such a system. Would you guys be kind enough to provide me with a few links that might help me in my search? Thanks.
First, consider OAuth. It's somewhat of a standard for web-based APIs nowadays.
Second, some other potential resources -
A couple of decent blog entries:
http://blog.sonoasystems.com/detail/dont_roll_your_own_api_security_recommendations1/
http://blog.sonoasystems.com/detail/more_api_security_choices_oauth_ssl_saml_and_rolling_your_own/
A previous question:
Good approach for a web API token scheme?
I'd like to add some clarifying information to this question. The "use OAuth" answer is correct, but also loaded (given the spec is quite long and people who aren't familiar with it typically want to kill themselves after seeing it).
I wrote up a story-style tutorial on how to go from no security to HMAC-based security when designing a secure REST API here:
http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/
This ends up being basically what is known as "2-legged OAuth"; because OAuth was originally intended to verifying client applications, the flow is 3-parts involving the authenticating service, the user staring at the screen and the service that wants to use the client's credentials.
2-legged OAuth (and what I outline in depth in that article) is intended for service APIs to authenticate between each other. For example, this is the approach Amazon Web Services uses for all their API calls.
The gist is that with any request over HTTP you have to consider the attack vector where some malicious man-in-the-middle is recording and replaying or changing your requests.
For example, you issue a POST to /user/create with name 'bob', well the man-in-the-middle can issue a POST to /user/delete with name 'bob' just to be nasty.
The client and server need some way to trust each other and the only way that can happen is via public/private keys.
You can't just pass the public/private keys back and forth NOR can you simply provide a unique token signed with the private key (which is typically what most people do and think that makes them safe), while that will identify the original request coming from the real client, it still leaves the arguments to the comment open to change.
For example, if I send:
/chargeCC?user=bob&amt=100.00&key=kjDSLKjdasdmiUDSkjh
where the key is my public key signed by my private key only a man-in-the-middle can intercept this call, and re-submit it to the server with an "amt" value of "10000.00" instead.
The key is that you have to include ALL the parameters you send in the hash calculation, so when the server gets it, it re-vets all the values by recalculating the same hash on its side.
REMINDER: Only the client and server know the private key.
This style of verification is called an "HMAC"; it is a checksum verifying the contents of the request.
Because hash generation is SO touchy and must be done EXACTLY the same on both the client and server in order to get the same hash, there are super-strict rules on exactly how all the values should be combined.
For example, these two lines provides VERY different hashes when you try and sign them with SHA-1:
/chargeCC&user=bob&amt=100
/chargeCC&amt=100&user=bob
A lot of the OAuth spec is spent describing that exact method of combination in excruciating detail, using terminology like "natural byte ordering" and other non-human-readable garbage.
It is important though, because if you get that combination of values wrong, the client and server cannot correctly vet each other's requests.
You also can't take shortcuts and just concatonate everything into a huge String, Amazon tried this with AWS Signature Version 1 and it turned out wrong.
I hope all of that helps, feel free to ask questions if you are stuck.