tl;dr: Python newbie, Django's session not propagated correctly while using HTTPS
I'm building a basic web service which rely on session/cookies to authentication an user.
During the first authentication, I configure a specific session like this:
request.session['userSecureId'] = "blabla"
return HttpResponseRedirect('http://localhost/secure',context)
At this point, a new session key has been added to django_session table. A basic b64 decode on the session_data field confirm the presence of 'userSecureId'
On my view, I check if this session exist like this:
if request.session.get('userSecureId'):
# do something
If I try this on my local system (plain HTTP), it works great. So my next step was to run it on my remote server with SSL enabled. I've configured
SESSION_COOKIE_SECURE = True on my settings.py but now, the value returned by 'userSecureId' is always None.
This is probably a newbie question, so any pointer will be appreciated =)
Additionally, If I print request.session.session_key I'm able to successfully retrieve the session key, meaning Django correctly detect my sessionid cookie, but can't decode the content of session_value
EDIT: I just tried accessing Django on my remote system (same configuration) and I'm facing the same issue. I have no idea why I can't run the session value. Code works using 127.0.0.1 w/o problem though
According to here and here
To share a session between HTTP and HTTPS (and cross domain also), you should set SESSION_COOKIE_DOMAIN in your settings.
SESSION_COOKIE_DOMAIN = '.example.com'
Related
I a making a web application with a session cookie log in system. When using the cookies they expire within seconds, logging the user out of any service they were in. When I open my app I occasionaly get a warning in the terminal that states UserWarning: The session cookie domain is an IP address. This may not work as intended in some browsers. Add an entry to your hosts file, for example "localhost.localdomain", and use that instead. I'm hosting this app on Heroku so I don't think editing my local file would help, but if theres a way to get this to be solved on Heroku that would be great. Another error message I get comes from the console in the website itself, which reads:
Cookie “session” will be soon rejected because it has the “SameSite” attribute set to “None”
or an invalid value, without the “secure” attribute. To know more about the “SameSite“
attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite
I set the Session cookie in my web application to:
app.config["SESSION_FILE_DIR"] = tempfile.mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config["SESSION_COOKIE_SECURE"] = True
app.config["SESSION_COOKIE_SAMESITE"] = "None"
Session(app)
But this didn't solve my problem and both errors keep coming up. If there's any way to manually set SameSite and Secure that would be fantastic. Getting a https connection on Heroku did not work, I don't know why this is happening and it breaks the site, if there's any advice anyone has that would be greatly appreciated!
You need to use a domain name to access the service (https://domain.xxx/) and not the IP-address (https://123.123.123.213).
To avoid a lot of pain and errors, you should aim to use HTTPS, especially if you want cookies to work properly. Both the Secure and SameSite attributes requires HTTPS to work properly in most cases. And to get HTTPS to work you need a domain name and a proper certificate.
I have multiple Django dev sites running locally like http://localhost:8000, http://localhost:8001, http://localhost:8002, etc.
Originally, I had SESSION_COOKIE_DOMAIN and CSRF_COOKIE_DOMAIN set to '' or 127.0.0.1 but this causes each site to overwrite the other's cookies, causing me to have to login every time I switch between sites. I tried using 127.0.0.1:<port> but that had no effect.
How do I get these sites to use separate cookies?
One solution to this problem is to use local domain name resolution to reach each of your different development servers. If you leave SESSION_COOKIE_DOMAIN as None, then the returned cookie is a standard domain cookie and will have the same domain as the request.
Have a look at http://en.wikipedia.org/wiki/Hosts_(file) which describes how to add local host file entries.
With a hosts file like this:
127.0.0.1 www.testserver1.com www.testserver2.com
You could then access each of your different test servers at:
http://www.testserver1.com:8000
http://www.testserver2.com:8001
I haven't tried this, but I believe it should work.
Alternatively, as per Mikhail's answer, use a different session cookie name for each instance.
Cookies are shared across ports for the same domain according to various RFCs (see e.g. https://www.rfc-editor.org/rfc/rfc6265#section-8.5). So this is not django-specific.
I think you could use different SESSION_COOKIE_NAME to have at least sessions separated.
I'm using the latest Flask/Werkzeug (Flask 0.9) client-side sessions to persist information between requests. The session is not set to be persistent (as I'm fine with the cookie being deleted when the browser is closed).
My problem is as follows:
I use some server-side code to fill the Flask session variable with an entry. After this, the Session variable looks something like this:
<SecureCookieSession {u'items': SOMENOTVERYIMPORTANTDICTIONARY}, '_fresh': True, 'user_id': u'1', 'csrf': '0aef1995cdf2cxx0233fdf3321d17fc7267f3b32', '_id': 'someUNIQUEcode'}*>
I use this information to render a page that performs a GET request (through JQuery) to the same Flask application, but suddenly the dictionary containing the 'items' entry in the session is gone:
<SecureCookieSession {'_fresh': True, 'user_id': u'1', 'csrf': '0aef1995cdf2cxx0233fdf3321d17fc7267f3b32', '_id': 'someUNIQUEcode'}>
I did some searching around, and thought that it may be related to the fact that I'm testing on localhost (127.0.0.1 is not the same as localhost). I fixed my hosts file and added a 'dev.localhost' entry to make sure that all requests are from the same host.
Also, the developer pane of my browser (Chrome) shows exactly the same identifiers for the session cookies being sent to the server.
Also, setting session.modified = True does not help.
The only thing that changes between requests is
__utmb=122666782.18.10.1363877633
for the first request (the one that populates the items entry) vs. the second request
__utmb=122666782.19.10.1363877633
Thinking that it still may be an Ajax-related-thing. I tested the contents of the session variable after a straightforward page reload: the items entry is still gone from the session.
Any help would be greatly appreciated.
It turns out that the cookie being sent back to the client (Chrome) exceeds the 4096 bytes limit for cookie size. Apparently Django uses server-side sessions by default, which made this problem only appear when I moved my code to Flask. Using server side sessions in Flask such as in flask-kvsession and others should fix the issue.
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 am working on a python web application based on the pyramid framework. I am trying to add session authentication to it. By that I understand that:
users can log in/out (security is desirable); user data are kept in a database
authentication is handled via the session (request.session)
First off: Is session authentication a good option or are there better ones?
Secondly: I can't really make heads or tails of the documentation and examples.
So far, I've followed http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/tutorials/wiki2/authorization.html#adding-login-and-logout-views so far that I have a login/logout form. However, my authn_policy is a http://docs.pylonsproject.org/projects/pyramid/en/latest/api/authentication.html#pyramid.authentication.SessionAuthenticationPolicy
As the session factory in pyramid is insecure (see http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/sessions.html), I use *pyramid_beaker* instead.
The configuration is:
in __init__.py: session_factory = session_factory_from_settings(settings)
in the .ini file:
beaker.session.lock_dir = %(here)s/data/sessions/lock
beaker.session.type = ext:database
beaker.session.sa.url = mysql://user:pass#localhost:3306/db
beaker.session.table_name = user_session
I hope I was able to make my problem clear.
I'd say it depends on what you want to do. Session authentication works fine if you use Beaker, but I like using AuthTktAuthenticationPolicy for the additional timeout and reissue options, and the fact that your authentication doesn't disappear even if you clear the session.