Security and Client-Side Data StorageΒΆ
Cookie is an important way for servers to store data on client-side. However, its content can be forged by malicious clients, and the content is accessible by the client. Therefore, FutureFinity provides two types of cookies – the normal cookie and secure cookie.
Let’s look at the example below:
import futurefinity.web
import asyncio
app = futurefinity.web.Application()
@app.add_handler("/")
class MainHandler(futurefinity.web.RequestHandler):
async def get(self, *args, **kwargs):
username = self.get_cookie("username", default=None)
if not username:
return self.redirect("/login")
return "Hi, %s!" % username
@app.add_handler("/login")
class LoginHandler(futurefinity.web.RequestHandler):
async def get(self, *args, **kwargs):
return ("<form method=\"post\">"
"<input type=\"text\" name=\"username\">"
"<input type=\"submit\" value=\"submit\">"
"</form>")
async def post(self, *args, **kwargs):
username = self.get_body_arg("username")
self.set_cookie("username", username)
return self.redirect("/")
app.listen(23333)
try:
asyncio.get_event_loop().run_forever()
except KeyboardInterrupt:
pass
It will try to get the user information from the cookie first, if it fails, redirect visitors to login page to submit a username. After that, redirect the visitors back to the index and show the username.
As shown above, we use RequestHandler.get_cookie
to get the value of
a cookie with the name, and we use RequestHandler.set_cookie
to
set a new cookie.
For redirecting requests to one other URL, we use RequestHandler.redirect
function. Although, in this example, we returned the value of self.redirect
,
actually, you don’t have to, the value of this function will always be None
,
and the request will be finished once the self.redirect
returns.
But, as mentioned before, the content of cookies is accessible by the client, and it can be forged. If we want to protect the content, we need to switch to secure cookies.
Before we continue, we need a security secret for encryption, you can generate
one on your own, or you can use futurefinity.security.get_random_str
function to generate a new one.
Cryptography Warning: If random.SystemRandom
is unavailable, this
function will fallback to fake random number generator, which has no promise on
cryptography security.
In order to protect the content inside the cookie, we also need to use
cryptography
library from PyPI. Please use pip install cryptography
to install it.
We have to change the example as below:
@app.add_handler("/")
class MainHandler(futurefinity.web.RequestHandler):
async def get(self, *args, **kwargs):
username = self.get_secure_cookie("username", default=None)
if not username:
return self.redirect("/login")
return "Hi, %s!" % username
@app.add_handler("/login")
class LoginHandler(futurefinity.web.RequestHandler):
async def get(self, *args, **kwargs):
return ("<form method=\"post\">"
"<input type=\"text\" name=\"username\">"
"<input type=\"submit\" value=\"submit\">"
"</form>")
async def post(self, *args, **kwargs):
username = self.get_body_arg("username")
self.set_secure_cookie("username", username)
return self.redirect("/")
A security secret is like a password for a server, please treat it like a password. If the security secret is changed, all former secure cookies will become invalid.
You can pass the security secret as a application setting, all application settings are passed by keywords arguments when the instance of application class is created.
Cross-Site Request Forgeries(CSRF) Protection
CSRF is one of malicious exploit of a website for a website to run unauthorized commands or even transactions for a online shopping website.
For more information, please visit: CSRF on Wikipedia
To prevent these attacks, FutureFinity has a built-in CSRF protection. To enable
the protection, simply set csrf_protect=True
and security_secret
in application settings.