Tornado之初学者(五)

Tronado的web应用安全(cookie和CSRF/XSRF)

  安全cookies是web应用的安全防范之一,浏览器中的cookies存储了用户的个人信息,当然包括了某些重要的敏感的信息,如果一些恶意的脚本得到甚至修改了用户的cookies的信息,用户的信息就得不到安全的保障,所以应该对用户的cookies进行保护。Tornado的安全cookies可以对cookies签名进行安全加密,已检查cookies是否被修改过,因为恶意脚本不知道安全密钥,所以无法修改(但是恶意脚本仍然可以截获cookies来“冒充”用户,只是不能修改cookies而已,这也是另外一个安全隐患,本文并不讨论这点)。

  Tornado的get_secure_cookie()和set_secure_cookie()可以安全的获取和发送浏览器的cookies,可以防止浏览器中的恶意修改,但是为了使用这个功能,必须在tornado.web.Application的settings中设置cookie_secret,其值为一个唯一的随机字符串(比如:base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)可以产生一个唯一的随机字符串)。

  CSRF或XSRF,即跨站请求伪造,是web应用都涉及到的一个安全漏洞,它利用了浏览器的一个安全漏洞:浏览器允许恶意攻击者在受害者网站注入脚本使未授权请求代表一个已登录用户。即别人可以“冒充”你做一些事情,而服务器也会认为这些操作是你做的。

  为了防止XSRF攻击,应该注意:一是开发者考虑某些重要的请求时需要使用POST方法,二是Tornado的一个防范伪造POST的功能(这个功能是一种策略,tornado也实现了这种策略),就是在每个请求中包含一个参数值(隐藏的HTML表单元素值)和存储的cookie值,若两者(称之为令牌)匹配上了,则证明请求有效,当某个不可信的站点没有访问cookie数据的权限时,它就不能再请求中包含这个令牌cookie值,自然就无法发送有效的请求了。tornado中使用这个功能需要在tornado.web.Application的settings中设置xsrf_cookies,值为True,同时必须在HTML的表单中包含xsrf_form_html()函数,以此形成cookie令牌。

  tornado的用户验证可以使用装饰器@tornado.web.authenticated,使用这个装饰器时需注意:①必须重写get_current_user()方法,这个返回的值将赋给self.current_user,②被装饰的方法被执行前会检查self.current_user的bool值是否为False(默认是None),若为False则会重定向到tornado.web.Application的settings中login_url指定的URL,③需要在tornado.web.Application的settings中login_url的URL,以便用户验证self.current_user之前可以重定向到这个URL。④当Tornado构建重定向URL时,它还会给查询字符串添加一个next参数,其值为重定向之前的URL,可以使用如self.redirect(self.get_argument('next', '/'))这样的语句在用户验证成功后回到原来的页面。

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 import tornado.httpserver
 4 import tornado.ioloop
 5 import tornado.web
 6 import tornado.options
 7 import os.path
 8 import base64
 9 import uuid
10 
11 from tornado.options import define, options
12 
13 define('port', default=8000, help='run on the given port', type=int)
14 
15 
16 class BaseHandler(tornado.web.RequestHandler):
17     def get_current_user(self):
18         """重写此方法,返回的值将赋给self.current_user"""
19         return self.get_secure_cookie('username')  # 获取cookies中username的值
20 
21 
22 class LoginHandler(BaseHandler):
23     def get(self):
24         self.render('login.html')
25 
26     def post(self):
27         self.set_secure_cookie('username', self.get_argument('username'))  # 将请求中的username值赋给cookie中的username
28         self.redirect('/')
29 
30 
31 class HomeHandler(BaseHandler):
32     @tornado.web.authenticated  # 使用此装饰器必须重写方法get_current_user
33     def get(self):
34         """被@tornado.web.authenticated装饰的方法被执行前会检查self.current_user的bool值是否为False,不为False时才会执行此方法"""
35         self.render('index.html', user=self.current_user)
36 
37 
38 class LogoutHandler(BaseHandler):
39     def get(self):
40         if self.get_argument('logout', None):
41             self.clear_cookie('username')  # 清楚cookies中名为username的cookie
42             self.redirect('/')
43 
44 
45 if __name__ == '__main__':
46     tornado.options.parse_command_line()
47 
48     settings = {
49         'template_path': os.path.join(os.path.dirname(__file__), 'templates'),
50         'cookie_secret': base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes),  # 设置cookies安全,值为一个唯一的随机字符串
51         'xsrf_cookies': True,  # 设置xsrf安全,设置了此项后必须在HTML表单中包含xsrf_form_html()
52         'login_url': '/login'  # 当被@tornado.web.authenticated装饰器包装的方法检查到self.current_user的bool值为False时,会重定向到这个URL
53     }
54 
55     application = tornado.web.Application([
56         (r'/', HomeHandler),
57         (r'/login', LoginHandler),
58         (r'/logout', LogoutHandler)
59     ], **settings)
60 
61     http_server = tornado.httpserver.HTTPServer(application)
62     http_server.listen(options.port)
63     tornado.ioloop.IOLoop.instance().start()
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Welcom Back!</title>
 6 </head>
 7 <body>
 8     <h1>Welcom back, {{ user }}</h1>
 9 </body>
10 </html>
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Please Log In</title>
 6 </head>
 7 <body>
 8     <form action="/login" method="POST">
 9         {% raw xsrf_form_html() %}<!-- 这其实是一个隐藏的<input>元素,定义了“_xsrf”的值,会检查POST请求以防止跨站点请求伪造 -->
10         Username: <input type="text" name="username" />
11         <input type="submit" value="Log In" />
12     </form>
13 </body>
14 </html>

猜你喜欢

转载自www.cnblogs.com/guyuyun/p/9086144.html