Accessing all cookies in the Flask test response - python

After I make a request with the Flask test client, I want to access the cookies that the server set. If I iterate over response.headers, I see multiple Set-Cookie headers, but if I do response.headers["Set-Cookie"], I only get one value. Additionally, the headers are unparsed strings that are hard to test.
response = client.get("/")
print(response.headers['Set-Cookie'])
'mycookie=value; Expires=Thu, 27-Jun-2019 13:42:19 GMT; Max-Age=1800; Path=/'
for item in response.headers:
print(item)
('Content-Type', 'application/javascript')
('Content-Length', '215')
('Set-Cookie', 'mycookie=value; Expires=Thu, 27-Jun-2019 13:42:19 GMT; Max-Age=1800; Path=/')
('Set-Cookie', 'mycookie2=another; Domain=.client.com; Expires=Sun, 04-Apr-2021 13:42:19 GMT; Max-Age=62208000; Path=/')
('Set-Cookie', 'mycookie3=something; Domain=.client.com; Expires=Thu, 04-Apr-2019 14:12:19 GMT; Max-Age=1800; Path=/')
Why does accessing the Set-Cookie header only give me one header? How can I access the cookies and their properties for testing?

response.headers is a MultiDict, which provides the getlist method to get all the values for a given key.
response.headers.getlist('Set-Cookie')
It might be more useful to examine the cookies the client has, rather than the specific raw Set-Cookie headers returned by a response. client.cookie_jar is a CookieJar instance, iterating over it yields Cookie instances. For example, to get the value of the cookie with the name "user_id":
client.post("/login")
cookie = next(
(cookie for cookie in client.cookie_jar if cookie.name == "user_id"),
None
)
assert cookie is not None
assert cookie.value == "4"

The previous answer guided me to a slightly alternate version depending on what you want to do with the cookie.
I tried using client.cookie_jar, but I was testing for a few "non-standard" attributes like HttpOnly and SameSite. The cookie returned from client.cookie_jar does not return them, so I instead inspect the Set-Cookie header:
from werkzeug.http import parse_cookie
cookies = response.headers.getlist('Set-Cookie')
cookie = next(
(cookie for cookie in cookies if expected_cookie_name in cookie),
None
)
assert cookie is not None
cookie_attrs = parse_cookie(cookie)
assert cookie_attrs[expected_cookie_name] == expected_cookie_value
assert 'Secure' in cookie_attrs
assert 'HttpOnly' in cookie_attrs
assert cookie_attrs['SameSite'] == 'Lax'

Related

Scripted requests to CloudFlare protected resource. Python

Firstly, I need to send requests to the API of the resource. The resource is protected by CloudFlare detecting system.
I cannot send any request. The response is 403 Forbidden.
No data, no cookies.
Firstly, the API request requires cookies in headers. I found the request that was sent to accept all cookies.
After that I copied the bash of request, switched off all headers and sent request through Insomnia. It returned me completed cookies.
When I send the identical request in code it returned me 403 Forbidden status and non-completed cookies.
After that I decided to check the difference between browser, Insomnia and Python requests. I used Fiddler4. Python request returned 200 OK when I send the request in Python and everything returns me completed cookies. But if I switch Fiddler off I have 403 status.
Result of sending Python request with Fiddler:
[200 OK]
__Host-ariregweb=g4q8icpEmmIdHnQmFSNsfHdNhLLsgISKE7XRzphrLiscb4pSIzrpRLm9aiLCHqXa; Domain=ariregister.rik.ee; HttpOnly; Path=/; SameSite=lax; Secure; __cf_bm=GIYOJNShc1kY79bPk7GG1U6T.jV6K4BG8DLQoc70NT8-1671428340-0-AcxNKFuuhvkmblvy/q4WPGEPezvLQUL8/k6NeylOmaX5awlf1L7eOWnc55DGMsyPzpv5YKUDL6w100KlzzjsJVE=; Domain=ariregister.rik.ee; expires=Mon, 19-Dec-22 06:09:00 GMT; HttpOnly; Path=/; SameSite=None; Secure; _cfuvid=0EIfdSb4ltpOgYzKuMthWVvN0x5L3kn_uCfhyvBEk34-1671428340155-0-604800000; Domain=ariregister.rik.ee; HttpOnly; Path=/; SameSite=None; Secure
Result of sending Python request without Fiddler:
[403 Forbidden]
__cf_bm=0h8pprOoE6nOtjrZt6MYx6l7_4DIxIPn1_BL_e7Um2s-1671430063-0-AbvFUsYR8fOTT0NnrO1B4lJVTziYD+x2pnPLx1IyGjsgC29mqBNk+9iXhw2b1ewJiL2Cyi/iaTUilt6uPIbrSnw=; Domain=ariregister.rik.ee; expires=Mon, 19-Dec-22 06:37:43 GMT; HttpOnly; Path=/; SameSite=None; Secure
Maybe someone had such kind of problem or has some ideas how to bypass it. Thanks a lot.

How to explicitly set samesite=None on a flask response

Due to changes arriving in Chrome during July, I need to modify my app to explicitly provide the SameSite=None key value. This is due to the RFC treating the absence of this setting in a more impacting way than if it is present but set to None.
However on the set_cookie method, the samesite parameter is defaulted to None which results in it not being written into the set-cookie. How can I force this into the set-cookie part of the response?
When I try to set the samesite=None with the following code
resp.set_cookie('abcid', 'Hello', domain=request_data.domain, path='/', samesite=None, max_age=63072000)
This does not show any SameSite detail in the returned set-cookie
abcid=Hello; Domain=.localhost; Expires=Tue, 29-Jun-2021 22:34:02 GMT; Max-Age=63072000; Path=/
And if I try and explicitly set the value of Lax (which is one of the accepted values per rfc) as so
resp.set_cookie('abcid', "Hello", domain=request_data.domain, path='/', samesite="Lax", max_age=63072000)
I get back the set-cookie which explicitly has the SameSite=Lax setting
abcid=Hello; Domain=.localhost; Expires=Tue, 29-Jun-2021 23:03:10 GMT; Max-Age=63072000; Path=/; SameSite=Lax
I have tried None, "None", and "" but these either crash the application or omit the SameSite in the resultant response.
Any help would be gratefully received
Once the fix to this issue is
released, you will be able to use
set_cookie()
like this:
from flask import Flask, make_response
app = Flask(__name__)
#app.route('/')
def hello_world():
resp = make_response('Hello, World!');
resp.set_cookie('same-site-cookie', 'foo', samesite='Lax');
resp.set_cookie('cross-site-cookie', 'bar', samesite='Lax', secure=True);
return resp
While you're waiting for the release, you can still
set the header
explicitly:
from flask import Flask, make_response
app = Flask(__name__)
#app.route('/')
def hello_world():
resp = make_response('Hello, World!');
resp.set_cookie('same-site-cookie', 'foo', samesite='Lax');
# Ensure you use "add" to not overwrite existing cookie headers
resp.headers.add('Set-Cookie','cross-site-cookie=bar; SameSite=None; Secure')
return resp
You can also use the following code to set cookies with SameSite=None until fix is released
from werkzeug.http import dump_cookie
# That's a workaround for explicitly setting SameSite to None
# Until the following fix is released:
# https://github.com/pallets/werkzeug/issues/1549
def set_cookie(response, *args, **kwargs):
cookie = dump_cookie(*args, **kwargs)
if 'samesite' in kwargs and kwargs['samesite'] is None:
cookie = "{}; {}".format(cookie, b'SameSite=None'.decode('latin1'))
response.headers.add(
'Set-Cookie',
cookie
)

How do I reach cookie information in python requests?

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.

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

Python how to get the domain of a cookie

I want to get the domain of the cookie from the HTTP response. Code is:
cookie = Cookie.SimpleCookie()
cookie.load(cookie_string)
print 'cookie = ', cookie
this shows the cookie as
cookie= Set-Cookie: Cycle=MA==|MA==|MA==; Domain=.abc.xyz.net; expires=Tue, 05-Oct-2021 04:15:18 GMT; Path=/
I want to extract the domain from the above result.
I am trying
print cookie['Domain']
print cookie['Domain'].value
print cookie['Cycle']['Domain'].value
None of these work.
Thanks
try:
cookie['Cycle']['domain'] # lowercase domain !

Categories