How do I reach cookie information in python requests? - python

I am trying to write a small script that will allow me to see information related to the cookies set by my website.
I want to know if it has secure or httpOnly flags set on them. But so far I wasn't able to do it, I only figured out how to get cookie names and values. Here is my current code:
r = requests.post('url', data=data, headers=headers)
for (name, cookie) in r.cookies.items():
print name, cookie
So far this works fine, but I want to get information related to the cookies, not the value itself. Cookie meta-data if you will.
How can I achieve that?

You can extract the information from each cookie individually:
import requests
r = requests.post('http://www.about.com')
for cookie in r.cookies:
print(cookie.__dict__)
print(cookie.secure)
This is because r.cookies is an instance of RequestsCookieJar which extends from CookieJar (Python 2: cookielib.CookieJar, Python 3: http.cookiejar.CookieJar). A CookieJar has Cookie objects.
References:
cookielib: https://docs.python.org/2.7/library/cookielib.html
cookielib.Cookie.secure: https://docs.python.org/2.7/library/cookielib.html#cookielib.Cookie.secure
https://stackoverflow.com/a/27523891/295246
Update:
I have not found a way to retrieve the httponly value from a Cookie object. In Python 3, you can define a Morsel object via a dictionary, and it considers httponly to be a standard attribute of a cookie (https://docs.python.org/3/library/http.cookies.html), but I couldn't find any reference to httponly in the defining specification RFC2109 (https://www.ietf.org/rfc/rfc2109.txt).
That said, if httponly is in fact a non-standard attribute, then you can use the following to check if a cookie has it:
cookie.has_nonstandard_attr('httponly')

Under Python 3, I was not able to retrieve the httpOnly flag from the following:
cookie.get_nonstandard_attr('httpOnly')
and
cookie.has_nonstandard_attr('httpOnly')
returned False even if the httpOnly flag was included with the cookie.
This didn't work with any of the variations of httponly, HttpOnly, etc. either.
Using #HEADLESS_0NE's post, I found you can retrieve the flag by looking at the _rest field in cookie.__dict__. If httpOnly is included in the cookie,
cookie.__dict__['_rest']
will return something like this:
{'HttpOnly': None, ...}
Thus, here is a small helper function to check if a cookie has the httpOnly flag.
def has_http_only(cookie):
extra_args = cookie.__dict__.get('_rest')
if extra_args:
for key in extra_args.keys():
if key.lower() == 'httponly':
return True
return False
The secure flag is automatically added to the cookie object and can be retrieved using cookie.secure.

Related

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')

Flask/Eve + WSGI and HTTP_X_HTTP_METHOD_OVERRIDE

I am trying to understand how and when the WSGI environment HTTP Header(s) get renamed in an app's request object.
I am trying Eve and I am sending a POST or a PUT with X-HTTP-Method-Override.
The code, within Eve, is trying to access the request headers using the following code (here):
return request.headers.get('X-HTTP-Method-Override', request.method)
In my WSGI Environment I have a HTTP_X_HTTP_METHOD_OVERRIDE with value PATCH.
When I try to do a request.headers dump, I get:
Request Header: ('X-Http-Method-Override', u'PATCH')
Request Header: ('Origin', u'http://localhost:9000')
Request Header: ('Content-Length', u'622')
Request Header: ('Host', u'localhost:24435')
Request Header: ('Accept', u'application/json;charset=UTF-8')
Request Header: ('Content-Type', u'application/json')
Request Header: ('Accept-Encoding', u'identity')
I checked online and other Python applications are trying to access this specific request header with the case:
X-HTTP-Method-Override and not X-Http-Method-Override (which I get in request)
Flask takes care of extracting the headers from the WSGI environment variables for you, in the process removing the initial HTTP_ prefix. The prefix is there in the WSGI environment to distinguish the headers from other WSGI information, but that prefix is entirely redundant once you extracted the headers into a dedicated structure.
The request object also provides you with a specialised dictionary where keys are matched case insensitively. It doesn't matter what case you use here, as long as the lowercased version matches the lowercased header key; http, Http, HTTP and HtTp all are valid case variations. That's because the HTTP standard explicitly states that case should be ignored when handling headers.
See the Headers class reference in the Werkzeug documentation, it is the bases for the request.headers object. It in turn is compatible with the wsgiref.headers.Headers class, including this:
For each of these methods, the key is the header name (treated case-insensitively), and the value is the first value associated with that header name.
Emphasis mine.

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.

Python parsing set-cookie header

In PHP I send one cookie with secure and http only flags, and other without
setcookie("c2","value");
setcookie("c1","value", 0, "/", "", true, true);
It produces header
Set-Cookie: c2=value, c1=value; path=/; secure; httponly
In firebug I can see, this is OK (c1 secure flag is True, c2 is False)
I want to get which one of them is not using secure flag
My python code:
cookies = Cookie.SimpleCookie()
cookies.load(headers['set-cookie'])
print cookies
Output:
Set-Cookie: c1=value; Path=/\\r\\nSet-Cookie: c2=value
headers['set-cookie'] does contain original set-cookie header, it's ok
According to python documentation printing(handling as string) SimpleCookie instance should create set-cookie header. Point is, that something is missing after parsing original header.
Morsels also contains wrong values (secure and http only).
Is this some kind of misconfiguration or it's a bug in python library ?
Thanks :)
This might be a bit late but saw your question and thought you may still need help.
The code I use to read a cookie is:
import Cookie,os
def getCookieData():
try:
cookie = Cookie.SimpleCookie(os.environ["HTTP_COOKIE"])
session = cookie['usrSession'].value
return session
except (Cookie.CookieError, KeyError):
return None
My cookie string its something along the lines of:
Cookie: usrSession=12345
Hope this helps

Really weird Cookie header behaviour? - Cookies

I'm using Firefox 3.6.8 for these tests.
I'm setting a cookie within the response headers of my web app using:
Set-Cookie: session=7878dfdsfjsdf89sd89f8df9
This does not seem to override the session Cookie.
When a request is performed instead Firefox even sends duplicate cookies:
Cookie: session=7d75cd8f55895cbccb0d31ee07c7afc0;
session=671e8448a5cebda0442005a186cf69a3;
4cb6f2d75c9ffc8916cb55bcbaafecd8
What is going on??
Any ideas would be great!! =)
This is quite disastrous in my case... if someone could explain what's going on it would really help me out!
If you don't specify the path or domain for a cookie when setting it, it defaults to the current path and current hostname. If you then go ahead and try setting the same cookie name from a URL with a different path or hostname, it will add a new cookie instead of replacing the old one.
I suspect what you want to do is just set a cookie with a global path for your site and for your entire domain. So something like this:
Set-Cookie: session=7878dfdsfjsdf89sd89f8df9; path=/; domain=.mysite.com
You can delete the previous cookie using the response object.
response.delete_cookie(cookie_key)
The set of cookies is available via the request object in the request.COOKIES dictionary, and you can obtain the key from there.
Since you're using Django, here's how you might do this in the view function:
def my_view(request):
# do some work and create a response object
response = HttpResponse(some_content)
# first delete any previously set cookie named "session"
if 'session' in request.COOKIES:
response.delete_cookie('session')
# set the new cookie
response.set_cookie('session', <cookie value goes here>')
return response

Categories