I'm storing django's session key (request.session.session_key) in a database column-- It's an app that doesn't require logins, and sessions are a acceptable enough proxy for a unique user. It works fine, except now that I'm writing tests.
Since the test client preserves cookies, I assumed it the session id would be stable over multiple requests. It seems like this is incorrect, though.
For example, I was hoping that in...
browser=Client()
browser.post(rating_url, {'term':'Helpful', 'action':'add'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
browser.post(rating_url, {'term':'Helpful', 'action':'remove'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
... the view would receive the same session key both times. It doesn't work that way, though.
If anyone can suggest a work-around, I'd appreciate it.
There is the feature request for simplification of anonymous sessions usage in unit tests. In this ticket you can find the hard way to do it. Here is the link - https://code.djangoproject.com/ticket/10899
Related
The context is testing of a web app with selenium while using a number of virtual user accounts we created for this very purpose. And so the testing process needs to access our sites and log-on with the virtual user's id and password.
None of these accounts are critical and they are flagged as testing accounts so no damage can be done. Still, it would probably be a good idea to encrypt the passwords and decrypt them prior to use.
If it matter, our test app is written in Python, Django and uses PostgreSQL for the database. It runs on a small Linode instance.
What might best practices be for something like this?
EDIT 1
The other thought I had was to store the credentials on a second machine and access them through and API while only allowing that access to happen from a known server's non-public IP. In other words, get two instances at Linode and create a private machine-to-machine connection within the data center.
In this scenario, access to the first machine would allow someone to potentially make requests to the second machine if they are able to de-obfuscate the API code. If someone really wants the data they can certainly get it.
We could add two factor authentication as a way to gate the tests. In other words, even if you had our unencrypted test_users table you couldn't do anything with them because of the 2FA mechanism in place just for these users.
Being that this is for testing purposes only I am starting to think the best solution might very well be to populate the test_users table with valid passwords only while running a test. We could keep the data safe elsewhere and have a script that uploads the data to the test server when we want to run a test suite. Someone with access to this table could not do thing with it because all the passwords would be invalid. In fact, we could probably use this fact to detect such a breach.
I just hate the idea of storing unencrypted passwords even if it is for test users that can't really do any damage to the actual app (their transactions being virtual).
EDIT 2
An improvement to that would be to go ahead and encrypt the data and keep it in the test server. However, every time the tests are run the system would reach out to us for the crypto key. And, perhaps, after the test is run the data is re-encrypted with a new key. A little convoluted but it would allow for encrypted passwords (and even user id's, just to make it harder) on the test server. The all-important key would be nowhere near the server and it would self-destruct after each use.
What is generally done in a case like this is to put the password through a cryptographic hash function, and store the hashed password.
To verify a login, hash the provided password and compare the calculated hash to the stored version.
The idea behind this is that it is considered impossible to reverse a good cryptographic hash function. So it doesn't matter if an attacker could read the hashed passwords.
Example in Python3:
In [1]: import hashlib
In [2]: hashlib.sha256('This is a test'.encode('utf8')).hexdigest()
Out[2]: 'c7be1ed902fb8dd4d48997c6452f5d7e509fbcdbe2808b16bcf4edce4c07d14e'
In [3]: hashlib.sha256('This is a tist'.encode('utf8')).hexdigest()
Out[3]: 'f80b4162fc28f1f67d1a566da60c6c5c165838a209e89f590986333d62162cba'
In [4]: hashlib.sha256('This is a tst.'.encode('utf8')).hexdigest()
Out[4]: '1133d07c24ef5f46196ff70026b68c4fa703d25a9f12405ff5384044db4e2adf'
(for Python2, just leave out the encode.)
As you can see, even one-letter changes lead to a big change in the hash value.
I have a flask running at domain.com
I also have another flask instance on another server running at username.domain.com
Normally user logs in through domain.com
However, for paying users they are suppose to login at username.domain.com
Using flask, how can I make sure that sessions are shared between domain.com and username.domain.com while ensuring that they will only have access to the specifically matching username.domain.com ?
I am concerned about security here.
EDIT:
Later, after reading your full question I noticed the original answer is not what you're looking for.
I've left the original at the bottom of this answer for Googlers, but the revised version is below.
Cookies are automatically sent to subdomains on a domain (in most modern browsers the domain name must contain a period (indicating a TLD) for this behavior to occur). The authentication will need to happen as a pre-processor, and your session will need to be managed from a centralised source. Let's walk through it.
To confirm, I'll proceed assuming (from what you've told me) your setup is as follows:
SERVER 1:
Flask app for domain.com
SERVER 2:
Flask app for user profiles at username.domain.com
A problem that first must be overcome is storing the sessions in a location that is accessible to both servers. Since by default sessions are stored on disk (and both servers obviously don't share the same hard drive), we'll need to do some modifications to both the existing setup and the new Flask app for user profiles.
Step one is to choose where to store your sessions, a database powered by a DBMS such as MySQL, Postgres, etc. is a common choice, but people also often choose to put them somewhere more ephemeral such as Memcachd or Redis for example.
The short version for choosing between these two starkly different systems breaks down to the following:
Database
Databases are readily available
It's likely you already have a database implemented
Developers usually have a pre-existing knowledge of their chosen database
Memory (Redis/Memchachd/etc.)
Considerably faster
Systems often offer basic self-management of data
Doesn't incur extra load on existing database
You can find some examples database sessions in flask here and here.
While Redis would be more difficult to setup depending on each users level of experience, it would be the option I recommend. You can see an example of doing this here.
The rest I think is covered in the original answer, part of which demonstrates the matching of username to database record (the larger code block).
Old solution for a single Flask app
Firstly, you'll have to setup Flask to handle subdomains, this is as easy as specifying a new variable name in your config file. For example, if your domain was example.com you would append the following to your Flask configuration.
SERVER_NAME = "example.com"
You can read more about this option here.
Something quick here to note is that this will be extremely difficult (if not impossible) to test if you're just working off of localhost. As mentioned above, browsers often won't bother to send cookies to subdomains of a domain without dots in the name (a TLD). Localhost also isn't set up to allow subdomains by default in many operating systems. There are ways to do this like defining your own DNS entries that you can look into (/etc/hosts on *UNIX, %system32%/etc/hosts on Windows).
Once you've got your config ready, you'll need to define a Blueprint for a subdomain wildard.
This is done pretty easily:
from flask import Blueprint
from flask.ext.login import current_user
# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")
# Define our route
#deep_blue.route('/')
def user_index(username):
if not current_user.is_authenticated():
# The user needs to log in
return "Please log in"
elif username != current_user.username:
# This is not the correct user.
return "Unauthorized"
# It's the right user!
return "Welcome back!"
The trick here is to make sure the __repr__ for your user object includes a username key. For eg...
class User(db.Model):
username = db.Column(db.String)
def __repr__(self):
return "<User {self.id}, username={self.username}>".format(self=self)
Something to note though is the problem that arises when a username contains special characters (a space, #, ?, etc.) that don't work in a URL. For this you'll need to either enforce restrictions on the username, or properly escape the name first and unescape it when validating it.
If you've got any questions or requests, please ask. Did this during my coffee break so it was a bit rushed.
You can do this with the builtin Flask sessions, which are cookie-based client-side sessions. To allow users to login to multiple subdomains in '.domain.com', you need only to specify
app.config['SESSION_COOKIE_DOMAIN'] = '.domain.com'
and the client's browser will have a session cookie that allows him to login to every Flask instance that is at 'domain.com'.
This only works if every instance of Flask has the same app.secret_key
For more information, also see
Same Flask login session across two applications
I've a Flask application, served with Nginx+WSGI (FastCGI & Gevent) and use standard Flask sessions. I do not use the session.permanent=True or any other extra option, but simply set SECRET_KEY in the default configuration.
I do not save any (key,value) pairs in the session, and only rely on the SID = session['_id'] entry to identify a returning user. I use the following code the read the SID:
#page.route ('/')
def main (page='home', template='index.html'):
if not request.args.get ('silent', False):
print >> sys.stderr, "Session ID: %r" % session['_id']
I made the following observations:
For same IP addresses, but different browsers I get different SIDs - that's expected;
For different IPs & same browser I again have different SIDs - expected;
For same IP address with same browser I get same SID - also expected;
Now, point (3) is interesting because even if a delete the corresponding cookie the SID remains constant! To some extent even that might be understandable, but actually I was expecting the SID to change between different cookies. But the only difference I see is that
session.new is True
for the first request immediately after the deletion of the cookie. Even that is very much expected; but given these facts I face the following problems:
Does this mean that for different users sitting behind the same IP (with the same browser configuration) my back-end will mistake them for the same user?
If point (1) is not the case, the current behavior of these "sticky" sessions is actually quite pleasant, since this avoids the situation where my users might loose there data just because they deleted the corresponding cookie.
They can still save the day, by revisiting the site from the same network with the same browser. I like that, but only if point (1) is not the case.
I assume point (1) will actually bite me, would the conclusion actually be to save a token in the session and hence accept the fate that the user can blow himself up, by simply deleting his cookie?
Or is there a way to tell Flask to give different SIDs for each fresh cookie?
Actually, this question arouse since I used a load impact service, which was simulating different users (on the same IP) but my back-end kept seeing them as a single user since the corresponding SIDs were all the same.
The application is available for tests at http://webed.blackhan.ch (which upon release will move the https://notex.ch [a browser based text editor]). Thank you for your answers.
It looks like you're using the Flask-Login extension. Here's the code that generates the id token:
def _create_identifier():
base = unicode("%s|%s" % (request.remote_addr,
request.headers.get("User-Agent")), 'utf8', errors='replace')
hsh = md5()
hsh.update(base.encode("utf8"))
return hsh.digest()
It's basically just md5(ip_address + user_agent).
Flask uses Werkzeug's secure cookies to store this identifier. Secure cookies are (as their name suggests) secure:
This module implements a cookie that is not alterable from the client because it adds a checksum the server checks for. You can use it as session replacement if all you have is a user id or something to mark a logged in user.
session['_id'] is not an actual session identifier. It's just a value used by Flask-Login to implement Session Protection.
Standard Flask sessions do not have an SID - as it would serve no purpose since the actual content of the session is stored in the cookie itself. Also see this.
it's now 2022, and Flask-Session does support session.sid to get a generated UUID that looks something like this:
print(session.sid)
>>> f9c792fa-70e0-46e3-b84a-3a11813468ce
From the docs (https://flasksession.readthedocs.io/en/latest/)
sid
Session id, internally we use uuid.uuid4() to generate one session id. You can access it with session.sid.
I'm looking to write some unit tests for some cherrypy code.
Most of the unit test examples I have seen depend on os.environ and generally only test for the final response (html output) from the cherrypy apps.
I'm looking for:
testing that session variables have been set/unset for a given request (e.g., user logs in, I want to verify that his 'user' session variable is correct as well as his login time, etc...
checking values in a datastore (mysql/mongodb)
running tests in parallel. By default you access session and other information from cherrypy.cookie/cherrypy.session, etc... These are basically global. Is it possible to access this information another way?
Any other recommendations would be greatly appreciated.
You could write a small python http client and (assuming you run your server) unittest the responses.
Some libs that might be useful:
urllib
urllib2
http.client
Also you might want to check out selenium (allows you to script your browser in python for test cases).
I'm using pyramid web framework. I was confused by the relationship between the cookie and session. After looked up in wikipedia, did I know that session is an abstract concept and cookie may just be an kind of approach (on the client side).
So, my question is, what's the most common implementation (on both the client and server)? Can somebody give some example (maybe just description) codes? (I wouldn't like to use the provided session support inside the pyramid in order to learn)
The most common implementation of sessions is to use a cookie.
A cookie provides a way to store an arbitrary piece of text, which is usually used as a session identifier. When the cookie gets sent along with a HTTP request, the server (technically the code running on it) can use the cookie text (if it exists) to recognise that it has seen a client before. Text in a cookie usually provides enough information to retrieve extra information from the database about this client.
For example, a very naive implementation might store the primary key to the shopping_cart table in a database, so that when the server receives the cookie text it can directly use it to access the appropriate shopping cart for that particular client.
(And it's a naive approach because a user can do something like change their own cookie to a different primary key and access someone else's cart that way. Choosing a proper session id isn't as simple as it seems, which is why it's almost always better to use an existing implementation of sessions.)
An alternate approach is to store a session identifier is to use a GET parameter in the url (for example, in something like http://example.com/some/page?sid=4s6da4sdasd48, then the sid GET param serves the same function as the cookie string). In this approach, all links to other pages on the site have the GET param appended to them.
In general, the cookie stored with the client is just a long, hard-to-guess hash code string that can be used as a key into a database. On the server side, you have a table mapping those session hashes to primary keys (a session hash should never be a primary key) and expiration timestamps.
So when you get a request, first thing you do is look for the cookie. If there isn't one, create a session entry (cookie + expiration timestamp) in the database table. If there is one, look it up and make sure it hasn't expired; if it has, make a new one. In either case, if you made a new cookie, you might want to pass that fact down to later code so it knows if it needs to ask for a login or something. If you didn't need to make a new cookie, reset the expiration timestamp so you don't expire the session too soon.
While handling the view code and generating a response, you can use that session primary key to index into other tables that have data associated with the session. Finally, in the response sent back to the client, set the cookie to the session key hash.
If someone has cookies disabled, then their session cookie will always be new, and any session-based features won't work.
A session is (usually) a cookie that has a unique value. This value maps to a value in a database or held in memory that then tells you what session to load. PHP has an alternate method where it appends a unique value to the end of every URL (if you've ever seen PHPSESSID in a URL you now know why) but that has security implications (in theory).
Of course, since cookies are sent back and forth with every request unless you're talking over HTTPS you are sending the only way to know (reliably) that the client you are talking to now is the same one you logged in ten seconds ago to anyone on the same wireless network. See programs like Firesheep for reasons why switching to HTTPS is a good idea.
Finally, if you do want to build your own I, was given some advice on the matter by a university professor. Give out a new token on every page load and invalidate all a users tokens if an invalid token is used. This just means that if an attacker does get a token and logs in to it whilst it is still valid when the victim clicks a link both parties get logged out.