- 举个例子
- 假设受害者在一家网银网站上登录账户,然后继续浏览其他网页。
- 同时,攻击者通过电子邮件等方式向受害者发送了一封包含恶意链接的邮件。
- 当受害者点击该链接时,潜在的威胁就会变得非常现实。
- 该链接指向一个由攻击者操纵的网站,该网站上的恶意代码会自动向网银网站发送一个请求,请求转账到攻击者的账户。
- 由于受害者在网银网站中已经登录,所以该请求会被认为是合法的,这样攻击者就可以成功地进行转账操作。
前端
这是正规的网站
后端
def transform_normal(request):
if request.method == "POST":
user_start = request.POST.get("start_user")
user_end = request.POST.get("end_user")
money = request.POST.get("money")
return HttpResponse(f"当前账户 :>>> {user_start} 向目标用户 :>>> {user_end} 转账了 :>>> {money}")
return render(request, 'transform_normal.html')
这是钓鱼的网站
def transform_normal(request):
if request.method == "POST":
user_start = request.POST.get("start_user")
user_end = request.POST.get("end_user")
money = request.POST.get("money")
return HttpResponse(f"当前账户 :>>> {user_start} 向目标用户 :>>> {user_end} 转账了 :>>> {money}")
return render(request, 'transform_normal.html')
介绍
csrf校验是一种用于防止跨站请求伪造(Cross-Site Request Forgery)攻击的安全措施
添加CSRF Token字段:
在form表单中添加一个隐藏字段,用于存储CSRF Token的值。
后端服务器在渲染表单时生成一个CSRF Token,并将其存储在会话中或者以其他方式关联到当前用户。
当用户提交表单时,前端将CSRF Token的值包含在请求中。
后端在验证表单数据时,检查请求中的CSRF Token是否与存储的Token匹配,如果不匹配,则拒绝请求。
设置Cookie:
后端服务器在渲染表单时,在客户端设置一个包含随机生成的CSRF Token的Cookie。
当用户提交表单时,表单数据会被一同发送到服务器,并自动包含该Cookie。
后端在验证表单数据时,检查请求中的CSRF Token是否与Cookie中的值匹配,如果不匹配,则拒绝请求。
双重Cookie校验:
后端服务器在渲染表单时,在Cookie中设置一个随机生成的CSRF Token,并将其存储在会话中或以其他方式关联到当前用户。
当用户提交表单时,表单数据会被一同发送到服务器,请求头或请求参数中携带一个包含CSRF Token的自定义字段。
后端在验证表单数据时,同时检查请求中的CSRF Token和Cookie中的值是否匹配,如果不匹配,则拒绝请求。
在form表单上面加上csrf_token
在页面标签中会自动出现一个标签
方式一
利用标签查找获取页面上的随机字符串
键必须叫 csrfmiddlewaretoken
方式二
利用模板语法进行快捷引入
方式三
定义一个js文件并引入
导入该配置文件之前,需要先导入jQuery,因为这个配置文件内的内容是基于jQuery来实现的
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);
}
}
});
importlib 模块是 Python 中用于动态加载和导入模块的内置模块。
它提供了一组函数和类,使得我们可以在运行时根据需要加载模块,并且可以对已导入的模块进行操作和管理。
scripts/my_package/my_func.py
name = 'Dream'
scripts/test.py
from my_package import my_func
print(my_func.name) # Dream
import importlib
module_name = 'my_package.my_func'
model_f = importlib.import_module(module_name)
print(model_f)
print(model_f.name)
最小单位,只能到模块名
importlib 模块提供了 import_module
函数
通过该函数可以在运行时动态加载一个模块。
示例代码如下:
# 导入模块 : 动态加载一个模块
import importlib
# 定义模块的名字
module_name = 'math'
# 动态导入模块
math_module = importlib.import_module(module_name)
importlib 模块还提供了 import_module
函数的变体 import_module
它可以直接返回指定模块的成员。
示例代码如下:
# 导入模块 : 动态加载一个模块
import importlib
# 定义模块的名字
module_name = 'math'
# 动态导入模块
math_module = importlib.import_module(module_name)
# 从模块中获取相关的值
sqrt_func = getattr(math_module, 'sqrt')
print(sqrt_func) #
在开发过程中,我们有时需要重新加载一个已经导入的模块,以便应用最新的修改。
importlib 模块提供了 reload
函数来实现这个功能。
示例代码如下:
import importlib
# 要重新加载的模块名
module_name = 'my_module'
# 加载模块
my_module = importlib.import_module(module_name)
# 重新加载模块
my_module = importlib.reload(my_module)
通过 sys.modules
可以获取当前已导入的所有模块的字典
其中键为模块名称,值为模块对象。
以下示例演示如何遍历已导入的模块列表:
import sys
for module_name, module in sys.modules.items():
print(module_name, module)
我们在Django的配置文件中,里面的中间件配置文件,虽然使用逗号分开,但是可以做到直接引入某个模块
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
这种路径构造方式,我们就可以通过importlib模块实现
定义一个包
# -*-coding: Utf-8 -*-
# @File : send_message .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
def wechat(content):
print(f"wechat接收到的通知:>>>{content}")
def QQ(content):
print(f"QQ接收到的通知:>>>{content}")
def email(content):
print(f"email接收到的通知:>>>{content}")
启动文件中启动包
# -*-coding: Utf-8 -*-
# @File : start .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
from send_message import *
def send_all(content):
wechat(content=content)
QQ(content=content)
email(content=content)
if __name__ == '__main__':
wechat接收到的通知:>>>这是一条测试信息
QQ接收到的通知:>>>这是一条测试信息
email接收到的通知:>>>这是一条测试信息
(1)功能部分
先分别创建不同的消息功能文件
在同一个文件夹下
创建三个功能文件
# -*-coding: Utf-8 -*-
# @File : WeChat .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
class WeChat(object):
def __init__(self):
# 发送消息前的准备工作
# 比如掉接口/初始化配置等
pass
def send(self, content):
print(f"WeChat 发送的消息 :>>>{content}")
# -*-coding: Utf-8 -*-
# @File : QQ .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
class QQ(object):
def __init__(self):
# 发送消息前的准备工作
# 比如掉接口/初始化配置等
pass
def send(self, content):
print(f"QQ 发送的消息 :>>>{content}")
# -*-coding: Utf-8 -*-
# @File : email .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
class email(object):
def __init__(self):
# 发送消息前的准备工作
# 比如掉接口/初始化配置等
pass
def send(self, content):
print(f"email 发送的消息 :>>>{content}")
在上面的文件内创建初始化文件
# -*-coding: Utf-8 -*-
# @File : __init__ .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
import settings
import importlib
def send_all(content):
# 拿到每一个包的路径
for path_str in settings.MODEL_LIST:
model_path, class_name = path_str.rsplit('.', maxsplit=1)
# model_path : model.email
# class_name : email
# (1)利用字符串导入模块
# models : 模块对象
models = importlib.import_module(model_path)
# (2)利用反射拿到类名
cls = getattr(models, class_name)
# (3)生成类的对象
obj = cls()
# (4)利用鸭子类型直接调用send发送消息
obj.send(content)
if __name__ == '__main__':
send_all('1')
(3)调用部分
在外部定义一个配置文件
# -*-coding: Utf-8 -*-
# @File : settings .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
MODEL_LIST = [
'model.email.email',
'model.QQ.QQ',
'model.WeChat.WeChat',
]
在外部的真正功能文件
# -*-coding: Utf-8 -*-
# @File : start .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/18
import model
model.send_all('这是测试消息')
email 发送的消息 :>>>这是测试消息
QQ 发送的消息 :>>>这是测试消息
WeChat 发送的消息 :>>>这是测试消息
(4)小结
遵从Python中的鸭子类型
可以在功能文件中自定义功能文件添加或者注释
在 settings.py
文件中相关的路径注释掉或添加上去即可