There is my code:
class WebHandler(RequestHandler):
def get(self):
self.set_cookie('name1', 'value.1')
self.set_cookie('name2', 'value.2')
self.write('OK')
When I run this code, chrome browser doesn't set cookie 'name1' but still set cookie 'name2'. When I remove dot character in value of cookies, it's oke. How do I set multiple cookies with dot character in value?
From my point of view, it is possible that the question is a bit incomplete, but I will still try to answer it in the hope that it will help you and others.
Tornado’s set_secure_cookie() and get_secure_cookie() functions send and retrieve browser cookies that are protected against malicious modifications in the browser. To use these functions, you must specify the cookie_secret parameter in the application constructor. Let’s look at a simple example.
The application will render a page that counts how many times it has been reloaded in the browser. If no cookie has been set (or if the cookie has been tampered with), the application will set a new cookie with the value 1. Otherwise, the application will increment the value read from the cookie.
import tornado.httpserver
import tornado.ioloop
import tornado.web
import tornado.options
from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)
class MainHandler(tornado.web.RequestHandler):
def get(self):
cookie = self.get_secure_cookie("count")
count = int(cookie) + 1 if cookie else 1
countString = "1 time" if count == 1 else "{} times".format(count)
self.set_secure_cookie("count", str(count))
self.write(
"""
<html><head><title>Cookie Counter</title></head>
<body><h1>You’ve viewed this page {} times.</h1>
</body></html>
""".format(
countString
)
)
if __name__ == "__main__":
tornado.options.parse_command_line()
settings = {
"cookie_secret": "u5SXVuerTfyQTT7uTbu7HjqiqHnh8UsBm37J4Y5lwto="
}
application = tornado.web.Application([(r"/", MainHandler)], **settings)
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(options.port)
try:
tornado.ioloop.IOLoop.instance().start()
except KeyboardInterrupt:
print("Server has shut down.")
If you inspect the value of the cookie in the browser, you will notice that the value stored for count is "count=\"2|1:0|10:1612910394|5:count|4:MQ==|e8c35def2daaec8da8ca5f3f1db63168f97027024a824d17b5e405f4f97c26ce\"". Tornado encodes the cookie value as a Base-64 string and appends a timestamp and an HMAC signature to the cookie contents. If the cookie’s timestamp is too old (or from the future), or if the signature doesn’t match the expected value, the get_secure_cookie() function assumes the cookie has been tampered with and will return None, as if the cookie had not been set.
The cookie_secret value passed to the Application constructor should be a unique, random string. Executing the following code snippet in a Python shell will generate one for you:
>>> import base64, uuid
>>> base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
b'KeQWrXgiTjWQIEzcRbx0vV/IM/mYAEqvs+EtZ/5dvfs='
Tornado’s secure cookies are still susceptible to snooping, however. Attackers may be able to intercept cookies via scripts or plug ins in the browser, or simply by eavesdropping unencrypted network data. Remember that cookie values are signed rather than encrypted. Malicious programs are able to read stored cookies and either transmit their data to arbitrary servers or forge requests by sending them unmodified to the application. Therefore, it’s important to avoid storing sensitive user data in a browser cookie.
We also need to be aware of the possibility that a user could modify his own cookies, which could lead to a privilege escalation attack. If, for example, we store the number of remaining articles a user has paid to view in a cookie, we would want to prevent the user from updating that number himself in an attempt to get free content. The httponly and secure cookie properties can help prevent these sorts of attacks.
Setting the secure attribute on a cookie instructs the browser to transfer the cookie only over SSL connections. (It’s a little confusing, but this is not the same as Tornado’s secure cookies, which are more accurately described as signed cookies.) Since Python version 2.6, the Cookie object also supports the httponly attribute. Including this attribute instructs the browser to make the cookie inaccessible to JavaScript, which can prevent cross-site scripting attacks from reading the cookie’s value.
To enable these features, you can pass keyword arguments to the set_cookie and
set_secure_cookie methods. For example, a secure, HTTP-only cookie (that’s not
signed by Tornado) could be sent with the call:
self.set_cookie('foo', 'bar', httponly=True, secure=True)
Since the Tornado comes with built-in XSRF protection. For more details on this, see the official documentation, you can also see one of my answers in which it is set "xsrf_cookies": True.
Related
I'm trying to put some security on my Flask web app. As a first step I'm going to make my session cookie secure by setting SESSION_COOKIE_SECURE to true.
But after I get my session cookie from "inspect element" I can decode session cookie easily and there is no difference whether I add SESSION_COOKIE_SECURE or not.
Here is my code:
from flask import Flask, request, app, render_template, session, send_file, redirect
MyApp = Flask(__name__)
MyApp.secret_key = "something"
application = MyApp
if __name__ == "__main__":
MyApp.debug = False
MyApp.config.update(
SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax',
)
MyApp.config["SESSION_PERMANENT"] = True
MyApp.run()
I also tried to add this attribute using the following syntax but this made no difference:
MyApp.config['SESSION_COOKIE_SECURE'] = True
When I try to print SESSION_COOKIE_SECURE I get this error
Traceback (most recent call last):
File "...", line ..., in <module>
print(MyApp.session_cookie_secure)
AttributeError: 'Flask' object has no attribute 'session_cookie_secure'
My Flask version is 1.0.2, and I'm on HTTPS.
Setting SESSION_COOKIE_SECURE does not encrypt the cookie value, no. When set, this causes Flask to create cookies with the "Secure" flag set. This means that a browser can only return the cookie to the server over an encrypted connection, nothing more. The setting doesn't change anything about the cookie value itself.
Flask produces cookies that are cryptographically signed, by default. That means that the cookie contents can be decoded but not altered, because a third party without access to the server secret can't create a valid signature for the cookie.
You generally don't need to encrypt your session cookie if you a) use HTTPS (which encrypts the data from outsiders) and b) protect your web app from XSS attacks. Without an XSS attack vector, attackers can't get access to your cookie contents at all anyway.
You certainly don't need to do so here, as SESSION_COOKIE_HTTPONLY means that the browser will never expose the cookie to JavaScript, and only someone with full access to the browser can see the cookie value.
Flask doesn't have a 'encrypt cookie' setting, because it is not deemed necessary when you can secure the cookie in other ways. You should not store information in a session cookie so sensitive that it should be protected from the end-user with access to the browser storage; keep such data on the server and only store a unique identifier in the session to retrieve that secret data later on.
If for some reason you can't keep such secrets out of the session cookie and are unwilling to accept that the end-user can read this data, then you'll have to encrypt the cookie yourself or use an alternative session provider for Flask, such as EncryptedSession.
As for the attribute error: only a few configuration settings are accessible as attributes on the Flask object. To print arbitrary configuration settings, use the app.config object:
print(MyApp.config['SESSION_COOKIE_SECURE'])
I couldn't log into my own Reddit account using the code below.
Error message:
raise HTTPError(http_error_msg, response=self) HTTPError: 403 Client Error: Forbidden
Is there a way to get around this error?
class PrawTest(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.out.write('Yo, imma redit bot!')
get_login= ConfigParser.ConfigParser()
get_login.read("logins.ini")
r = praw.Reddit(user_agent='Captain Reddit!')
r.login(get_login.get("login1", "username"),get_login.get("login1","password"))
app = webapp2.WSGIApplication([('/hype_shit_up', PrawTest)], debug=True)
Your code is very needlessly over-complicated just to login with PRAW. The way reccomended in the documentation is to have reddit = praw.Reddit('SOME-NAME-HERE') to login. Then, in your praw.ini file, set it up to look like this:
[DEFAULT]
# A boolean to indicate whether or not to check for package updates.
check_for_updates=True
# Object to kind mappings
comment_kind=t1
message_kind=t4
redditor_kind=t2
submission_kind=t3
subreddit_kind=t5
# The URL prefix for OAuth-related requests.
oauth_url=https://oauth.reddit.com
# The URL prefix for regular requests.
reddit_url=https://www.reddit.com
# The URL prefix for short URLs.
short_url=https://redd.it
[SOME-NAME-HERE]
user_agent=USER-AGENT-HERE
username=REDDIT-ACCOUNT-USERNAME
password=REDDIT-ACCOUNT-PASSWORD
client_id=REDDIT-APP-CLIENT-ID
client_secret=REDDIT-APP-CLIENT-SECRET
Requirements for the user agent are found here:
Change your client's User-Agent string to something unique and descriptive, including the target platform, a unique application identifier, a version string, and your username as contact information, in the following format:
<platform>:<app ID>:<version string> (by /u/<reddit username>)
-Example: User-Agent: android:com.example.myredditapp:v1.2.3 (by /u/kemitche)
Many default User-Agents (like "Python/urllib" or "Java") are drastically limited to encourage unique and descriptive user-agent strings.
Including the version number and updating it as you build your application allows us to safely block old buggy/broken versions of your app.
NEVER lie about your user-agent. This includes spoofing popular browsers and spoofing other bots. We will ban liars with extreme prejudice.
If you have any future questions dont hesitate to ask them as a comment to this answer!
I'm struggling to figure this one out, sessions work when i run my application normally but i can't figure out how to set data in the session in my test case.
The docs say in a test case you have to save the session to apply the changes before making the request. https://docs.djangoproject.com/en/1.2/topics/testing/#persistent-state
e.g.
from django.test import TestCase
class TestLogin(TestCase):
def test_processuser(self):
redirect = '/processuser/'
session = self.client.session
session["id"] = '1234'
session.save()
response = self.client.get(redirect)
However the session object returned from self.client.session is just a normal python dict?
Diging into the code the Client.session call is this:
def _session(self):
"""
Obtains the current session variables.
"""
if 'django.contrib.sessions' in settings.INSTALLED_APPS:
engine = import_module(settings.SESSION_ENGINE)
cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None)
if cookie:
return engine.SessionStore(cookie.value)
return {}
session = property(_session)
cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) returns None so it just returns a dict in stead of a session store.
It looks like i have to do some more preparation in the test client before i save a session? Not really got much experience in this any help would be appreciated.
Django 1.2.5
Python 2.6.5
Cheers,
Asim.
Edit: this answer is now outdated; as of at least Django 1.7, you can just set the cookie directly on the test client.
See e.g. this answer to this question or the comments on this answer to another, similar, question.
Old outdated answer follows...
Adding this for people who really do need to set a cookie, e.g. because they need to do something which isn't covered by the Django auth mechanism...
You can't set cookies directly on TestClient objects but if you use the RequestFactory class you can do it. So instead of (say):
response = Client().post('/foo')
you do:
request = RequestFactory().post('/foo')
request.COOKIES['blah'] = 'hello'
response = foo_view(request)
where foo_view is the view corresponding to the '/foo' path, i.e. the view you're looking to test.
HTH somebody.
The simplest thing would be to login as someone, so the test client would set the cookie for you.
self.client.login(username,password)
should do. Refer the documentation for more.
Contrary to the most upvoted answer, you CAN set cookies directly on the test client.
Remember everything is an object, you just have to know where/what to patch
so it goes like this:
client.cookies[key] = data
client.cookies is an instance of http.cookies.SimpleCookie from the standard library and it behaves like a dict. so you can use .update for bulk updates to a cookies value. This can be useful if you want to alter other cookie values like max-age, path domain etc.
Finally, if you want to set a signed_cookie, You can reuse the helpers from django like this:
from django.core.signing import get_cookie_signer
signed_cookie_value = get_cookie_signer(salt=key).sign(data)
client.cookies[key] = signed_cookie_value
Pay attention to the salt. It has to match on both ends (Signing and retrieval). A Different salt value for signing would generate a different cookie that cannot be retrieved when you call response.get_signed_cookie(key)
For other people who are running into this problem please be aware that the Client.logout() function will throw away your cookies. For example:
response = self.client.post(self.url, self.data)
print response.client.cookies.items() # Displays the cookie you just set
self.client.logout()
response = self.client.post(reverse('loginpage'), {'username': 'username', 'password': 'password'}, follow=True)
print response.client.cookies.items() # Does not display the cookie you set before since it got destroyed by logout()
To make sure your cookies stay alive during testing make a call to your logout page in stead of using the Client.logout() function, like so:
response = self.client.post(self.url, self.data)
print response.client.cookies.items() # Displays the cookie you just set
self.client.get(reverse('logoutpage'))
response = self.client.post(reverse('loginpage'), {'username': 'username', 'password': 'password'}, follow=True)
print response.client.cookies.items() # Does display the cookie you set before since it did not get destroyed by client.logout()
In web.py, you can get access to the request's cookies with web.webapi.cookies(), and you can set the value of a cookie with web.webapi.setcookie(...). The documentation isn't clear on how one deletes a cookie, however -- do you just setcookie with a value of None?
You're right, it's certainly not obvious from setcookie()'s docstring, or from the online docs, but it is there somewhere:
The third (and optional) argument to web.setcookie(), "expires", allows you to set when you want your cookie to expire. Any negative number will expire the cookie immediately.
For example, here's part of what we do in our sign-out code (delete the user's session cookie):
web.setcookie('session', '', expires=-1, domain=session_cookie_domain)
Note that you must delete the cookie with the same domain and secure flag as you set it with, otherwise it won't delete. Also, with web.py, you normally use web.setcookie() as a shortcut to web.webapi.setcookie().
web.py doesn't seem to have a way to delete a cookie. If you go through the documentation in the cookbook, it's sparse and doesn't even talk about things like path (so you can set a cookie to a specific location within a domain). So we must turn to the sourcecode. In this, try as I may, I cannot find any reference to a delete,remove, or revoke cookie method.
Having said that, after testing, it is safe to use None to expire a cookie. Here's a quick web app that'll display it.
import web
web.config.debug = False
urls = (
'/', 'index'
)
class index:
def GET(self):
c = web.cookies().get('test1')
if c:
if c=="test":
print 'this is ' + c
web.setcookie('test1', 'test2', domain = 'example.com')
else:
print c
web.setcookie('test1', 'test', domain = 'example.com' expires = None)
return c
else:
print "didnt find cookie"
web.setcookie('test1', 'test', domain = 'example.com' expires='')
return 'I set fire to the rain'
app = web.application(urls, globals())
if __name__ == "__main__":
app.run()
In this the web app first checks if the cookie exists. If it doesn't it sets the cookie 'test1' with the value 'test'. On the next refresh, it will change the value to 'test2'. On the next refresh it sets the value again to 'test' but also expires the cookie. This should result in the next refresh showing 'I set fire to the rain'.
On my Pylons website, I have my login form sending it's data to 'https://mysite.com'. Upon a successful login, a redirect takes place to send them to their profile page.
redirect(url(controller='profile'))
This sends the user to http://mysite.com/profile instead of https://mysite.com/profile. The only way I've found to fix this is to change the redirect to:
redirect(url(controller='profile', protocol='https'))
The problem I have with this is "what if, for whatever reason, my cert goes away and I have to drop SSL" I don't want to have to go through my entire code looking for all redirects I specify the 'https' protocol in. I want my login to send the user to HTTPS and that's it...
Is there a reason the redirect drops to HTTP? Is there a way to stop it? :/
Since I spent a couple of hours wading through the pylons/routes/beaker/etc. source I thought I'd share my solution.
First a bit of context. I'm using an elastic load balancer (ELB) on AWS with SSL termination. The application is built to run solely over https; this is a post-firesheep world after all. It's layered like so:
ELB -> nginx -> pasteWSGI -> pylons
ELB is jolly good in terms of simplicity but any call to pylons.controllers.util.redirect would trigger a 302 Redirect to "http://mysite/". The ELB would not change that on the way back (no reason to) and so my browser would be sent back to port 80 and there is no ELB listening on that port.
I've tried updating the Mapper as suggested above.
it did not work,
I wanted my redirects to be relative. Switching to https in pylons means that the URL generator goes and fetches the host to create a new URL (https://localhost/....)
Note that Mapper.redirect_to works out of the box and uses relative redirects so there is no need to mess with that. The fundamental problem is that controllers.redirect uses a slightly different code path. In particular, in Routes, the controllers.util.redirect is not a redirect (there's an "if routes and routes.redirect" which evals to False).
My solution: replace all calls to redirect by a new controller method (called redirect too) to change redirects from absolute to relative redirects.
The code is as follows:
lib/helpers.py
def relative_redirect(to_url, start_response):
"""Returns a redirect that is compatible with AWS ELB (no absolute http responses)
Using pylons.controllers.util.redirect triggers an exception that'll be turned into a 302
But with an absolute path so the response does not contains https but simple http
"""
start_response("302 Found", [("Content-Type", "text/plain; charset=utf-8"), ("Location", url(to_url))])
return ["You are being redirected to {0}".format(url(to_url))]
With that bit called from the base class of my controllers:
class BaseController(WSGIController):
...
def redirect(self, to_url):
"""Force a relative redirection, to work with AWS ELB"""
return relative_redirect(to_url, self.start_response)
I'd customize the Mapper so that every call to "url" would force the correct protocol...
Inside routing.py:
class CustomMapper(Mapper):
def generate(self, *args, **kwargs):
kwargs["protocol"] = "https"
return Mapper.generate(self, *args, **kwargs)
def make_map(config):
"""Create, configure and return the routes Mapper"""
map = CustomMapper(directory=config['pylons.paths']['controllers'],
always_scan=config['debug'])