Allowing an anonymous person to post a value - django

An anonymous user (no login) gets directed to a landing page where there is a button and text field to post information.
I want the act posting of data to be tied to the person landing on the page. ie knowing the target url of the post shouldn't allow you to post stuff, it should be tied to a very short duration session.
I am using Django.
What is the simplest or built in method to use?

Sessions
You can store this information in the anonymous user's session if you have a session store configured. To start the session:
request.session["allow_post_until"] = datetime.datetime.now() + datetime.timedelta(...)
And to check it:
if not (request.session["allow_post_until"] and request.session["allow_post_until"] < datetime.datetime.now()):
raise PermissionDenied
Signed Cookies
If you are using django 1.4 and don't want to configure a session store you can use signed cookies for this. When you want to enable a session for the user, set a cookie with an appropriate max_age. When a user posts, check for the signed cookie and check its validity. To set:
response.set_signed_cookie("mysession", "sessiondata", max_age=<session period in seconds>)
To check:
request.get_signed_cookie("mysession", max_age=<session period in seconds>)

Related

Revoking tokens using Django rest-framework-jwt

I'm thinking of allowing a user to revoke previously issued tokens (yes, even though they are set to expire in 15 minutes), but did not find any way to do so using DRF-jwt.
Right now, I'm considering several options:
Hope someone on SO will show me how to do this out-of-the-box ;-)
Use the jti field as a counter, and, upon revocation, require jti > last jti.
Add user-level salt to the signing procedure, and change it upon revocation
Store live tokens in some Redis DB
Is any of the above the way to go?
We did it this way in our project:
Add jwt_issue_dt to User model.
Add original_iat to payload. So token refresh won't modify this field.
Compare original_iat from payload and user.jwt_issue_dt:
from calendar import timegm
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class CustomJSONWebTokenAuthentication(JSONWebTokenAuthentication):
def authenticate_credentials(self, payload):
user = super(CustomJSONWebTokenAuthentication, self).authenticate_credentials(payload)
iat_timestamp = timegm(user.jwt_issue_dt.utctimetuple())
if iat_timestamp != payload['iat']:
raise exceptions.AuthenticationFailed('Invalid payload')
return user
To revoke a token you just need to update the field user.jwt_issue_dt.

How to set and get sessionid from Django without web browser

I'm using Django 1.6.
I want to set session id.
When I use web browser, I can watch "sessionid" logs.
But, when I used other application like Unity3d,
I can't watch "sessionid" logs.
Could you tell me how to get a session id?
I've create the view
But, I'm accessing application like unity
it returns.
def get_token(request):
# request_cookies = OrderedDict(request.COOKIES)
context = {}
context.update(csrf(request))
context['csrf_token'] = force_text(context['csrf_token'])
session_key = request.session.session_key
context['sessionid'] = session_key
return HttpResponse(json.dumps(context), content_type="application/json")
But, session id is null. however web broweser is okey.
jsonText: {"csrf_token": "xma9Dtmv46va5uomZiZ9dcjjfCB9E2Gw", "sessionid": null}
Sessions are set using cookies. Cookies are implemented by the vast majority of browser but not by other http clients.
Since you mention Unity, I guess what you are building with Django is an api. I suggest you take a look at http://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication as an alternative to relying on cookies.

How to clear all session variables without getting logged out

I am trying to clear all of the session variables but not logout the current user.
user = request.session.get('member_id', None)
request.session.flush()
request.session.modified = True
request.session['member_id'] = user
request.session.modified = True
Will this also affect other users of the site?
As of Django 1.8, any call to flush() will log out the user. From the docs:
Changed in Django 1.8: Deletion of the session cookie is a behavior
new in Django 1.8. Previously, the behavior was to regenerate the
session key value that was sent back to the user in the cookie.
If you want to be able to delete keys but keep the user logged in, you'll need to handle it manually:
for key in request.session.keys():
del request.session[key]
Or just delete the specific keys that are of concern:
del request.session['mykey']
In versions of django < 1.8, session.flush deletes the session data and regenerates the session key. It won't affect other users since session keys are unique.
As an improvement to shacker's1 in Python 2.x dict.keys() returns a list copy of the keys of a dictionary, in Python 3.x it instead returns an iterator. changing the size of an iterator is unwise. For an version safe implementation casting to list will prevent any size issues
for key in list(request.session.keys()):
del request.session[key]
My previous answer suggested the use of dict.viewkeys() but it will also return an iterator in python 3.x.
You can clear keys you have set in the django session, but to do so without logging the user out takes a little bit of trickiness; request.session.flush() logs the user out. And request.session = {} in deleting all keys in the session dictionary will also log the user out.
Thus, to clear out keys without logging the user out, you have to avoid keys that begin with an underscore character. The following code does the trick:
for key in list(request.session.keys()):
if not key.startswith("_"): # skip keys set by the django system
del request.session[key]
request.session internally uses cookies. And when a user requests some url of the site, only cookies present on that user's machine is sent to the server. So, request.session is always tied to the current user.
So, this in no way will affect other users of the site.
Also this will not log out the current user, because you are using flush() which will delete the old session and create a new session and this new session would be associated with the current user.
flush() internally uses clear(), delete() and create().
In the response this new session's key would be sent as a cookie and in subsequent requests this new session would continue working normally.
session_keys = list(request.session.keys())
for key in session_keys:
del request.session[key]

django: csrftoken COOKIE vs. csrfmiddlewaretoken HTML Form value

Trying to learn about security. Curious about why in django when
submitting a form (a POST), there are 2 separate "elements" that
contain the same csrf token value:
- the csrftoken cookie:
COOKIES:{'csrftoken': '1effe96056e91a8f58461ad56c0d4ddc', ...
- the Form's hidden csrfmiddlewaretoken:
POST:<QueryDict: {u'csrfmiddlewaretoken':
[u'1effe96056e91a8f58461ad56c0d4ddc'], ...
If django is inserting the hidden csrf field/value to
the form when it sends it to the browser (GET), and expects the
same value back when receiving the POST, then why is it
necessary to also set a cookie?
A more general question, if either of them was missing (form, cookie),
could you provide a scenario that explains how this could be exploited
(security attack)?
By the way, I ran a couple of simple tests to make sure that
django was checking the validity of each one separately and
indeed it is:
if I change the form's csrf value before doing the POST,
I get this debug error back:
CSRF token missing or incorrect
if I delete the csrf cookie before doing the POST,
I get a different error back:
CSRF cookie not set.
I'm just familiar with basic csrf concepts and want to
learn how django helps protect against these types of attacks.
Thanks,
jd
update:
Although both answers (S.Lott and M. DeSimone) were informative and
make sense, I thought that there could be a more detailed explanation
for requiring the presence of the security value in both the form and
in the cookie. While searching outside stackoverflow.com, I came across
a blog post from...Jeff Atwood.
I have included a third answer (sorry to answer my own question but
I think that it is relevant supplemental info) that refers to a blog
post from Jeff and includes a quotation.
From Jeff Atwood's blog entry:
Preventing CSRF and XSRF Attacks
(Oct 14, 2008)
The original post
The Felten and Zeller paper (pdf) recommends the "double-submitted
cookie" method to prevent XSRF:
When a user visits a site, the site should generate a
(cryptographically strong) pseudorandom value and set it as a
cookie on the user's machine. The site should require every form
submission to include this pseudorandom value as a form value and
also as a cookie value. When a POST request is sent to the site,
the request should only be considered valid if the form value and
the cookie value are the same. When an attacker submits a form on
behalf of a user, he can only modify the values of the form. An
attacker cannot read any data sent from the server or modify cookie
values, per the same-origin policy. This means that while an
attacker can send any value he wants with the form, he will be
unable to modify or read the value stored in the cookie. Since the
cookie value and the form value must be the same, the attacker will
be unable to successfully submit a form unless he is able to guess
the pseudorandom value.
The advantage of this approach is that it requires no server state;
you simply set the cookie value once, then every HTTP POST checks to
ensure that one of the submitted values contains the exact
same cookie value. Any difference between the two means a possible
XSRF attack.
The cookie is there for AJAX support. Quoting the Django docs:
While the above method can be used for AJAX POST requests, it has some inconveniences: you have to remember to pass the CSRF token in as POST data with every POST request. For this reason, there is an alternative method: on each XMLHttpRequest, set a custom X-CSRFToken header to the value of the CSRF token. This is often easier, because many javascript frameworks provide hooks that allow headers to be set on every request. In jQuery, you can use the ajaxSend event as follows:
$('html').ajaxSend(function(event, xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
});
Adding this to a javascript file that is included on your site will ensure that AJAX POST requests that are made via jQuery will not be caught by the CSRF protection.
They spot two different problems.
Cookie is to authenticate the client machine making the connection.
The hidden form field is to authenticate the source of the form.
Example Scenario: User A, on the client machine could bookmark the form. User B logs on, gets a valid cookie from today. User A could submit the invalid form field from yesterday when the browser has a left-over cookie from user B's session.
what client/browser resources are typically compromised,
None.
and how is it that these csrf fields help protect us from the forgery requests?
The CSRF tokens establish identity.
One (and only one) browser has a CSRF cookie token. But that browser could have multiple copies of a site open or bookmarked forms.
One (and only one) page form on that browser has a CSRF form token.
The browser and form cookies must match to assure one browser/one form.

Flash + pyAMF + Django session cookie security

First off, if there is a true, official way of having flash/flex's NetConnections usurp the session/cookie state of the surrounding web page, so that if the user has already logged in, they don't need to provide credentials again just to set up an AMF connection, please stop me now and post the official answer.
Barring that, I'm assuming there is not, as I have searched and it seems to not exist. I've concocted a means of doing this, but want some feedback as to whether it is secure.
Accessing a wrapper-page for a flash object will always go to secure https due to django middleware
When the page view is loaded in Django, it creates a "session alias" object with a unique key that points to the current session in play (in which someone ostensibly logged in)
That session alias model is saved, and that key is placed into a cookie whose key is another random string, call it randomcookie
That randomcookie key name is passed as a context variable and written into the html as a flashvar to the swf
The swf is also loaded only via https
The flash application uses ExternalInterface to call java to grab the value at that randomcookie location, and also deletes the cookie
It then creates a NetConnection to a secure server https location, passing that randomcookie as an argument (data, not in the url) to a login-using-cookie rpc
At the gateway side, pyamf looks up the session alias and gets the session it points to, and logs in the user based on that (and deletes the alias, so it can't be reused)
(And the gateway request could also set the session cookie and session.session_key to the known session ID, but I could let it make a whole new session key... I'm assuming that doing so should affect the response properly so that it contains the correct session key)
At this point, the returned cookie values on the flash side should stick to the NetConnection so that further calls are authenticated (if a connection is authenticated using username and password the normal way, this definitely works, so I think this is a safe bet, testing will soon prove or disprove this)
So, is this unsafe, or will this work properly? As far as I know, since the html page is guaranteed to be over ssl, the key and cookie data should be encrypted and not steal-able. Then, the info therein should be safe to use one-time as basically a temporary password, sent again over ssl because the gateway is also https. After that, it's using the normal pyAMF system over https and not doing anything out of the ordinary.
No responses on this so far, so the best I can do is confirm that it does in fact physically work. For details on how to set up Flex Builder to write html-wrappers that communicate with Django pages templates, see my other post. The above was accomplished using a combination of the aforementioned, plus:
Made a SessionAlias model:
class SessionAlias(models.Model):
alias = models.CharField( max_length=40, primary_key=True )
session = models.ForeignKey( Session )
created = models.DateTimeField( auto_now_add=True )
Flex points to a Django page that loads via a view containing:
s = SessionAlias()
s.alias = SessionStore().session_key // generates new 40-char random
s.session = Session.objects.get( session_key=request.session.session_key )
s.save();
randomcookie = SessionStore().session_key // generates new 40-char random
kwargs['extra_context']['randomcookie'] = randomcookie
response = direct_to_template( request, **kwargs )
response.set_cookie( randomcookie, value=alias )
In the flex html-wrapper, where randomcookie is the location to look for the alias:
<param name="flashVars" value="randomcookie={{randomcookie}}" />
In applicationComplete, where we get randomcookie and find the alias, and log on using that:
var randomcookie:String = this.parameters["randomcookie"];
// randomcookie is something like "abc123"
var js:String = "function get_cookie(){return document.cookie;}";
var cookies:String = ExternalInterface.call(js).toString();
// cookies looks like "abc123=def456; sessionid=ghi789; ..."
var alias:String = // strip out the "def456"
mynetconnection.call( "loginByAlias", alias, successFunc, failureFunc );
Which in turn access this pyamf gateway rpc:
from django.contrib.auth import SESSION_KEY, load_backend
from django.contrib.auth.models import User
from django.contrib import auth
from django.conf import settings
def loginByAlias( request, alias ):
a = SessionAlias.objects.get( alias=alias )
session_engine = __import__( settings.SESSION_ENGINE, {}, {}, [''] )
session_wrapper = session_engine.SessionStore( a.session.session_key )
user_id = session_wrapper.get( SESSION_KEY )
user = User.objects.get( id=user_id )
user.backend='django.contrib.auth.backends.ModelBackend'
auth.login( request, user )
a.delete()
return whateverToFlash
And at that point, on the flash/flex side, that particular mynetconnection retains the session cookie state that can make future calls such that, inside the gateway, request.user is the properly-authenticated user that logged onto the webpage in the first place.
Note again that the run/debug settings for flex must use https, as well as the gateway settings for NetConnection. And when releasing this, I have to make sure that authenticated users stay on https.
Any further info from people would be appreciated, especially if there's real feedback on the security aspects of this...
IE doesn't give access to cookies in local development but if you publish the SWF and put on a domain, it should pickup the session just like ever other browser. Use Firefox 3.6 to build your flex apps locally.
Tested in IE8, Firefox using a pyamf gateway on Flex 3 with NetConnection. The gateway function was decorated with #login_required