Ident/auth with temporary URLs in Pyramid - pyramid

I'm building a Pyramid application. In "normal" usage, users have to use typical username/password to login and do much of anything. The Pyramid documentation made it pretty easy to cut and paste and get that going.
However, now I want to extend limited (both in authority and time -- permission expires on a given date) editing ability to people that I don't want to experience any account/password UI. I just want to email them a link that I generate, and when they click on the link I want them to land on the associated page and get identified and authorized to make some limited changes.
All the obvious stuff like generating a link, storing it in the database, associating a username and expiration date, is no problem. It's plugging this into the Pyramid ident/auth framework that I don't know how to do. I made it this far without really understanding their code in depth, and am hoping that someone has a code example of what I want to do lying around that could allow me to continue to not dive into that topic.
Or if the answer is stop being lazy and read the documentation, well, it cost me little to ask. :-)

Create a random number and expiration date and store them in database. Generate a link with this number and send it to the user. Check that when the link is clicked its generated random number matches one in the database. Authenticating Pyramid user by hand:
from pyramid.security import remember, forget
def authenticate_user(request, user)
if not user.can_login():
raise AuthenticationFailure('This user account cannot log in at the moment.')
# get_session_token_from_user() is your custom function.
# It usually returns user.id - it's the key how session backend maps sessions
# back to authenticated user.
token = get_session_token_from_user(user)
headers = remember(request, token)
# assert headers, "Authentication backend did not give us any session headers"
if not location:
location = get_config_route(request, 'websauna.login_redirect')
# Send any custom events related to user login your appplication cares of
e = events.Login(request, user)
request.registry.notify(e)
# Redirect user to the post-login form location
return HTTPFound(location=location, headers=headers)
For the specific use case of doing one time email link logins like Slack or Medium please see websauna.magiclogin addon.

Related

How can I protect a express route without authentication?

I'm trying to implement a GET method with Express in my nodeJs application.
I'd like to do something like this in order to display user data :
router.get("/user/:idUser", (req, res) => {
The user doesn't need to be authenticated in order to execute this code. However I don't want that anybody can execute this request with a user id of someone else. Because he could see data he's not supposed to see.
How could I proceed ? I thought about using some encryption process to have something like :
/user/PdfgdfJFDGTfrfgdsf
Your question isn't really making sense. You don't want authentication, but you only want a user to be able to view their own data so nobody else can view it.
The ONLY way to solve that is by using some form of authentication. The user has to prove to the server that they are allowed to view that data before the user renders the page for them.
Yes, you could obscure the URL (make it some mostly unguessable string of characters), but it's not clear what problem that is solving. The user themselves won't be able to remember it or type it so it would probably have to be a link in a web page and if it's a link in an unauthenticated web page, then anyone can get to it - thus defeating the purpose.
There are cases where temporary links (often done for privileged downloads) such as what you mention /user/PdfgdfJFDGTfrfgdsf are sent via an authenticated channel (either an authenticated webpage or sent to an email address known to belong to an authenticated user) and these links contain some unique and hard to guess code. The user can then click on that link (in authenticated webpage or in email) and access that resource without further authentication. In that case, the knowledge of the code in the URL is serving as a form of proof of authentication. Because URLs may be logged in service providers or corporate infrastructure and thus not remain entirely private, this technique has its limitations and is typically only used for short term (download this resource in the next 10 minutes) type of uses, not a long term substitute for authentication and not used for things that demand real security. You don't explain enough of your use case to know whether this is practical for your situation or not.
The user doesn't need to be authenticated in order to execute this code. However I don't want that anybody can execute this request with a user id of someone else. Because he could see data he's not supposed to see.
That's an inconsistent statement. You say "user doesn't need to be authenticated in order to execute this code" and then you say "I don't want that anybody can execute this request with a user id of someone else.". You can't have both. The user is either required to prove authorization or they aren't. Pick one. It can't be both.
you can use jwt for this and a auth middleware for this
upon decoding jwt token, you can implement logic to check if the decodedToken.user_id (given that you add user_id when encode token payload) is equal to the :idUser in route (or any kind of logic you want) there.

Auto-login best practices

I'm developing a web app that sends the user an email notification to complete a lesson/tutorial. I've added the ability to automatically login the user via the link in that email. This featured has been added to several services around the internet, most notable, OkCupid.
Here's how I've set up my table:
+----+-------------+-------------------+-----------+--------------+----------------------+
| id | key (22) | secret (40) | user_id | action | expires |
+----+-------------+-------------------+-----------+--------------+----------------------+
| 1 | IbQlQW8Dn...| hdC4dXQJUPA0... | 1 | lesson/14 | 2013-06-21 16:28:55 |
+----+-------------+-------------------+-----------+--------------+----------------------+
When a user visits a link via the email, something like:
http://example.com/go/IbQlQW8Dn8PNXJFFwHQxwh/hdC4dXQJUPA0pU7I6eUiXawbnobYv0iThA
[http:/example.com/go/key/secret]
The server first checks that the url isn't expired based on the date in the table. If it isn't expired, the user is automatically logged in using the user_id and then redirected to the given url in the action column. I used two separate values (key & secret) for the url just for added security (prevent fusking).
Now because of the nature of the site (video lessons), security isn't a huge concern, but I'd still like to know what best practices to consider.
Should I limit the number of times a link can be used?
Currently I have the link expire 60 hours (3 days) from when the
email is sent. Should this be lowered?
Obvious two risks for unauthorized access include someone forwarding the email or someone gaining access to the user's email account. Anything else to consider?
Thanks for everyones insight, if this should be moved to another section of StackExchange, please let me know. I know I've seen other best practice post on here in the past.
Sending an auto-login link is fairly similar in risk to sending password-reset links in email and lots of sites do that.
This is a judgement call that you have to make. There's not a shared decision matrix that people use to decide what is and isn't an acceptable risk. What you're making here is more of a business decision, you're weighing the security risks versus ease of use (which can translate to more users and more business).
You need to ask the question 'What's the absolute worst thing in terms of site availability, business reputation and user experience that can happen if this feature is mis-used?'.
Additional things you should be concerned about:
People plucking your auto-login links of off of shared wifi networks
Auto-login links ending up in the logs of proxies between your server and the client
I recommend making the links single-use only or keeping the expiration time low. You should also put in monitoring that will alarm if a link is being overused.
You should also make sure you're not vulnerable to SQL injection when you take the secret and query the DB.
I pretty much agree with everything u2702 said in his answer, but you should also consider not allowing the user to change their password without confirming their current password if their current session was created from any form of autologin (cookie, link etc.). This can at least protect the user from getting locked out of their account by a changed password.
I think you’ve got all of the important stuff. I’d agree with everything #u2702 said and just add…
You might want to give users a way to invalidate their outstanding login links, or invalidate them automatically when they change their email address or password.
If the site sends out a lot of email, you can skip the database and encode the user, target, and expiration in the URL along with a signature. Amazon supports this for temporarily granting access to objects in S3 (http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html) and other sites use it for CSRF tokens. Pseudocode:
params = encode_qs(userid=1, target="lesson/14", expires=…)
url = "http://example.com/l/?" + params + "&" +
encode_qs(sig=hmac_sha1(secret, params))
(The secret you use to sign the request doesn't have to be unique per-email since it's not revealed.) This is no better than generating random keys, like you do, if you don't mind the load.
I don’t think you gain anything by limiting the number of times a link can be used before it expires (OkCupid doesn’t, some people hold onto the most recent email and use it more than once).
Does a separate “key” and “secret” give you any real extra security if they’re always used together?

Node.js user system

I'm currently working on a web application which deals with multiple users. Whilst it currently works, it relies on some real bad practises which I'll outline in a minute.
We're using MySQL as the database system, since we're updating our current application, we want to ensure everything is backwards compatible. Otherwise I'd look at MongoDB etc.
Our users are stored in a table aptly named login. This contains their username, email, hashed password etc and a field which contains a JSON encoded object of their preferences. There is no real reason for doing this over using a meta table.
So the bad practises:
We're storing the entire users login row, excluding their password (although this is an internal-only app) in a cookie. It's JSON encoded.
Once the user logs in we have a secure HTTP cookie, readable only via Node.js for their username and their password so that we can continue to keep the user logged in automatically.
We have a app.get('*') route which constantly ensures that the user has their three cookies and updates their acc cookie with new preferences. This means that every time the user switches page or accesses a new AJAX item (all under the same routes) they have an updated cookie.
Every time a user performs an action we do this to get their user id: JSON.parse(res.cookies.acc).agent_id yuck!
Now, each user is able to perform actions to certain elements on the page, this effects everyone as the application is internal and anybody can work on the data inside of it.
I know what I want to achieve and how it should be done in say PHP, but I can't figure out the most effective way in Node.js.
I've started creating a User module which would allow us to get the user who performed the action and neatly update their preferences etc. You can see this here bearing in mind that it's a WIP. The issue I'm having with the module is that it doesn't have access to the users cookies, since it's not "a part of" Express. Which explains the last bad practise.
What would be the best way to handle such a system and remain bad-practise free?
I doubt it meets all of your requirements but its worth checking out out Drywall; A website and user system for Node.js
Hopefully it (or parts of it) could be helpful to you.
http://jedireza.github.io/drywall/

Is this safe for client side code?

I'm writing a GWT application where users login and interact with their profile. I understand that each form entry needs to be validated on the server, however, I am unsure about potential security issues once the user has logged in.
Let me explain. My application (the relevant parts) works as follows:
1 - user enters email/pass
2 - this info is sent back to the server, a DB is queried, passwords are checked (which are salted and hashed)
3. if the passwords match the profile associated w/ the email, this is considered success
Now I am unsure whether or not it is safe to pass the profile ID back to the client, which would then be used to query the DB for information relevant to the user to be displayed on the profile page.
Is there a possibility for a potential user to manually provide this profile ID and load a profile that way? My concern is that somebody w/ bad intentions could, if they knew the format of the profile ID, load an arbitrary amount of information from my DB without providing credentials.
-Nick
What you are dealing with here is a session management issue. Ideally, you want a way to keep track of logged in users (using random values as the session key), know how long they have been idle, be able to extend sessions as the user is using the site, and expire sessions.
Simply passing the profile ID to the client, and relying on it to send it back for each request is not sufficient - you are correct with your concern.
You want to keep a list of sessions with expiration times in a database. Every time an action is executed that needs user permissions (which should be pretty much everything), check to see if the session is still valid, if it is, extend it by however long you want. If it is expired, kill the session completely and log the user out.
You can store your session keys in a cookie (you have to trust the client at some point), but make sure they are non-deterministic and have a very large keyspace so it cannot be brute forced to get a valid session.
Since you're logging a user in, you must be using a backend that supports sessions (PHP, .Net, JAVA, etc), as Stefan H. said. That means that you shouldn't keep any ids on your client side, since a simple id substitution might grant me full access to another user's account (depending on what functionality you expose on your client, of course).
Any server request to get sensitive info (or for any admin actions) for the logged in user should look something like getMyCreditCard(), setMyCreditCard(), etc (note that no unique ids are passed in).
Is there a possibility for a potential user to manually provide this profile ID and load a profile that way? My concern is that somebody w/ bad intentions could, if they knew the format of the profile ID, load an arbitrary amount of information from my DB without providing credentials.
Stefan H is correct that you can solve this via session management if your session keys are unguessable and unfixable.
Another way to solve it is to use crypto-primitives to prevent tampering with the ID.
For example, you can store a private key on your server and use it to sign the profile ID. On subsequent requests, your server can trust the profile ID if it passes the signature check.
Rule 1 - Avoid cooking up your own security solution and use existing tested approaches.
Rule 2 - If your server side is java then you should be thinking along the lines of jsessionid. Spring Security will give you a good starting point to manage session ids with additional security features. There will be similar existing frameworks across php too (i did not see server side language tags in the question).
Rule 3 - With GWT you come across javascript based security issues with Google Team documents and suggests XSRF and XSS security prevention steps. Reference - https://developers.google.com/web-toolkit/articles/security_for_gwt_applications

Facebook Javascript SDK security

I'm in the process of using the facebook javascript sdk to provide user login functionality for a website.
What I'd like to do is simply take the logged in user's unique facebook id and then put/fetch data to/from a mysql database using the id to determine what data is available to said user.
However I don't really feel like this is very secure. Whilst I'm not storing anything sensitive like credit-card details etc, I'd obviously prefer it to be as secure as practically possible.
My fear is that with javascript being what is it, someone could fake the facebook id and just pull whatever they wanted.
I'm aware that the php sdk would provide a solid solution to this problem, but i like the javascript one mainly because it's easy to use and I have the basis of it set up (I admit it, I'm lazy).
So, my questions are:
Would this set up be as insecure as I feel it might be?
Is there anything I can do to improve the security of such a system, other than switching to the php sdk?
Thanks!
Facebook Ids are pretty hard to make up (at most a user will only know their own). Depending on what you store in the database (which will not be anything that the user cannot get on their own, unless you ask for extended permissions)
If you are worried about a user trying to get information from the database, add an access token or signed request to each row and us that and facebook id to get data. That will greatly increase security.
EDIT
There are few occasions where you get a signed request from a user:
* A signed_request is passed to Apps on Facebook.com when they are loaded into the Facebook environment
* A signed_request is passed to any app that has registered an Deauthorized Callback in the Developer App whenever a given user removes the app using the App Dashboard
* A signed_request is passed to apps that use the Registration Plugin whenever a user successfully registers with their app
Signed requests will contain a user id only if the use has accepted permissions though, and are not passed again if the user enters the application, and accepts permissions (meaning that the signed request would not contain the ID). Because of this saving an access token may be a better idea. Here is more on the signed request
Also the signed request is in the url (param = "signed_request"). I always parse it through c# but I am sure you can at least get one through javascript
It's pretty easy to spoof the origin using curl. I'd imagine Facebook has another mecanism in place to make this possible. If you inspect their code, it appears that they generate an iframe and pass requests through. If I had to guess, they have setup the requests to only be made from the Facebook domain, and ensure that the iframe can only be embedded in a page that has a white listed domain.

Resources