I use flask session in my app. In one of my handler I set session value and no session set in other handlers. But I found that in every response there is a http header: Set Cookie exists. Why does that happen?
app = Flask(__name__)
app.secret_key = r"A0Zr98j/3yX R~XHH!jmN'LWX/,?RT"
#app.route('/auth/login', methods=['POST'])
#crossdomain(origin='*')
def authlogin():
user = User(username=username, registered_at=sqlnow())
user.accounts = [Account(provider='weibo', access_token=access_token, uid=uid)]
account = user.accounts[0]
session['user_id'] = account.user_id
return jsonify({
'status': 'success',
'data': {
'user_id': account.user_id,
'uid': account.uid
}
})
#app.route('/api/movies/<movie_type>')
def moviescoming(movie_type):
if movie_type == 'coming':
return getmovies(MOVIE_TYPE_PLAYING, offset, limit)
else:
return getmovies(MOVIE_TYPE_COMING, offset, limit)
app.run(host='0.0.0.0', debug=True)
Code shows here:
https://github.com/aisensiy/dianying/blob/master/index.py
The Short Answer:
This is by design, but a recent change in Flask allows you to change this behavior through use of the SESSION_REFRESH_EACH_REQUEST option. As of the posting of this answer, that option is not yet in a stable release of Flask.
The Long Answer
Let's back up and discuss how cookies are supposed to work to begin with:
Cookies as a Standard
RFC 6265 defines that a cookie should expire when the agent (the browser) declares the session closed (typically, when the browser is closed), unless there was provided some mechanism to tell the browser when the cookie should actually expire:
Unless the cookie's attributes indicate otherwise, the cookie [...]
expires at the end of the current session (as defined by the user
agent).
[...]
If a cookie has neither the Max-Age nor the Expires attribute, the
user agent will retain the cookie until "the current session is over"
(as defined by the user agent).
If the server wishes a cookie to survive an agent restart, they need to set an expiration. Note that the Expires attribute is typically preferred due to the fact that Internet Explorer has a history of poor support for max-age.
Creating Permanent Cookies
So, it's impossible to say that a cookie should be "permanent". When people talk about a "permanent" cookie, what they really are talking about is a cookie that survives a browser restart. There are two strategies that I know of for creating this "permanent" cookie:
Set the cookie's expiration to something that is good enough to be considered permanent (such as the year 9999).
Set the cookie's expiration to something relatively recent in the future (e.g., 31 days), but every time the cookie is used update the expiration again. For example, on January 1st we will set the cookie to expire on February 1st, but then when the user uses the cookie on January 2nd we are updating the cookie (by using Set-Cookie) to have it expire on February 2nd.
The first method requires the Set-Cookie header to only be set to the client once (unless the cookie contents need to change).
The second method would require the Set-Cookie header to be sent with every update so that the expiration is constantly "pushed off" as the user continues to use the service. Note that it also isn't really "permanent", as a user that does not use your site for over 31 days will have their cookie expire.
RFC 6265 does have a bit to say on defining the expiration date:
Although servers can set the expiration date for cookies to the
distant future, most user agents do not actually retain cookies for
multiple decades. Rather than choosing gratuitously long expiration
periods, servers SHOULD promote user privacy by selecting reasonable
cookie expiration periods based on the purpose of the cookie. For
example, a typical session identifier might reasonably be set to
expire in two weeks.
So, while it doesn't explicitly say whether or not to be constantly updating the expiration date, it does seem to say that using a far-future date should NOT be considered a good practice.
Flask's Implementation of "Permanent Cookies"
Flask uses the second method (constantly updating the cookie expiration with Set-Cookie) by design. By default, the expiration of the cookie will be 31 days in the future (configurable by PERMANENT_SESSION_LIFETIME). With every request, Flask will use another Set-Cookie to push the expiration out another 31 days (or whatever you set your permanent session lifetime value to). Therefore, the Set-Cookie on every request you see is expected, even if the session has not changed.
Recently, however, there has been a discussion in a pull request regarding using Set-Cookie only when the cookie changes. This resulted in a new feature that allows the user to change how this works. Flask will continue to work as it has, but the user can set a new SESSION_REFRESH_EACH_REQUEST option to False, which will cause the Set-Cookie header to only be sent when the cookie changes.
The new item is documented as:
this flag controls how permanent sessions are refresh [sic]. If set to
True (which is the default) then the cookie is refreshed each
request which automatically bumps the lifetime. If set to False a
set-cookie header is only sent if the session is modified. Non
permanent sessions are not affected by this.
This new option, together with the existing PERMANENT_SESSION_LIFETIME, allows Flask developers to better tune exactly how their "permanent" cookies will be set to expire.
As of this answer's posting date (December 24th, 2013), the SESSION_REFRESH_EACH_REQUEST option has not been part of any Flask release, and therefore users wishing to use it will need to wait for a future Flask release.
Related
Our Django application has the following session management requirements.
Sessions expire when the user closes the browser.
Sessions expire after a period of inactivity.
Detect when a session expires due to inactivity and display appropriate message to the user.
Warn users of a impending session expiry a few minutes before the end of the inactivity period. Along with the warning, provide users an option to extend their session.
If user is working on a long business activity within the app that doesn't involve requests being sent to the server, the session must not timeout.
After reading the documentation, Django code and some blog posts related to this, I have come up with the following implementation approach.
Requirement 1
This requirement is easily implemented by setting SESSION_EXPIRE_AT_BROWSER_CLOSE to True.
Requirement 2
I have seen a few recommendations to use SESSION_COOKIE_AGE to set the session expiry period. But this method has the following problems.
The session always expires at the end of the SESSION_COOKIE_AGE even if the user is actively using the application. (This can be prevented by setting the session expiry to SESSION_COOKIE_AGE on every request using a custom middleware or by saving the session on every request by setting SESSION_SAVE_EVERY_REQUEST to true. But the next problem is unavoidable due to the use of SESSION_COOKIE_AGE.)
Due to the way cookies work, SESSION_EXPIRE_AT_BROWSER_CLOSE and SESSION_COOKIE_AGE are mutually exclusive i.e. the cookie either expires on browser close or at the specified expiry time. If SESSION_COOKIE_AGE is used and the user closes the browser before the cookie expires, the cookie is retained and reopening the browser will allow the user (or anyone else) into the system without being re-authenticated.
Django relies only on the cookie being present to determine if the session is active. It doesn't check the session expiry date stored with the session.
The following method could be used to implemented this requirement and to workaround the problems mentioned above.
Do not set SESSION_COOKIE_AGE.
Set the expiry date of the session to be 'current time + inactivity period' on every request.
Override process_request in SessionMiddleware and check for session expiry. Discard the session if it has expired.
Requirement 3
When we detect that the session has expired (in the custom SessionMiddleware above), set an attribute on the request to indicate session expiry. This attribute can be used to display an appropriate message to the user.
Requirement 4
Use JavaScript to detect user inactivity, provide the warning and also an option to extend the session. If the user wishes to extend, send a keep alive pulse to the server to extend the session.
Requirement 5
Use JavaScript to detect user activity (during the long business operation) and send keep alive pulses to the server to prevent session from expiring.
The above implementation approach seem very elaborate and I was wondering if there might a simpler method (especially for Requirement 2).
Any insights will be highly appreciated.
I am just pretty new to use Django.
I wanted to make session expire if logged user close browser or are in idle(inactivity timeout) for some amount of time. When I googled it to figure out, this SOF question came up first. Thanks to nice answer, I looked up resources to understand how middlewares works during request/response cycle in Django. It was very helpful.
I was about to apply custom middleware into my code following top answer in here. But I was still little bit suspicious because best answer in here was edited in 2011. I took more time to search little bit from recent search result and came up with simple way.
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_AGE = 10 # set just 10 seconds to test
SESSION_SAVE_EVERY_REQUEST = True
I didn't check other browsers but chrome.
A session expired when I closed a browser even if SESSION_COOKIE_AGE set.
Only when I was idle for more than 10 seconds, A session expired. Thanks to SESSION_SAVE_EVERY_REQUEST, whenever you occur new request, It saves the session and updates timeout to expire
To change this default behavior, set the SESSION_SAVE_EVERY_REQUEST setting to True. When set to True, Django will save the session to the database on every single request.
Note that the session cookie is only sent when a session has been created or modified. If SESSION_SAVE_EVERY_REQUEST is True, the session cookie will be sent on every request.
Similarly, the expires part of a session cookie is updated each time the session cookie is sent.
django manual 1.10
I just leave answer so that some people who is a kind of new in Django like me don't spend much time to find out solution as a way I did.
Here's an idea... Expire the session on browser close with the SESSION_EXPIRE_AT_BROWSER_CLOSE setting. Then set a timestamp in the session on every request like so.
request.session['last_activity'] = datetime.now()
and add a middleware to detect if the session is expired. something like this should handle the whole process...
from datetime import datetime
from django.http import HttpResponseRedirect
class SessionExpiredMiddleware:
def process_request(request):
last_activity = request.session['last_activity']
now = datetime.now()
if (now - last_activity).minutes > 10:
# Do logout / expire session
# and then...
return HttpResponseRedirect("LOGIN_PAGE_URL")
if not request.is_ajax():
# don't set this for ajax requests or else your
# expired session checks will keep the session from
# expiring :)
request.session['last_activity'] = now
Then you just have to make some urls and views to return relevant data to the ajax calls regarding the session expiry.
when the user opts to "renew" the session, so to speak, all you have to do is set requeset.session['last_activity'] to the current time again
Obviously this code is only a start... but it should get you on the right path
django-session-security does just that...
... with an additional requirement: if the server doesn't respond or an attacker disconnected the internet connection: it should expire anyway.
Disclamer: I maintain this app. But I've been watching this thread for a very, very long time :)
One easy way to satisfy your second requirement would be to set SESSION_COOKIE_AGE value in settings.py to a suitable amount of seconds. For instance:
SESSION_COOKIE_AGE = 600 #10 minutes.
However, by only doing this the session will expire after 10 minutes whether or not the user exhibits some activity. To deal with this issue, expiration time can be automatically renewed (for another extra 10 minutes) every time the user performs any kind of request with the following sentence:
request.session.set_expiry(request.session.get_expiry_age())
also you can use
stackoverflow build in functions
SESSION_SAVE_EVERY_REQUEST = True
In the first request, you can set the session expiry as
self.request.session['access_key'] = access_key
self.request.session['access_token'] = access_token
self.request.session.set_expiry(set_age) #in seconds
And when using the access_key and token,
try:
key = self.request.session['access_key']
except KeyError:
age = self.request.session.get_expiry_age()
if age > set_age:
#redirect to login page
I'm using Django 3.2 and i recommend using the django-auto-logout package.
It allows active time and idle time session control.
In the template you can use variables together with Javascript.
I'm using Flask builtin session mecanism.
Here is my understanding of session mecanism (with flask) :
all session data are stored in a signed cookie (with app.secret_key)
when a session data is modified, the cookie is changed
session's data are protected against write client side (due to signature) but not against read
Imagine the following scenario :
In my session I put a variable try_number=3
Each time the user make an action, a decrease this number
If this number is equal to 0, action is forbidden
The user connect to the application for the first time, the application send a Set-Cookie: sesssion=Flask.sign("try_number=3"), let's call this cookie COOKIE_A.
The user perform his first action, he send COOKIE_A, the application reply with Set-Cookie: sesssion=Flask.sign("try_number=2"), let's call this cookie COOKIE_B.
Now, if the user perform another action, but doesn't use COOKIE_B but COOKIE_A again (using curl for exemple), the cookie is still signed, and will be handled by the server, with try_number=3.
Therefore, only using the COOKIE_A for all operation, he will be able to "spoof" session mecanism, and make unlimited action with the same session.
Is there any builtin mecanism to prevent this ?
(I'm not talking about snippet for using sqlite / redis, but builtin solution)
This is not a failure of the security of Flask's cookies, it's a failure of your counter design. There is no built in protection against replay attacks.
You can shorten the expiration time of the session cookie. This doesn't really solve the problem, it just makes the window smaller. It also makes the session inconvenient for regular use, which would annoy your normal users.
Ultimately, you'll have to store some information on the server and check against it. You could send a nonce with every request and keep a store of which ones have been sent back, ignoring ones that have been seen before. You could also just store all session information (except some identifying key) on the server side, so it can't be re-sent.
I'm taking my first foray into the Pyramid security module. I'm using this login code to set the auth_tkt:
#view_config(route_name='LoginForm', request_method='POST', renderer='string')
class LoginForm(SimpleObject):
def __call__(self):
emailAddress = self.request.params.get('emailAddress')
password = self.request.params.get('password')
if emailAddress != 'testemail#gmail.com' or password != 'testpassword':
errorDictionary = { 'message' : "Either the email address or password is wrong." }
self.request.response.status = 400
return json.dumps( errorDictionary, default=json_util.default)
testUserGUID = '123123123'
headers = remember(self.request, testUserGUID)
return HTTPOk(headers=headers)
It seems to work ok, but there are some puzzling details:
First of all, 2 cookies actually get set instead of one. The 2 cookies are identical (both with name "auth_tkt") except for one difference: one has a host value of ".www.mydomain.com" while the other cookie has a host value of "www.mydomain.com" Why are 2 cookies being set instead of one? What's the significance of the difference host values?
Question 2, web tools reports that neither cookie is secure. What can I do to make sure the cookie/s are secure?
Question 3: Both cookies have an expiration value of "At end of session". What does this mean and how can I customize the expiration value myself? What's the recommended practice for login cookie expiration times?
Question 4: I don't understand why the first argument of "remember" is self.request instead of self.request.response. Shouldn't the data be remembered on the response object, not the request object?
Actually, 3 cookies are generated; one without a Domain key, one with, and a 3rd with the wildcard version of your domain (the leading dot). Your browser usually either merges the two or ignores one of those (which one differs by browser, which is why 2 are set).
That last cookie is generated when the wild_domain option is set on the AuthTktAuthenticationPolicy (True by default); see the AuthTktAuthenticationPolicy API. You need this if your authentication cookie is to be shared between different subdomains (think app1.domain, app2.domain); your browser won't share cookies across subdomains without a wildcard cookie.
You need to set the secure option on your auth policy for cookies to get the secure flag set. Again, see the API.
No expiration is set, which means that the cookies are deleted when you close your browser (the end of the session your browser shows you). If you want your users to be logged out when they close the browser, leave this as the default.
Only if you want sessions to last across browser closures, set a cookie maximum age, see the max_age option in the API. This option will cause browsers to store the cookie on disk to persist between browser closures, and delete them when the maximum age has passed.
Do note that the AuthTktAuthenticationPolicy policy object can manage login sessions in a more fine-grained manner by limiting how long it'll consider any authentication cookie valid, and will let you set up a cookie refresh policy. With such a refresh policy in place users will receive new (refreshed) cookies as they continuing to use your application, but if they don't connect to your server within a set period of time, their cookie would be considered invalid and they would have to log in again.
See the timeout and reissue_time options in the API documentation for more detail on how to configure this.
The policy object requires several pieces of information from the request to be able to generate the cookies, not least of all the host name of your server.
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.
Our Django application has the following session management requirements.
Sessions expire when the user closes the browser.
Sessions expire after a period of inactivity.
Detect when a session expires due to inactivity and display appropriate message to the user.
Warn users of a impending session expiry a few minutes before the end of the inactivity period. Along with the warning, provide users an option to extend their session.
If user is working on a long business activity within the app that doesn't involve requests being sent to the server, the session must not timeout.
After reading the documentation, Django code and some blog posts related to this, I have come up with the following implementation approach.
Requirement 1
This requirement is easily implemented by setting SESSION_EXPIRE_AT_BROWSER_CLOSE to True.
Requirement 2
I have seen a few recommendations to use SESSION_COOKIE_AGE to set the session expiry period. But this method has the following problems.
The session always expires at the end of the SESSION_COOKIE_AGE even if the user is actively using the application. (This can be prevented by setting the session expiry to SESSION_COOKIE_AGE on every request using a custom middleware or by saving the session on every request by setting SESSION_SAVE_EVERY_REQUEST to true. But the next problem is unavoidable due to the use of SESSION_COOKIE_AGE.)
Due to the way cookies work, SESSION_EXPIRE_AT_BROWSER_CLOSE and SESSION_COOKIE_AGE are mutually exclusive i.e. the cookie either expires on browser close or at the specified expiry time. If SESSION_COOKIE_AGE is used and the user closes the browser before the cookie expires, the cookie is retained and reopening the browser will allow the user (or anyone else) into the system without being re-authenticated.
Django relies only on the cookie being present to determine if the session is active. It doesn't check the session expiry date stored with the session.
The following method could be used to implemented this requirement and to workaround the problems mentioned above.
Do not set SESSION_COOKIE_AGE.
Set the expiry date of the session to be 'current time + inactivity period' on every request.
Override process_request in SessionMiddleware and check for session expiry. Discard the session if it has expired.
Requirement 3
When we detect that the session has expired (in the custom SessionMiddleware above), set an attribute on the request to indicate session expiry. This attribute can be used to display an appropriate message to the user.
Requirement 4
Use JavaScript to detect user inactivity, provide the warning and also an option to extend the session. If the user wishes to extend, send a keep alive pulse to the server to extend the session.
Requirement 5
Use JavaScript to detect user activity (during the long business operation) and send keep alive pulses to the server to prevent session from expiring.
The above implementation approach seem very elaborate and I was wondering if there might a simpler method (especially for Requirement 2).
Any insights will be highly appreciated.
I am just pretty new to use Django.
I wanted to make session expire if logged user close browser or are in idle(inactivity timeout) for some amount of time. When I googled it to figure out, this SOF question came up first. Thanks to nice answer, I looked up resources to understand how middlewares works during request/response cycle in Django. It was very helpful.
I was about to apply custom middleware into my code following top answer in here. But I was still little bit suspicious because best answer in here was edited in 2011. I took more time to search little bit from recent search result and came up with simple way.
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_AGE = 10 # set just 10 seconds to test
SESSION_SAVE_EVERY_REQUEST = True
I didn't check other browsers but chrome.
A session expired when I closed a browser even if SESSION_COOKIE_AGE set.
Only when I was idle for more than 10 seconds, A session expired. Thanks to SESSION_SAVE_EVERY_REQUEST, whenever you occur new request, It saves the session and updates timeout to expire
To change this default behavior, set the SESSION_SAVE_EVERY_REQUEST setting to True. When set to True, Django will save the session to the database on every single request.
Note that the session cookie is only sent when a session has been created or modified. If SESSION_SAVE_EVERY_REQUEST is True, the session cookie will be sent on every request.
Similarly, the expires part of a session cookie is updated each time the session cookie is sent.
django manual 1.10
I just leave answer so that some people who is a kind of new in Django like me don't spend much time to find out solution as a way I did.
Here's an idea... Expire the session on browser close with the SESSION_EXPIRE_AT_BROWSER_CLOSE setting. Then set a timestamp in the session on every request like so.
request.session['last_activity'] = datetime.now()
and add a middleware to detect if the session is expired. something like this should handle the whole process...
from datetime import datetime
from django.http import HttpResponseRedirect
class SessionExpiredMiddleware:
def process_request(request):
last_activity = request.session['last_activity']
now = datetime.now()
if (now - last_activity).minutes > 10:
# Do logout / expire session
# and then...
return HttpResponseRedirect("LOGIN_PAGE_URL")
if not request.is_ajax():
# don't set this for ajax requests or else your
# expired session checks will keep the session from
# expiring :)
request.session['last_activity'] = now
Then you just have to make some urls and views to return relevant data to the ajax calls regarding the session expiry.
when the user opts to "renew" the session, so to speak, all you have to do is set requeset.session['last_activity'] to the current time again
Obviously this code is only a start... but it should get you on the right path
django-session-security does just that...
... with an additional requirement: if the server doesn't respond or an attacker disconnected the internet connection: it should expire anyway.
Disclamer: I maintain this app. But I've been watching this thread for a very, very long time :)
One easy way to satisfy your second requirement would be to set SESSION_COOKIE_AGE value in settings.py to a suitable amount of seconds. For instance:
SESSION_COOKIE_AGE = 600 #10 minutes.
However, by only doing this the session will expire after 10 minutes whether or not the user exhibits some activity. To deal with this issue, expiration time can be automatically renewed (for another extra 10 minutes) every time the user performs any kind of request with the following sentence:
request.session.set_expiry(request.session.get_expiry_age())
also you can use
stackoverflow build in functions
SESSION_SAVE_EVERY_REQUEST = True
In the first request, you can set the session expiry as
self.request.session['access_key'] = access_key
self.request.session['access_token'] = access_token
self.request.session.set_expiry(set_age) #in seconds
And when using the access_key and token,
try:
key = self.request.session['access_key']
except KeyError:
age = self.request.session.get_expiry_age()
if age > set_age:
#redirect to login page
I'm using Django 3.2 and i recommend using the django-auto-logout package.
It allows active time and idle time session control.
In the template you can use variables together with Javascript.