Tornado入门(七)【认证和安全】

Cookies和安全Cookies

通过set_cookie来设置浏览器中的cookies信息。

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        if not self.get_cookie("mycookie"):
            self.set_cookie("mycookie", "myvalue")
            self.write("Your cookie was not set yet!")
        else:
            self.write("Your cookie was set!")

Cookies信息通常不安全,很容易被篡改。如果需要通过cookies来区分不同的登录用户,则需要对cookies进行签名,以防伪造。Tornado通过set_secure_cookieget_secure_cookie方法支持签名Cookies。为了使用这两个方法,需要在应用中配置cookie_secret

application = tornado.web.Application([
    (r"/", MainHandler),
], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")

签过名的cookies包含了编码之后的cookie值,时间戳,和一个HMAC签名。如果cookie过期或者签名不匹配,则get_secure_cookie将返回None

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        if not self.get_secure_cookie("mycookie"):
            self.set_secure_cookie("mycookie", "myvalue")
            self.write("Your cookie was not set yet!")
        else:
            self.write("Your cookie was set!")

Tornado的安全cookie机制并不能保证绝对的安全,一是因为cookie值可以被用户看到,二是因为cookie_secret是一个对称密钥,所以必须确保它的安全。

默认情况下,Tornado cookies的生存期为30天,如果需要修改,可以将参数expire_days传递给set_secure_cookiemax_age_days参数传递给get_secure_cookie。这个两个参数是分别传递的。

用户认证

认证过的用户可以通过self.current_user访问,在模板中可以通过current_user访问,默认情况下current_userNone

为了实现用户认证,需要重写处理器中的get_current_user方法,以确定当前的用户。下面是一个简单的示例。

class BaseHandler(tornado.web.RequestHandler):
    def get_current_user(self):
        return self.get_secure_cookie("user")

class MainHandler(BaseHandler):
    def get(self):
        if not self.current_user:
            self.redirect("/login")
            return
        name = tornado.escape.xhtml_escape(self.current_user)
        self.write("Hello, " + name)

class LoginHandler(BaseHandler):
    def get(self):
        self.write('<html><body><form action="/login" method="post">'
                   'Name: <input type="text" name="name">'
                   '<input type="submit" value="Sign in">'
                   '</form></body></html>')

    def post(self):
        self.set_secure_cookie("user", self.get_argument("name"))
        self.redirect("/")

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__")

通过tornado.web.authenticated修饰器来进行用户认证,只有用户登录过之后,才可以访问当前请求,否则重定向至login_url(在应用中进行配置)。

class MainHandler(BaseHandler):
    @tornado.web.authenticated
    def get(self):
        name = tornado.escape.xhtml_escape(self.current_user)
        self.write("Hello, " + name)

settings = {
    "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
    "login_url": "/login",
}
application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], **settings)

第三方认证

tornado.auth模块实现了第三方的认证协议,包括Google和Facebook等。

下面是使用Google的认证:

class GoogleOAuth2LoginHandler(tornado.web.RequestHandler,
                               tornado.auth.GoogleOAuth2Mixin):
    @tornado.gen.coroutine
    def get(self):
        if self.get_argument('code', False):
            user = yield self.get_authenticated_user(
                redirect_uri='http://your.site.com/auth/google',
                code=self.get_argument('code'))
            # Save the user with e.g. set_secure_cookie
        else:
            yield self.authorize_redirect(
                redirect_uri='http://your.site.com/auth/google',
                client_id=self.settings['google_oauth']['key'],
                scope=['profile', 'email'],
                response_type='code',
                extra_params={'approval_prompt': 'auto'})

跨站请求伪造

跨站请求伪造是很多个人网站都会遇到的安全问题。

通常防止XSRF的方法是传递一个不可预测的cookie值给用户,然后用户提交表单时携带这个值。如果cookie信息-和表中的值不匹配,则说明请求时可能是伪造的。

Tornado内置了XSRF防御机制,通过在应用中设置xsrf_cookies参数来开启。

settings = {
    "cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
    "login_url": "/login",
    "xsrf_cookies": True,
}
application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/login", LoginHandler),
], **settings)

如果配置了xsrf_cookies, 则Tornado应用会为所有的用户设置_xsrfcookie,并拒绝所有没有携带正确的_xsrf字段的POSTPUT或者DELETE请求。注意需要确保所有提交的表单都包含了这个字段。可以使用UIModule xsrf_form_html()

<form action="/new_message" method="post">
  {% module xsrf_form_html() %}
  <input type="text" name="message"/>
  <input type="submit" value="Post"/>
</form>

如果是通过Ajax提交表单,同样需要确保提供了_xsrf字段 :

function getCookie(name) {
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

jQuery.postJSON = function(url, args, callback) {
    args._xsrf = getCookie("_xsrf");
    $.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
        success: function(response) {
        callback(eval("(" + response + ")"));
    }});
};

对于PUTDELETExsrf_form_html可以通过头部字段X-XSRFToken设置。

如果需要自定义XSRF的行为,可以重写RequestHandler.check_xsrf_cookie方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张善友的专栏

HTTP Basic Authentication验证WCF Data Service

WCF Data Service是OData协议,也是RESTFul Service的一种,上篇文章已经介绍了HTTP Basic Authentication...

2098
来自专栏积累沉淀

CXF 框架拦截器

CXF的拦截器 •为什么设计拦截器? 1.为了在webservice请求过程中,能动态操作请求和响应数据, CXF设计了拦截器. •拦截器分类: 1...

1946
来自专栏挖坑填坑

使用.net core ABP和Angular模板构建博客管理系统(实现博客列表页面)

1731
来自专栏安恒网络空间安全讲武堂

XCTF-赛博地球杯工业互联网安全大赛web部分题解

0x01工控云管理系统项目管理页面解析漏洞 题目首先给出了源码: `http://47.104.156.32:20007/view-source.php` 关键...

5678
来自专栏Kubernetes

原 深入分析Kubernetes Sche

1914
来自专栏FreeBuf

Windows DNS API RCE漏洞分析及PoC构造

根据 Microsoft 2017 年 10 月安全通告,多个版本 Windows 中的 dnsapi.dll 在处理 DNS response 时可导致 SY...

34610
来自专栏比原链

Derek解读Bytom源码-P2P网络 地址簿

Gitee地址:https://gitee.com/BytomBlockchain/bytom

1113
来自专栏landv

杨格门锁 YGSLockSDK V3.3酒管软件接口_99v

1759
来自专栏高性能服务器开发

windows完成端口(二)

系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) ...

39711
来自专栏Kubernetes

原 荐 Kubernetes Resourc

更多关于kubernetes的深入文章,请看我csdn或者oschina的博客主页。 ResoureQuota介绍 关于ResoureQuota和Resourc...

5069

扫码关注云+社区