How to set cookie in Python Flask? - python

In this way, I want to set my cookie. But it fails to set.
#app.route('/')
def index():
res = flask.make_response()
res.set_cookie("name", value="I am cookie")
When I print res it shows <Response 0 bytes [200 OK] But not set cookie

You have to return the response after setting the cookie.
#app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('somecookiename', 'I am cookie')
return resp
This way a cookie will be generated in your browser, but you can get this cookie in the next request.
#app.route('/get-cookie/')
def get_cookie():
username = request.cookies.get('somecookiename')

The cookie you set will be visible if you either use other tools to see it (ex: Press F12 for Developer Tools in Firefox or Chrome) or use a piece of code of JavaScript inside the rendered response.
The cookies are set on the browser by either browser itself (JavaScript) or as a response from a server.
The difference is of great importance as even if the cookie is set by the server the cookie might not be set on the browser (ex: cases where cookie are completely disabled or discarded).
So even if the server might tell "I set up the cookie" - the cookie might not be present on the browser.
For the server to be sure that the cookie was set a subsequent request from the browser is needed (with the cookie present in the request's headers).
So even if the Flask's response (res variable) will mention that the cookie is set we can only be sure that it was set by the server but it will have no confirmation about it from the browser.
Advanced
Another aspect is about how Flask or any other API is creating the responses. As the payload (html/template code) and headers (cookie) are set at same time - the "code" inside the payload (html/template code) might not have access to the cookie.
So you might not be able to set a cookie and display it in the response.
An idea might be to (be able to) first set the cookies and THEN to render the context and the order of setup to be important - so that the html/template to be aware of already setup values. But even in this case is only the server's confirmation that it set up the cookie.
A solution
#app.route('/')
def index():
res = flask.make_response()
res.set_cookie("name", value="I am cookie")
# redirect to a page that display the cookie
resp.headers['location'] = url_for('showcookies')
return resp, 302

This response will set cookie in you browser
def func():
response = make_response( render_template() )
response.set_cookie( "name", "value" )
return response

Related

CSRF token not set in Django with Python Request

I'm trying to send a POST request to a Django view from an ordinary Python script using Python-Request. The django view is not #login_required, so the only thing i need to send, other than my JSON data, is a CSRF token, here is what i tried:
token = session.get('http://127.0.0.1:8000/myview/view')
data = json.dumps({'test': 'value'})
session.post('http://127.0.0.1:8000/myview/myview',
data={
'csrfmiddlewaretoken': token,
'data': data})
The django view should just receive the Request and print it to my console:
def myview(request):
if request.method == 'POST':
data = request.POST.get('data')
print(json.loads(data))
print('received.')
response = HttpResponse(get_token(request))
return response
The problem with my current code is that my console will throw a log: WARNING - Forbidden (CSRF token missing or incorrect.). I cannot use #csrf_exempt, since i need this to be as safe as possible. Any advice? Thanks in advance!
Why might a user encounter a CSRF validation failure after logging in?
For security reasons, CSRF tokens are rotated each time a user logs in. Any page
with a form generated before a login will have an old, invalid CSRF token and need to be reloaded. This might happen if a user uses the back button after a login or if they log in a different browser tab.
This also goes for cookies. After you log in, django will send a new csrf cookie to the client. This will be stored in client.cookies and replaces the old one. The django server does not keep any record of the old token, so that's why you get the "CSRF token missing or incorrect." response.
You can access the new token from request.cookies['csrftoken'] as before.
import requests
LOGIN_URL = 'http://127.0.0.1:8000/myview/view'
request = requests.session()
request.get(LOGIN_URL)
# Retrieve the CSRF token first
csrftoken = request.cookies['csrftoken']
r1 = request.post(LOGIN_URL, headers={'X-CSRFToken': csrftoken},
allow_redirects=False))
new_csrftoken = r1.cookies['csrftoken']
data = json.dumps({'test': 'value'})
payload = {'csrfmiddlewaretoken': new_csrftoken,'data':data }
In fact, you can just use the client cookie directly. This would have avoided this bug in the first place. Requests keeps track of cookies for you when you use requests.session().
try :
r2 = request.post('http://127.0.0.1:8000/myview/myview', data=payload, headers={'X-CSRFToken': r1.cookies['crsftoken']})
except :
print('error expected')

Is there a way to set a cookie in #before.request?

I want my application to be able to detect the user's language to serve the corresponding page to them. My idea was to use #before.request to read the Accepted-Languages header, match it against the app's supported languages, and set a cookie if needed, but it seems that the last step is not achievable.
Here is the code for that:
#app.before_request
def before_request_callback():
if request.cookies.get('lang'):
pass
else:
lang = request.accept_languages.best_match(supported_languages)
print(lang)
#I would like to set the cookie here
I thought about setting the cookie in the response object directly in the decorator (resp.set_cookie()) and thus reformatted all the returns in my app to look like this
#app.route("/")
def hello():
resp = make_response(render_template('index.html'))
return resp
to maybe be able to fetch the cookie and attach it to this response, but since the response is created afterwards in the endpoitn function, i have no idea how to do that either.
I also thought about creating the response directly in the decorator, but since i need the return condition, i don't know if thats possible
I think you're trying to use a cookie when you don't need to. As you noted, you can only set cookies on the response. You send the browser cookies in the response and then it re-sends them to you in any subsequent request. But it's already sending you the Accept-Language. So there's no point in setting a cookie on the request. It's already arrived, and already contains what you need.
Instead of getting hung up on setting cookies, just consult the request at the point that you're generating the response to ensure that the served content is in the appropriate language.
from flask import request
#app.route("/")
def hello():
lang = request.accept_languages.best_match(supported_languages)
return render_template(f'{lang}/index.html')

How to set a cookie on a redirect in python

I am writing a Proxy Server where I will intercept a request, and check the header to see if it has a cookie named URID, and if it does not, I want to redirect the request to a fw.asta.com, see if this domain has a cookie called URID, and if it doesn't, I want to set the cookie for fw.cisco.com, and then redirect to the originally intended site, and then set the same cookie for that site as well. If the cookie exists for the fw.asta.com site, I will use that cookie to redirect to the originally intended site, and if the originally intended site has the URID cookie, I will simply serve the request on behalf of the site.
So I have been struggling with this for quite some time. I have a feeling like I don't understand what actually happens when I set a cookie during a redirection.
I have this code right here:
def do_GET(self):
#seperate the path into parts
(scm, netloc, path, params, query, fragment) = urlparse.urlparse(self.path, 'http')
#check the header for the urid cookie, returns the URID if present
urid = self.find_urid()
if netloc == 'fw.asta.com':
if urid:
#redirect to the originally intended site and set the cookie
path = self.path[23:]
self.redirect_with_urid(path, urid)
else:
#redirect to fw.asta.com
urid = 333
self.redirect_with_urid(self.path, urid)
else:
if urid:
#simply serve the site because if has the URID
self.final_destination()
else:
#no urid, so we need to go the fw.asta.com to check if the urid is there
self.redirect('http://fw.asta.com/?url=' + str(self.path))
As you can see the logic makes sense, however when I run the code, I run into an infinite redirect loop to the originally intended site.
When I get the redirect response, I can clearly see that the set-cookie field is there, however, when I get the redirected request, it does not send the cookie back to me.
Is this even the right way to go about the problem? Any advice would be appreciated.

How to set 'secure' and 'httponly' cookie in Tornado?

I have a Tornado app which uses Google Oauth 2.0 Authentication, gets the email and sets that in a cookie. Now I don't want anyone else to access this cookie, copy the value and get another user's details on my app. So I want to make this cookie httponly and secure cookie. However when I pass these as arguments its not able to set the cookie:
self.set_secure_cookie('trakr', email, secure=True, httponly=True)
I am suing Tornado 3.2.2 and Python 2.7.5.
since its not able to set the cookie, it keeps redirecting to google auth page. Here is my code:
class GAuthLoginHandler(BaseHandler, tornado.auth.GoogleOAuth2Mixin):
#tornado.gen.coroutine
def get(self):
if self.get_current_user():
self.redirect('/products')
return
if self.get_argument('code', False):
user = yield self.get_authenticated_user(redirect_uri=settings.google_redirect_url,
code=self.get_argument('code'))
if not user:
self.clear_all_cookies()
raise tornado.web.HTTPError(500, 'Google authentication failed')
access_token = str(user['access_token'])
http_client = self.get_auth_http_client()
response = yield http_client.fetch('https://www.googleapis.com/oauth2/v1/userinfo?access_token='+access_token)
user = json.loads(response.body)
self.set_secure_cookie('trakr', user['email'], secure=True, httponly=True)
self.redirect(self.get_argument("next", "/products"))
return
elif self.get_secure_cookie('trakr'):
self.redirect('/products')
return
else:
yield self.authorize_redirect(
redirect_uri=settings.google_redirect_url,
client_id=self.settings['google_oauth']['key'],
scope=['email'],
response_type='code',
extra_params={'approval_prompt': 'auto'})
The code works perfectly fine when I remove secure and httponly arguments. It also works if I just send httponly argument, however it doesn't seem to set the cookie when I pass both the arguments.
Am I doing something wrong?
The issue is not with Tornado or Python, but with my server as I was not using HTTPS:
A secure cookie has the secure attribute enabled and is only used via
HTTPS, ensuring that the cookie is always encrypted when transmitting
from client to server. This makes the cookie less likely to be exposed
to cookie theft via eavesdropping. In addition to that, all cookies
are subject to browser's same-origin policy.

Unit testing authorization in a Pylons app fails; cookies aren't been correctly set or recorded

I'm having an issue running unit tests for authorization in a Pylons app. It appears as though certain cookies set in the test case may not be correctly written or parsed. Cookies work fine when hitting the app with a browser.
Here is my test case inside a paste-generated TestController:
def test_good_login(self):
r = self.app.post('/dologin', params={'login': self.user['username'], 'password': self.password})
r = r.follow() # Should only be one redirect to root
assert 'http://localhost/' == r.request.url
assert 'Dashboard' in r
This is supposed to test that a login of an existing account forwards the user to the dashboard page. Instead, what happens is that the user is redirected back to the login. The first POST works, sets the user in the session and returns cookies. Although those cookies are sent in the follow request, they don't seem to be correctly parsed.
I start by setting a breakpoint at the beginning of the above method and see what the login response returns:
> nosetests --pdb --pdb-failure -s foo.tests.functional.test_account:TestMainController.test_good_login
Running setup_config() from foo.websetup
> /Users/istevens/dev/foo/foo/tests/functional/test_account.py(33)test_good_login()
-> r = self.app.post('/dologin', params={'login': self.user['username'], 'password': self.password})
(Pdb) n
> /Users/istevens/dev/foo/foo/tests/functional/test_account.py(34)test_good_login()
-> r = r.follow() # Should only be one redirect to root
(Pdb) p r.cookies_set
{'auth_tkt': '"4c898eb72f7ad38551eb11e1936303374bd871934bd871833d19ad8a79000000!"'}
(Pdb) p r.request.environ['REMOTE_USER']
'4bd871833d19ad8a79000000'
(Pdb) p r.headers['Location']
'http://localhost/?__logins=0'
A session appears to be created and a cookie sent back. The browser is redirected to the root, not the login, which also indicates a successful login. If I step past the follow(), I get:
> /Users/istevens/dev/foo/foo/tests/functional/test_account.py(35)test_good_login()
-> assert 'http://localhost/' == r.request.url
(Pdb) p r.request.headers
{'Host': 'localhost:80', 'Cookie': 'auth_tkt=""\\"4c898eb72f7ad38551eb11e1936303374bd871934bd871833d19ad8a79000000!\\"""; '}
(Pdb) p r.request.environ['REMOTE_USER']
*** KeyError: KeyError('REMOTE_USER',)
(Pdb) p r.request.environ['HTTP_COOKIE']
'auth_tkt=""\\"4c898eb72f7ad38551eb11e1936303374bd871934bd871833d19ad8a79000000!\\"""; '
(Pdb) p r.request.cookies
{'auth_tkt': ''}
(Pdb) p r
<302 Found text/html location: http://localhost/login?__logins=1&came_from=http%3A%2F%2Flocalhost%2F body='302 Found...y. '/149>
This indicates to me that the cookie was passed in on the request, although with dubious escaping. The environ appears to be without the session created on the prior request. The cookie has been copied to the environ from the headers, but the cookies in the request seems incorrectly set. Lastly, the user is redirected to the login page, indicating that the user isn't logged in.
Authorization in the app is done via repoze.who and repoze.who.plugins.ldap with repoze.who_friendlyform performing the challenge. I'm using the stock tests.TestController created by paste:
class TestController(TestCase):
def __init__(self, *args, **kwargs):
if pylons.test.pylonsapp:
wsgiapp = pylons.test.pylonsapp
else:
wsgiapp = loadapp('config:%s' % config['__file__'])
self.app = TestApp(wsgiapp)
url._push_object(URLGenerator(config['routes.map'], environ))
TestCase.__init__(self, *args, **kwargs)
That's a webtest.TestApp, by the way.
The encoding of the cookie is done in webtest.TestApp using Cookie:
>>> from Cookie import _quote
>>> _quote('"84533cf9f661f97239208fb844a09a6d4bd8552d4bd8550c3d19ad8339000000!"')
'"\\"84533cf9f661f97239208fb844a09a6d4bd8552d4bd8550c3d19ad8339000000!\\""'
I trust that that's correct.
My guess is that something on the response side is incorrectly parsing the cookie data into cookies in the server-side request. But what? Any ideas?
This issue disappeared after downgrading WebTest from 1.2.1 to 1.2.
The issue continually appeared for me regardless of the version of WebTest. However, after much mucking around I noticed that when the cookie was first set it was using 127.0.0.1 as the REMOTE_ADDR value but on the second request it changed to 0.0.0.0.
If I did the get request and set the REMOTE_ADDR to 127.0.0.1 all was well!
response = response.goto(url('home'), extra_environ=dict(REMOTE_ADDR='127.0.0.1'))

Categories