How to prevent a "replay" with Javascript SDK authResponse - security

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.

Related

How to implement expiring to the account activation link?

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.

Cookie-challenges, storing logged in user

Hello fellow developers
I have obviously under estimated a thing when developing my first complex web site, where user creation and login is required.
It appears that cookies can be edited and modified by the user logged in, by using some developer tools i.e. in Google Chrome. That, I never gave a thought.
So, here is my issue.
When the user is logged in, I store the user name in a cookie.
If username-cookie is not blank, and I can find a user file with that name, the user is logged in, per se. Otherwise, no user is logged in.
When the user logs out, I simply expires the cookie, which works fine.
Now, the problem is, that a user obviously can edit the content of a cookie, outside the web application, or with javascript.
What would be the correct approach here to ensure, that the username cookie is not compromised in any way, other by my web application?
Making them read-only is not possible, I assume. Encrypting the cookie and then decrypting might work, I guess. Then, the cookie would be nonsense to the user, and if modified, result in a logout, as no valid username can be found upon decrypting the edited cookie.
I have stalked Googles cookies, and it appears that there are a lot of xxID cookies, which contains garbage. Does that mean, that encrypting/decrypting is the only way to make it work? I also considered some kind of login-ticket, but that would require a table lookup every time a user interacts with my web page.
Can anyone give me a hint as to what would be the correct approach?
Thanks in advance
Best regards,
Karsten Heitmann
You should look up session management for the language you are using.
The traditional approach is that when a user logs on, your application generates a long, cryptographically random token called the "session id" and sets that into a cookie. It stores data like who is logged in on the server side identified by the random value, so when a logged on user comes back, the browser sends the cookie with the random session id and the application can look up session data on the server side. This way an attacker has no way to guess a valid session id for a logged on user, assuming the session id is cryptographically random and long enough (which more precisely means it has enough entropy). Logging out means deleting the session data on the server side, and also removing the cookie, but that is not the most important part - the session will be invalid anyway.
Note that you should not code this yourself. You did not mention the language and environment you are developing in, but session management is rather tricky business if you want to secure it, and it is already provided by most languages / frameworks.
Just for curiosity, the encryption approach you mention is by the way a valid one. Some frameworks actually do that, but you should not attempt to code that either, because it is very easy to get it wrong, lots of things need to be taken care of to make it secure enough. Unfortunately an answer here is not the right format to go into details I'm afraid.
Btw you mention looking at Google. They use their own single sign-on solution, it is very complex compared to simple session management, so it's probably not the best example for you to look at. Find simple websites, most of those work the traditional way.

How to accept authentication on a web API without SSL?

I'm building a web API very similar to what StackOverflow provide.
However in my case security is importance since data is private.
I must use HTTP.
I can't use SSL.
What solution(s) do you recommend me?
EDIT: authentication != encryption
Nearly every public API works by passing an authentication token for each web request.
This token is usually assigned in one of two ways.
First, some other mechanism (usually logging into a website) will allow the developer to retrieve a permanent token for use in their particular application.
The other way is to provide a temporary token on request. Usually you have a webmethod in which they pass you a username / password and you return a limited use token based on if it is authenticated and authorized to perform any API actions.
After the dev has the token they then pass that as a parameter to every webmethod you expose. Your methods will first validate the token before performing the action.
As a side note the comment you made about "security is important" is obviously not true. If it was then you'd do this over SSL.
I wouldn't even consider this as "minimal" security in any context as it only provides a false belief that you have any sort of security in place. As Piskvor pointed out, anyone with even a modicum of interest could either listen in or break this in some way.
First of all, I suggest you read this excellent article: http://piwik.org/blog/2008/01/how-to-design-an-api-best-practises-concepts-technical-aspects/
The solution is very simple. It is a combination of Flickr like API (token based) and authentication method used by the paiement gateway I use (highly secure), but with a private password/salt instead.
To prevent unauthorized users from using the API without having to send the password in the request (in my case, in clear since there is no SSL), they must add a signature that will consist of a MD5 hashing of a concatenation of both private and public values:
Well know values, such as username or even API route
A user pass phrase
A unique code generated by the user (can be used only once)
If we request /api/route/ and the pass phrase is kdf8*s#, the signature be the following:
string uniqueCode = Guid.NewGuid().ToString();
string signature = MD5.Compute("/api/route/kdf8*s#" + ticks);
The URL of the HTTP request will then be:
string requestUrl =
string.Format("http://example.org/api/route/?code={0}&sign={1}", uniqueCode, signature);
Server side, you will have to prevent any new requests with the same unique code. Preventing any attacker to simply reuse the same URL to his advantage. Which was the situation I wanted to avoid.
Since I didn't want to store code that were used by API consumer, I decided to replace it by a ticks. Ticks represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001.
On server side, I only accept ticks (timestamp) with a tolerance of +-3 minutes (in case client & server are not time synchronized). Meaning that potential attacker will be able to use that window to reuse the URL but not permanently. Security is reduced a little, but still good enough for my case.
Short answer: if it's supposed to be usable through usual clients (browser requests/AJAX), you're screwed.
As long as you are using an unencrypted transport, an attacker could just remove any sort of in-page encryption code through a MITM attack. Even SSL doesn't provide perfect security - but plain HTTP would require some out-of-page specific extensions.
HTTP provides only transport - no secure identification, no secure authentication, and no secure authorization.
Example security hole - a simple HTTP page:
<script src="http://example.com/js/superstrongencryption.js"></script>
<script>
encryptEverything();
</script>
This may look secure, but it has a major flaw: you don't have any guarantee, at all, that you're actually loading the file superstrongencryption.js you're requesting. With plain HTTP, you'll send a request somewhere, and something comes back. There is no way to verify that it actually came from example.com, nor you have any way to verify that it is actually the right file (and not just function encryptEverything(){return true}).
That said, you could theoretically build something very much like SSL into your HTTP requests and responses: cryptographically encrypt and sign every request, same with every response. You'll need to write a special client (plus server-side code of course) for this though - it won't work with standard browsers.
HTTP digest authentication provides very good authentication. All the HTTP client libraries i've used support it. It doesn't provide any encryption at all.

How to make an API call on my server accessible only from one URL

I don't know if the title is clear enough, anyway what I need to do is quite simple: I have some content you can access by an API call on my server; this content is user-related so when you request access to it, you must first wait for the owner to authorize you. Since this content will be probably embedded into blog articles or form posts I want it to be accessible only from the URL the user authorized to.
The only way that came to my mind is to check in some secure way where the request is coming from: the problem with this approach is that anybody could create a fake request, using a valid URL but coming from a non-authorized URL actually.
I'm looking for a way to solve this problem, even if this doesn't involve checking the actual URL but using some other approach or whatever. Feel free to ask any questions if this is not clear enough.
With Sessions:
If you generate a secure token, most languages have libraries to do such a thing, you will have to persist it probably in a session on your server. When you render the page which will access the other content you can add that token to the link/form post/ajax request on the page you wish to be able to access it from.
You would then match that token against the value in the user session if the token doesn't match you return an error of some sort. This solution relies on the security of your session.
Without Sessions:
If you don't have sessions to get around server persistance, you can use a trick that amazon s3 uses for security. You would create something like a json string which gives authorization for the next 30 seconds, 5 minutes, whatever is appropriate. It would need to include a timestamp so that the value changes. You would use a secret key on your sever that you combine with the JSON string to create a hash value.
Your request would have to include the JSON string as one request parameter. You would need to base64 encode it or some other means so that you don't run into special characters not allowed over http. The second parameter would be the output of your hash operation.
When you get the request you would decode the JSON string so it was exactly the same as before and hash it with your secret key. If that value matches the one sent with the request it means those are the two values you sent to the page that ultimately requested the content.
Warnings:
You need to make sure you're using up to date algorithms and properly audited security libraries to do this stuff, do not try to write your own. There may be other ways around this depending on what context this ultimately ends up in but I think it should be relatively secure. Also I'm not a security expert I would consult one if you're dealing with very sensitive information.

I need resources for API security basics. Any suggestions?

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.

Resources