Flask相关知识学习 需要的前置知识有Python基础和前端老三样基础,以及Linux的基础 这些我都没什么问题,要是学习过程中遇到什么奇奇怪怪的东西,到时候再去学习也是可以的
全过程我都会用wsl来操作
前置操作 ==我曾经的日常操作,但是因为太久没操作导致生疏?那不妨好好记录一下==
1 2 3 4 5 6 caochuhan@DESKTOP-B9Q8MAA:/mnt/f/watchlist$ python3 -m venv env caochuhan@DESKTOP-B9Q8MAA:/mnt/f/watchlist$ .env /bin/activate -bash: .env /bin/activate: No such file or directory caochuhan@DESKTOP-B9Q8MAA:/mnt/f/watchlist$ . env /bin/activate (env ) caochuhan@DESKTOP-B9Q8MAA:/mnt/f/watchlist$ deactivate caochuhan@DESKTOP-B9Q8MAA:/mnt/f/watchlist$
python3直接调用内置的venv模块,然后==. env/bin/activate==激活,要退出的话就直接deactivate
我操,我这虚拟环境没自带pip,破防了,还得自己装
OK,装flask的过程中还是遇到了不测,Ubuntu的通病
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 (env ) caochuhan@DESKTOP-B9Q8MAA:/mnt/f/watchlist$ pip install Flask error: externally-managed-environment × This environment is externally managed ╰─> To install Python packages system-wide, try apt install python3-xyz, where xyz is the package you are trying to install. If you wish to install a non-Debian-packaged Python package, create a virtual environment using python3 -m venv path/to/venv. Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make sure you have python3-full installed. If you wish to install a non-Debian packaged Python application, it may be easiest to use pipx install xyz, which will manage a virtual environment for you. Make sure you have pipx installed. See /usr/share/doc/python3.12/README.venv for more information. note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages. hint: See PEP 668 for the detailed specification.
没话说,查查怎么解决
最后还是屈服了,使用pipx(很奇怪,难道是我虚拟环境的创建过程有误才导致了这种结果?) 而且pipx装的flask库根本不在虚拟环境中,而是在我的bin目录下! 教程有问题,我重新解决这个问题
成功解决,接下来我来说一下我的方法
自打ubuntu24面世以来,就有pip install会产生上述报错的问题,但是我很奇怪的是,我明明启用了venv,按道理来说是不会有报错的 ==除非,我的pip不是虚拟环境中的pip,而是我本地bin目录下的pip== 验证
果然被做局了 因此只能重新创建一个虚拟环境,这个虚拟环境得自带pip
安装系统的 venv 支持
1 2 sudo apt updatesudo apt install python3-full python3-venv
删除旧环境
重建虚拟环境
激活并安装 Flask
1 2 3 source env /bin/activatepython -m pip install --upgrade pip pip install flask
验证
1 2 which pip/mnt/f/watchlist/env/bin/flask
终于解决问题了
helloFlask 简单的引导 1 2 3 4 5 6 7 from flask import Flaskapp=Flask(__name__) @app.route('/' ) def Hello (): return 'Welcome to my watchlist'
很容易理解,搞web的基本上不用看就能知道是什么意思 导入Flask类,然后通过实例化这个类,创建了对象app
然后外面直接==注册==一个处理函数,用于处理某个请求(官方叫做视图函数),其实就是一个请求处理函数 所谓注册 ,就是给这个函数戴上一个装饰器帽子。我们使用 app.route() 装饰器来为这个函数绑定对应的 URL,当用户在浏览器访问这个 URL 的时候,就会触发这个函数,获取返回值,并把返回值显示到浏览器窗口
填入 app.route() 装饰器的第一个参数是 URL 规则字符串,这里的 /指的是根地址。 我们只需要写出相对地址,主机地址、端口号等都不需要写出。所以说,这里的 / 对应的是主机名后面的路径部分,完整 URL 就是 http://localhost:5000/。如果我们这里定义的 URL 规则是 /hello,那么完整 URL 就是 http://localhost:5000/hello。
管理环境变量 在启动flask的时候,通常是和FLASK_APP,FLASK_DEBUG这两个环境变量打交道,陈旭名为app.py,所以暂时可以不去动FLASK_APP FLASK_DEBUG,老演员了,不知道见过多少次了,什么什么计算pin码,进去任意python代码执行,都和这个有关
这里安装用来自动导入系统环境变量的python-dotenv 主要是读取.env文件还有.flaskenv文件,这里我直接touch创建,然后.env不管它,直接在.flaskenv文件中写入
1 2 3 4 5 6 7 8 9 10 from flask import Flaskapp=Flask(__name__) @app.route('/' ) def Hello (): return '<h1>Hello Totoro!</h1><img src="http://helloflask.com/totoro.gif">' @app.route('/home' ) def home (): return 'zhubi'
自己随意添加处理函数和它的修饰器,这个也很简单,就不多说什么
==url中自定义变量部分== 例如:
1 2 3 @app.route('/user/<name>' ) def user_page (name ): return 'User page'
这里的name就作为了变量,user_page这个函数会处理所有类似”/user/name”的操作 例如/user/eminem,/user/kwansh,都会触发这个函数。那就很自然的有一个想法,把这个变量自然的放在了操作函数中 接下来会学习有关escape操作,没进行这个操作的话恐怕是会被rce哩
1 2 3 4 5 from markupsafe import escape@app.route('/user/<name>' ) def user_page (name ): return f'User: {escape(name)} '
用户输入的数据会包含恶意代码,所以不能直接作为响应返回,需要使用 MarkupSafe(Flask 的依赖之一)提供的 escape() 函数对 name 变量进行转义处理,比如把 < 转换成 <。这样在返回响应时浏览器就不会把它们当做代码执行。 那我接下来打打看 确实打通了,可以执行rce,大家要是有兴趣的话可以自己试试 本地起的flask
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from flask import Flaskfrom markupsafe import escapeimport osapp=Flask(__name__) @app.route('/' ) def Hello (): return '<h1>Hello Totoro!</h1><img src="http://helloflask.com/totoro.gif">' @app.route('/home' ) def home (): return 'zhubi' @app.route('/user/<path:name>' ) def user_page (name ): return eval (f"f'{name} '" )
攻击payload
1 http://localhost:5000/user/%7B__import__('os' ).popen('cat%20app.py' ).read ()%7D
结果图 当然这里是我用了eval的原因,这里eval后面要是跟着escape的话,我打这个payload上去就直接给我干出debug了
1 2 3 @app.route('/user/<path:name>' ) def user_page (name ): return eval (f"User: {escape(name)} " )
大家也可以去试试,也可以练练计算pin码
修改视图函数名? 视图函数的名字是自由定义的,和 URL 规则无关。和定义其他函数或变量一样,只需要让它表达出所要处理页面的含义即可。
然后就是一个flask的url_for函数来生成url(有空我去看看这个库,指不定有点0day?),然后它接受的第一个参数就是端点值,默认为操作函数的名称
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from flask import url_forfrom markupsafe import escape@app.route('/' ) def hello (): return 'Hello' @app.route('/user/<name>' ) def user_page (name ): return f'User: {escape(name)} ' @app.route('/test' ) def test_url_for (): print (url_for('hello' )) print (url_for('user_page' , name='greyli' )) print (url_for('user_page' , name='peter' )) print (url_for('test_url_for' )) print (url_for('test_url_for' , num=2 )) return 'Test page'
大概测试一下这个exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 127.0.0.1 - - [20/Jun/2025 13:50:24] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [20/Jun/2025 13:50:24] "GET /favicon.ico HTTP/1.1" 404 - 127.0.0.1 - - [20/Jun/2025 13:50:36] "GET /user/eminem HTTP/1.1" 200 - 127.0.0.1 - - [20/Jun/2025 13:50:48] "GET /user/greyli HTTP/1.1" 200 - 127.0.0.1 - - [20/Jun/2025 13:51:11] "GET /user/hello HTTP/1.1" 200 - / /user/greyli /user/peter /test /test?num=2 127.0.0.1 - - [20/Jun/2025 13:51:23] "GET /test HTTP/1.1" 200 - / /user/greyli /user/peter /test /test?num=2 127.0.0.1 - - [20/Jun/2025 13:51:41] "GET /test?num=2 HTTP/1.1" 200 - / /user/greyli /user/peter /test /test?num=2 127.0.0.1 - - [20/Jun/2025 13:51:44] "GET /test?num=2 HTTP/1.1" 200 -
你应该就能知道是什么意思了第一课结束
模板 这次学习的主要是Jinja2模板(fenjing狂喜) 基于flask的SSTI就爱打jinja2
我们把包含变量和运算逻辑的 HTML 或其他格式的文本叫做模板,执行这些变量替换和逻辑计算工作的过程被称为渲染,这个工作由我们这一章要学习使用的模板渲染引擎——Jinja2 来完成。 按照默认的设置,Flask 会从程序实例所在模块同级目录的 templates 文件夹中寻找模板,我们的程序目前存储在项目根目录的 app.py 文件里,所以我们要在项目根目录创建这个文件夹
模板基本语法 教程教的 基本老三样
1 2 3 "{{...}}" 用来标记变量,也可直接输出运算的结果"{%...%}" 用来标记语句,例如if else "{#...#}" 用来写注释
exp
1 2 3 4 5 6 <h1>{{ username }}的个人主页</h1> {% if bio %} <p>{{ bio }}</p> { {% else %} <p>自我介绍为空。</p> {% endif %} {
=={{...}}== 有它自己的用处,可以直接判断有没有 SSTI 注入点
1 2 3 4 {{ 1 + 2 }} {# 输出 3 {{ user.age * 2 }} {# 输出用户年龄乘以 2 {{ 'Hello ' ~ user }} {# 字符串拼接,用 ~ 运算符 {{ list|length }} {# 使用 filter(见下文)求长度
我自学的 做安全的话,仅仅只了解上面的东西是绝对不够用的
过滤器
过滤器(Filter)是对变量进行后处理的函数,以管道符 | 连接在变量或表达式之后
1 {{ variable | filter1(arg1, arg2) | filter2 }}
过滤器可以用内置的,也可以自己定义 exp
1 2 3 4 5 6 7 8 9 10 11 { <ul> {% for name in users |selectattr('enabled' ) |sort(attribute='joined_at' , reverse=True ) |map ('username' ) |list |slice (0 ,5 ) %} <li>{{ name }}</li> {% endfor %} </ul>
和php://filter的那个过滤器有异曲同工之妙,都差不多
测试 测试用于布尔判断,语法是 value is test 或 value is not test。常用测试:
测试
功能
defined
是否已定义
undefined
是否未定义
none
是否为 None
even
/odd
是否为偶数 / 奇数
in
是否在序列或映射中
exp
1 2 3 4 5 6 7 {% if user.age is even %} 年龄是偶数 {% endif %} {% if item is not defined %} 该变量不存在 {% endif %}
编写主页模板 接下来像做项目一样一步步来,学习flask的应用逻辑和底层原理
要在templates/index.html做为主页模板,罗列一些信息,这里就直接借鉴教程的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="utf-8" > <title > {{ name }}'s Watchlist</title > </head > <body > <h2 > {{ name }}'s Watchlist</h2 > {# 使用 length 过滤器获取 movies 变量的长度 #} <p > {{ movies|length }} Titles</p > <ul > {% for movie in movies %} {# 迭代 movies 变量 #} <li > {{ movie.title }} - {{ movie.year }}</li > {# 等同于 movie['title'] #} {% endfor %} {# 使用 endfor 标签结束 for 语句 #} </ul > <footer > <small > © 2018 <a href ="http://helloflask.com/book/3" > HelloFlask</a > </small > </footer > </body > </html >
这里也直接介绍了过滤器 介绍的也比较简单,要是上面的看不懂的话可以看下面的这个
https://jinja.palletsprojects.com/en/3.0.x/templates/#builtin-filters 访问可查看所有的可用的过滤器
准备虚拟数据 这个很简单,在app.py中定义一下就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 name = 'Grey Li' movies = [ {'title' : 'My Neighbor Totoro' , 'year' : '1988' }, {'title' : 'Dead Poets Society' , 'year' : '1989' }, {'title' : 'A Perfect World' , 'year' : '1993' }, {'title' : 'Leon' , 'year' : '1994' }, {'title' : 'Mahjong' , 'year' : '1996' }, {'title' : 'Swallowtail Butterfly' , 'year' : '1996' }, {'title' : 'King of Comedy' , 'year' : '1999' }, {'title' : 'Devils on the Doorstep' , 'year' : '1999' }, {'title' : 'WALL-E' , 'year' : '2008' }, {'title' : 'The Pork of Music' , 'year' : '2012' }, ]
渲染主页模板 模板渲染函数之 render_template()
使用 render_template() 函数可以把模板渲染出来,必须传入的参数为模板文件名(相对于 templates 根目录的文件路径),这里即 ‘index.html’。为了让模板正确渲染,我们还要把模板内部使用的变量通过关键字参数传入这个函数(想想你index.html都定义了什么变量) exp
1 2 3 @app.route('/' ) def index (): return render_template('index.html' , name=name, movies=movies)
render_template() 函数在调用时会识别并执行 index.html 里所有的 Jinja2 语句,返回渲染好的模板内容。在返回的页面中,变量会被替换为实际的值(包括定界符),语句(及定界符)则会在执行后被移除(注释也会一并移除)。
然后直接跑跑看 效果很成功,把定义的键值对的值都输出了(这个也是实现在index模板中写好的,只输出值)
第二课结束
静态文件 这个类比hexo博客搭建过程中的图片添加,会更加容易理解
静态文件(static files)和我们的模板概念相反,指的是内容不需要动态生成的文件。比如图片、CSS 文件和 JavaScript 脚本等。 在 Flask 中,我们需要创建一个 static 文件夹来保存静态文件,它应该和程序模块、templates 文件夹在同一目录层级,所以我们在项目根目录创建它:
你类比成在source目录下创建image文件夹来放置图片即可