Email and Reusable Token URLs - html

I'm building a site that offers functionality to users without requiring them to register. The idea is to send an email to the specified address containing a link with a token. That way the user would could this link anytime they want to make changes to the functionality.
While I realize that there is no way to truly secure such a concept, I'm looking for options to minimize the visibility of the token. In its current state, soon as the user clicks on the link it is added to their browser history, available to anyone who has access to the computer.
In most cases I would over come this with a simple form so that the token could be passed through with a POST request, but forms aren't really supported in emails.
So the question is, does anyone know of an alternative way to hide a token in such an email?

I'm sure you've thought of this, but you could send them a password and a link to a URL where they'd need to enter that password. If the emailed URL contained another password, it would be a smaller compromise to security than usual to make the user-entered password quite short, like a PIN number, say.

You could resend a new token every time the user wants to log in. Have them plop in their email address and send them a new token, while setting previous tokens to 'expired.' Or, if the server detects that an old link/token was used, it could automatically send a new one to the associated email address and ask the user to check their email for a new link.
That would require keeping track of old, expired tokens and the associated email addresses, but still requires no registration - just that a user check their mail every time they want to log in. You'd essentially be piggy backing on their email authentication.
It'd also be counter-intuitive for users.
This would turn the token into a cryptographic nonce, which is primarily used to prevent the replay attack you mentioned.

Another answer, perhaps more useful:
Some browsers (like Chrome) do not record 301 "Moved Permanently" redirects in the browser history. Firefox does, but there's a proposal to change that:
https://wiki.mozilla.org/Browser_History:Redirects
For example, in Chrome, if you navigate directly to
amazon.com
it will follow a 301 Redirect to
www.amazon.com
If you then check your browser history, it will only show
www.amazon.com
Thus, if your server returns a 301 redirect from the login link, the server could record the token, remove it from the redirect link, and the user's browser would only record the redirect link.
(this is my first time responding on stack overflow - let me know if my writing is unclear or if I'm missing other etiquette)

Related

why is my web browser not sending cookies when I click a link, but it sends them just fine when I type in the url

I am creating a web application in Golang/HTML. I am implementing registration, sessions, email verification, and login.
My code works, however I have noticed some strange browser behavior. When the user registers for the first time, my application will send them an email containing a link with a unique nonce (number used once) in the url. This is to ensure that the user is able to receive email from us at that address and "verify their email", as is standard practice on many web applications.
Please click the following link to verify your account: http://localhost:8080/verify-email/55c17d2c
I noticed that when I receive this email, if I click on the link in the email, the browser will open the link in a new tab as expected, however, it will not send any cookies on requests associated with that tab.
But when I copy and paste the link into a new tab manually and press enter, it sends the cookies just fine. What gives? is this some sort of undocumented security feature? What should I do about this?
I used https://github.com/six-ddc/httpflow to capture a log of the HTTP requests and responses going between my web browser and my server application. I have two separate logs, one of them captured a registration flow where i clicked the link, and the other one captured a registration flow where i copy and pasted the link into a new tab.
Log where link in email was clicked: https://paste.cyberia.club/~forest/2f3fce7dcc71fc095341eeaefb33f20883c79886
Log where link was copy and pasted from email into url bar: https://paste.cyberia.club/~forest/0623f76cfee339e91d2213dd8f4c7710c6fa2797
Please note that I tried this on firefox and google chrome, I also tried it with a real domain and https certificate, got the same behavior in all browsers and setups.
Here are my constraints:
I want the application to work fine with javascript disabled, however, I'm open to javascript-based solutions if they are simple, secure, and make the site more enjoyable to use. For example, I am using a javascript that hashes passwords client side before sending to the server for login. But if javascript is disabled, the raw password will be sent.
I don't want the user to have to log in again after they click the link to verify their email address.
I don't want the link in the email address to represent a "free pass" into the user's account. I want to require the user to be already logged in (or somehow otherwise authenticated) before they can verify their email address. For example, if someone steals that email and clicks that link before the intended user does, I don't want the email thief to be able to take over the account.
OOPS I just figured this out, I wanted Lax SameSite policy on my cookies:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_attribute
It takes three possible values: Strict, Lax, and None. With Strict, the cookie is sent only to the same site as the one that originated it; Lax is similar, except that cookies are sent when the user navigates to the cookie's origin site, for example, by following a link from an external site;

How do I persist login state for a user between my website and my chrome extension

I built a Chrome Extension, where a login form is displayed as a side bar using content scripts injection. I do not want the user to see this login form if the user is already logged in on the website and vice versa if the user logs in on the Chrome Extension and then visits the website, user should automatically log in.
I am returning tokens from the backend when a user successfully logs in.
My question is, what is the best way to store these tokens so both the content scripts in my extension and the website have access to the token to check to see if the user is already logged in.
As far as I understand I have localStorage, ChromeStorage but I do not know if they are shared between the tabs of the browser and the extension.
Any direction is highly appreciated.
Thank you.
If you're returning tokens, a reasonable way to do it would be to inject a content script into pages that match the callback URL containing the token, extract it and save into chrome.storage. It is shared between the content script and all other extension contexts.
Do note: chrome.storage is not exactly secure: it's not encrypted on disk, and can be snooped upon with Dev Tools. Then again, the token is normally stored in the cookie store, which can be likewise examined even without access to the (slightly) more secure password storage.
Perhaps the only more secure way to keep the token in the extension is chrome.identity API, but then you have to login separately, defeating your goal.

Correct HATEOAS response when creating a user account

I have a REST api written in node which uses HATEOAS. The user is required to have an account before they can access the bulk of it.
They register an account with login details, then login to obtain an access token, and then use that token in order to access any endpoints that aren't register or login.
Issuing a get to the root responds with a directory with available actions.
Q: What is the correct response from register, to tell the client what it can do next (i.e. login)?
register technically creates a new resource on the server so a 201 CREATED and a Location header would seem appopriate. However the login reference isn't the location of the newly created resource.
Should I return 201 Created with a Location pointing to the newly created user (e.g. /myaccount or /users/{id} and then include a login link in the response body?
{
_links: {
self: { href: "what goes here?" },
x:login: { href: "/login" }
}
}
Do I not tell the client at all, and require them to do a get on the application root in order to fetch a list of available endpoints. This should include login anyway. Assuming the client had to do that in the first place to get the register link it should already have login.
Expecting the client already to already have the login link feels uncomfortable as it relies on an assumption of the client's prior activity.
Requiring the client to issue another request to the root directory after registering seems mean, inefficient and unnecessary. If the client has just created a resource it seems only fair that the server should respond with what it can do with it next.
I like to have my api's act no differently than a webpage. If you want the UX of your application to be the user is taken to login after they register, then 302 them from a successful register to the login resource. And upon successful login, 302 to them to the appropriate destination (IE, if they tried to access something with no token, then take them to login, with a destination of the original requested resource). That's and important part to your #3. Having a link off the root that leads to login, but you need to protect all the other links such that they indicate (and link to) a login being required to access the resource. The client app should expect to get this login required response at any time as tokens can (and do) expire at any time.
Following on this, it might make sense to do the JWT as a cookie instead of as an Authorization Header, it would make it a bit easier for the client (they just have to setup a cookie jar)..if the client is say a native mobile app that maintains a single connection setup. If it's server to server, then auth header makes sense. I'd go about supporting both to cover both scenarios.
Continuing on the idea of thinking of the api as a web site. Why have them login after registration at all? Why not have the registering of an account end up with the login token being sent? they just set their user/pass, why make them enter it again? I realize with some more exotic architectures the register service can not perform the login action (perhaps it doesn't have the private key to sign the token), but if it is possible i'd consider it.
If you really want to stick to the 201 header (which is fine, just make sure the docs of your register relationship indicate that), then option 2 is the closest in my opinion. A location header to the URL of the account just created a 201 is pretty standard for creating a user. But, i'd not return what you've supposed there. You're kind of returning a account-created resource (the thing with the login link), but do you really need this custom resource? If you want to give some messaging back to the client (like "Account Created") in that resource then absolutely yes, but you could also just give them back the root resource.
tl;dr; Decide what you want your UX to be and then make your API implement your UX.

Can a malicious user submit data if there is no form?

In my site, users can only modify their personal information only once a day. Script-side, I determine if they are allowed to (i.e. check with the database if it's been 24 hours since the last modification) and whether or not to display the form.
My question is, could a malicious user manage to submit information if there's no form? In other words, if there is no FORM element no data should be submitted by the user's browser, right?
What I'm afraid of is that if someone manages to send the data, the script would still process it and change the personal information when it shouldn't.
Of course - this would be a kind of replay attack. So long as your resource endpoint will handle a malicious POST request, regardless of the content of the preceding GET then you're vulnerable.
Remember: never trust the client. Provided that you do authentication and authorization checks before handling a POST request then you'll be fine.
Yes, a malicious user can still send data even without the form, if he knows the url of the page which accepts the data and their corresponding attributes it expects. He can then easily create a form with that info and submit the data.
So you basically need to validate the data at the server-side.
YES
For example someone could use curl.
curl -d profile=value http://www.yoursite.com/profile
You could prevent such attacks with a CSRF token you send along with your form.
See this article for more background http://shiflett.org/articles/cross-site-request-forgeries.

Include Additional HTTPS Request Header Information in Form

Is there a way to include additional request headers in form data, other than action and method? I am hoping to send some authentication credentials cross domain without making the user re-enter their login credentials. ie I want to build an Authentication header directly from form submission.
The domain is SSL enabled, so I considered including credentials in the URL, but as explained here this is a bad idea, as those credentials may be secure over the connection, but can be accessed through the browser by other apps potentially.
Larger Picture
I have access to the cross domain username and password through an AJAX request to the client server (home domain). I want to take those credentials and submit them through a non-AJAX request, so a user can download a document securely without the URL being publicly accessible.
To the specific question, I believe the answer is no - you can't control sending any extra headers from the form itself. There are some other things you can send with a form, but they are not useful to what you want to do: W3 Form Tag Specification
What you could do is do a form POST, which is the standard way to communicate when sessions cookies are out of the question and a query string won't do; just use a hidden field with some sort of token/hash of the credentials. Avoid clear-text of passwords like the plague, and really try to avoid reversible encryption of them too. This is just one of those areas you have to be extra careful to avoid creating an easily exploitable security vulnerability.
But generally speaking it works just fine, and anything that can do an AJAX GET should be able to do an AJAX POST.