csrf跨站请求伪造

csrf跨站请求伪造

1、前戏

"""
钓鱼网站
	搭建一个跟正规网站一摸一样的界面(中国银行)
	用户进入到我们的网站,用户给某人打钱,
	用户打钱操作确确实实是提交给了中国银行的系统,用户的钱也确确实实减少了,但是唯一不同是打钱的账户不是用户想要打的账户,变成了另一个账户

内部本质:
	在钓鱼网站的页面,针对对方账户,只给用户提供一个没有name属性的input框,然后我们再内部隐藏一个已经写好name和value的input框,
		
"""
  • 真正的网站端口:8000

http://127.0.0.1:8000

# 注释掉csrf
 'django.middleware.csrf.CsrfViewMiddleware',
<body>
<h1>中国银行</h1>
<form action="" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
</body>
# views.py
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s给%s转了%s元' % (username, target_user, money))

    return render(request, 'transfer.html')

# urls.py
path('transfer/',views.transfer)
  • 钓鱼网站模拟端口:8001
#不用注释csfr
<body>
<h1>phishing site</h1>
<form action="http://127.0.0.1:8000/transfer/" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text"></p>
    <input type="text" name="target_user" value="zhao" style="display: none">
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
</body>
#views.py
def transfer(request):
    return render(request,'transfer.html')

# urls.py
path('transfer/',views.transfer)

2、csrf校验

如何规避上述问题:
csrf跨站请求伪造
网站再给用户返回一个提交数据功能的页面的时候会给这个页面加一个唯一标识
当这个页面朝后端发post请求的时候,后端会先校验唯一标识,如果唯一标识不对,直接拒绝(403 forbiden),如果成功则正常执行

2.1、from表单如何符合校验

#开启配置文件里的csrf中间件
<form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>

image-20221111105106883

再次发送请求

image-20221111105127536

钓鱼网站再次发送请求

image-20221111105520008

2.2、ajax如何符合校验

不论是ajax还是谁,只要是向我Django提交post请求的数据,都必须校验csrf_token来防伪跨站请求

  • 方式一

通过获取隐藏的input标签中的csrfmiddlewaretoken值,放置在data中发送

</head>
<body>
<h1>中国银行</h1>
<form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
<button id="d1">ajax请求</button>
</body>


<script>
    $('#d1').on('click', function () {
        $.ajax({
            url: '',
            type: 'post',
            //第一种:利用标签查找获取页面上的随机字符串
            data: {'username': 'zhao', 'csrfmiddlewaretoken': $('[name=csrfmiddlewaretoken]').val()},
            success() {

            }

        })

    })
</script>
  • 方式二
</head>
<body>
<h1>中国银行</h1>
<form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
<button id="d1">ajax请求</button>
</body>

<script>
    $('#d1').on('click', function () {
        $.ajax({
            url: '',
            type: 'post',
            //第二种方式:利用模板语法快捷书写
            data: {'username': 'zhao', 'csrfmiddlewaretoken': '{{csrf_token}}'},
            }
        })
    })
</script>
  • 方式三

先拷贝js文件:

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var csrftoken = getCookie('csrftoken');


function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
    beforeSend: function (xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

将文件配置到静态文件中,在html页面上通过导入该文件即可自动帮我们解决ajax提交post数据时校验csrf_token的问题

image-20221111112430616

</head>
<body>
<h1>中国银行</h1>
<form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>
<button id="d1">ajax请求</button>
</body>


{% load static %}
<script src="{% static  'js/setup.js' %}"></script>
<script>
    $('#d1').on('click', function () {
        $.ajax({
            url: '',
            type: 'post',
            //第三种方式:引入js文件
            data: {'username': 'zhao'},
            success() {

            }

        })

    })
</script>

3、csrf相关装饰器

3.1、FBV

  • 网站整体都校验csrf,就单单几个视图函数不校验
#配置文件中开始csrf中间件,
from django.views.decorators.csrf import csrf_protect, csrf_exempt

"""
csrf_protect,需要校验
csrf_exempt  忽视校验
"""

@csrf_exempt 
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s给%s转了%s元' % (username, target_user, money))

    return render(request, 'transfer.html')
  • 网站整体都不校验csrf,就单单几个视图函数需要校验
# 注释掉form表单中 {% csrf_token %}#}
#关闭配置文件中csrf中间件

@csrf_protect
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s给%s转了%s元' % (username, target_user, money))

    return render(request, 'transfer.html')

image-20221111141146200

3.2、CBV

csrf_protect,需要校验
​	针对csrf_protect符合CBV装饰器的三种写法

csrf_exempt  忽视校验
​	针对csrf_protect只能给dispatch方法加才有效
  • 网站整体都不校验csrf,就单单几个视图函数需要校验
# views.py
from django.utils.decorators import method_decorator
from django.views import View


# @method_decorator(csrf_protect,name='post') #第二种
class MycsrfToken(View):
    @method_decorator(csrf_protect)
    def dispatch(self, request, *args, **kwargs):
        return super(MycsrfToken, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return HttpResponse('get')

    # @method_decorator(csrf_protect) #第一种方式可以
    def post(self, request):
        return HttpResponse('post')
    
    
# urls.py
 path('csrf/', views.MycsrfToken.as_view()),
# html  from表单向csrf路由提交
<h1>中国银行</h1>
<form action="/csrf/" method="post">
{#    {% csrf_token %}#}
    <p>username:<input type="text" name="username"></p>
    <p>target_user:<input type="text" name="target_user"></p>
    <p>money:<input type="text" name="money"></p>
    <input type="submit">
</form>


    
# urls.py
path('transfer/', views.transfer),

# views.py
def transfer(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        target_user = request.POST.get('target_user')
        money = request.POST.get('money')
        print('%s给%s转了%s元' % (username, target_user, money))

    return render(request, 'transfer.html')
  • 网站整体都校验csrf,就单单几个视图函数不校验
from django.utils.decorators import method_decorator
from django.views import View



# @method_decorator(csrf_exempt,name='dispatch') 
class MycsrfToken(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(MycsrfToken, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return HttpResponse('get')

 
    def post(self, request):
        return HttpResponse('post')
posted @ 2022-12-12 16:57  ExpiredSaury  阅读(22)  评论(0编辑  收藏  举报