阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

XSRF跨站请求伪造

216次阅读
没有评论

共计 4591 个字符,预计需要花费 12 分钟才能阅读完成。

一、伪造案例

  • 先建立一个网站 127.0.0.1:8000,使用上一节中的 Cookie 计数器:

    class IndexHandler(RequestHandler): def get(self): cookie = self.get_secure_cookie("count") count = int(cookie) + 1 if cookie else 1 self.set_secure_cookie("count", str(count)) self.write('<html><head><title>Cookie 计数器 </title></head>' '<body><h1> 您已访问本页 %d 次。</h1>' % count + '</body></html>' )
  • 再建立一个网站 127.0.0.1:9000

    class IndexHandler(RequestHandler): def get(self): self.write('<html><head><title> 被攻击的网站 </title></head>' '<body><h1> 此网站的图片链接被修改了 </h1>' '<img alt=" 这应该是图片 "src="https://www.zutuanxue.com:8000/?f=9000/">' '</body></html>' )
  • 说明

    在 9000 网站我们模拟攻击者修改了我们的图片源地址为 8000 网站的 Cookie 计数器页面网址。当我们访问 9000 网站的时候,在我们不知道、未授权的情况下 8000 网站的 Cookie 被使用了,以至于让 8000 网址认为是我们自己调用了 8000 网站的逻辑。这就是 CSRF(Cross-site request forgery)跨站请求伪造(跨站攻击或跨域攻击的一种),通常缩写为 CSRF 或者 XSRF

    我们刚刚使用的是 GET 方式模拟的攻击,为了防范这种方式的攻击,任何会产生副作用的 HTTP 请求,比如点击购买按钮、编辑账户设置、改变密码或删除文档,都应该使用 HTTP POST 方法(或 PUT、DELETE)。但是,这并不足够:一个恶意站点可能会通过其他手段来模拟发送 POST 请求,保护 POST 请求需要额外的策略

二、同源策略

浏览器有一个很重要的概念——同源策略 (Same-Origin Policy)。所谓同源是指,域名,协议,端口相同。不同源的客户端脚本(javascript、ActionScript) 在没明确授权的情况下,不能读写对方的资源

由于第三方站点没有访问 cookie 数据的权限(同源策略),所以我们可以要求每个请求包括一个特定的参数值作为令牌来匹配存储在 cookie 中的对应值,如果两者匹配,我们的应用认定请求有效。而第三方站点无法在请求中包含令牌 cookie 值,这就有效地防止了不可信网站发送未授权的请求。

三、开启 XSRF 保护

  • 在 Application 的构造函数中添加 xsrf_cookies 参数

    app = tornado.web.Application([(r"/", IndexHandler),], cookie_secret = "2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A=", xsrf_cookies = True )

    说明

    当这个参数被设置时,Tornado 将拒绝请求参数中不包含正确的_xsrf 值的 POST、PUT 和 DELETE 请求

    class IndexHandler(RequestHandler): def post(self): self.write("hello Lucky")

    用不带_xsrf 的 post 请求时,报出了 HTTP 403: Forbidden ('_xsrf' argument missing from POST) 的错误

四、模板应用

  • 在模板中添加

    {% module xsrf_form_html() %}
  • 模板应用示例

    建模板 index.html

    <!DOCTYPE html> <html> <head> <title> 测试 XSRF</title> </head> <body> <form method="post"> {% module xsrf_form_html() %} <input type="text" name="username"/> <input type="submit" value="Post"/> </form> </body> </html>

    Tornado

    import tornado.web,os import tornado.ioloop import tornado.httpserver from tornado.web import RequestHandler class IndexHandler(RequestHandler): def get(self): self.render('test_xsrf.html') def post(self): self.write("hello lucky") if __name__ == '__main__': BASE_DIR = os.path.dirname(__file__) app = tornado.web.Application([(r"/", IndexHandler),], cookie_secret = "2hcicVu+TqShDpfsjMWQLZ0Mkq5NPEWSk9fi0zsSt3A=", xsrf_cookies = True, template_path = os.path.join(BASE_DIR,'templates'), autoreload=True, debug=True, ) http_server = tornado.httpserver.HTTPServer(app) http_server.listen(8000) tornado.ioloop.IOLoop.current().start()

    说明:

    模板中添加的语句帮我们做了两件事

    • 为浏览器设置了_xsrf 的 Cookie(注意此 Cookie 浏览器关闭时就会失效)
    • 为模板的表单中添加了一个隐藏的输入名为_xsrf,其值为_xsrf 的 Cookie 值

    渲染后的页面原码如下

    <!DOCTYPE html> <html> <head> <title>测试 XSRF</title> </head> <body> <form method="post"> <input type="hidden" name="_xsrf" value="2|543c2206|a056ff9e49df23eaffde0a694cde2b02|1476443353"/> <input type="text" name="username"/> <input type="submit" value="Post"/> </form> </body> </html>

五、非模板应用

说明:对于不使用模板的应用来说,首先要设置_xsrf 的 Cookie 值,可以在任意的 Handler 中通过获取 self.xsrf_token 的值来生成_xsrf 并设置 Cookie

  • 非模板应用示例

    下面两种方式都可以起到设置_xsrf Cookie 的作用

    class XSRFTokenHandler(RequestHandler): """专门用来设置_xsrf Cookie 的接口""" def get(self): self.xsrf_token # 设置浏览器中的_xsrf 的 cookie 值 self.write("Ok") class StaticFileHandler(tornado.web.StaticFileHandler): """重写 StaticFileHandler,构造时触发设置_xsrf Cookie""" def __init__(self, *args, **kwargs): super(StaticFileHandler, self).__init__(*args, **kwargs) self.xsrf_token
  • 对于请求携带_xsrf 参数,有两种方式

    • 若请求体是表单编码格式的,可以在请求体中添加_xsrf 参数
    • 若请求体是其他格式的(如 json 或 xml 等),可以通过设置 HTTP 头 X -XSRFToken 来传递_xsrf 值
  • 请求体携带_xsrf 参数
    新建一个页面 xsrf.html

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>测试 XSRF</title> </head> <body> <a href="javascript:;" onclick="xsrfPost()">发送 POST 请求</a> <script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> <script type="text/javascript"> // 获取指定 Cookie 的函数 function getCookie(name) {var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); return r ? r[1] : undefined; } //AJAX 发送 post 请求,表单格式数据 function xsrfPost() {var xsrf = getCookie("_xsrf"); $.post("/new", "_xsrf="+xsrf+"&key1=value1", function(data) {alert("OK"); }); } </script> </body> </html>

    注意:

    • 需要先设置 csrf 才能使用 ajax 携带进行访问

    视图函数 get 添加如下代码

    def get(self): self.xsrf_token # 设置 csrf self.render('test_xsrf.html')
  • HTTP 头 X -XSRFToken
    新建一个页面 json.html

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>测试 XSRF</title> </head> <body> <a href="javascript:;" onclick="xsrfPost()">发送 POST 请求</a> <script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script> <script type="text/javascript"> // 获取指定 Cookie 的函数 function getCookie(name) {var r = document.cookie.match("\\b" + name + "=([^;]*)\\b"); return r ? r[1] : undefined; } //AJAX 发送 post 请求,json 格式数据 function xsrfPost() {var xsrf = getCookie("_xsrf"); var data = {key1:1, key1:2 }; var json_data = JSON.stringify(data); $.ajax({url: "/new", method: "POST", headers: {"X-XSRFToken":xsrf, }, data:json_data, success:function(data) {alert("OK"); } }) } </script> </body> </html>

正文完
星哥说事-微信公众号
post-qrcode
 
星锅
版权声明:本站原创文章,由 星锅 2022-05-26发表,共计4591字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中