那天快下班的时候,我刚泡好一杯速溶,准备摸鱼看会儿剧,结果收同事小菜鸡发来的消息:
“姐,我写了个 Flask 小项目,直接放服务器上就可以了吧?”
我刚想说“可以”,但转念一想,这小子八成是啥都没配,直接 python app.py
就上线了……果然,一问,他是真·直接暴露在公网。
于是我一句话把他从美梦中拍醒:
“你这不是部署,是自杀。”
为什么你写的 Python 应用不安全?
我们先别说啥黑客、漏洞、攻击,咱们就说你平时写代码,有没有干过以下几件事:
.py
文件里,甚至上传了 GitHub?别不好意思,花姐我刚学 Python 那会儿,这些坑都一个个踩过,摔得鼻青脸肿。今天就来聊聊:怎么让你的 Python 项目不再裸奔。
我见过太多初学者,数据库账号密码直接写在代码里,看着贼方便:
# 警告:这代码真的不能上线
conn = psycopg2.connect(
host="localhost",
user="root",
password="123456",
dbname="my_db"
)
你这要是传到 GitHub,等着被扫库吧。解决办法其实很简单:用环境变量!
先创建 .env
文件(别忘了加到 .gitignore
):
DB_USER=root
DB_PASS=123456
然后用 python-dotenv
加载:
from dotenv import load_dotenv
import os
load_dotenv()
user = os.getenv("DB_USER")
password = os.getenv("DB_PASS")
是不是也不难?而且更灵活,部署的时候换环境都不用改代码!
花姐碎碎念: .env
文件千万别传上 GitHub,哪怕你说“我只给朋友看”,朋友的朋友可不会对你这么客气。
你写了个接口,别人的 curl 一下就能用?
@app.route('/delete_user', methods=['POST'])
def delete_user():
user_id = request.form['id']
# 直接删库
delete_user_by_id(user_id)
你可真大方。真不是我吓唬你,别人要是发现你这接口,直接扫一遍用户 ID,整站数据清空不是梦。
解决方案:
使用 Token 校验、Session、JWT,总之别裸奔!
比如最简单的 token 校验:
@app.route('/delete_user', methods=['POST'])
def delete_user():
token = request.headers.get('Authorization')
if token != 'super-secret-token':
return jsonify({'error': 'unauthorized'}), 401
# 安全地操作
当然,实际项目中建议用 Flask-JWT 或 Flask-Login,不然 token 乱发也是坑。
你是不是觉得 Flask 报错页面很贴心?红底白字,定位精确,一行一行源码全给你展示出来。
是的,对你很贴心,对黑客也很贴心。
举个例子:
你数据库连不上,页面报错:“OperationalError: FATAL: password authentication failed for user ‘root’”,
这信息已经够别人写个爆破脚本了。
正确做法:
@app.errorhandler(Exception)
def handle_error(e):
# 打日志可以,但展示给用户的要简洁
return jsonify({'error': 'Something went wrong, please try again later'}), 500
有一次我写了个头像上传功能,忘了检查文件类型,结果同事测试的时候,上传了个 .php
文件,服务器立马多了个“命令执行工具”……
如果你用的是 Flask,可以这样简单过滤:
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in {'jpg', 'jpeg', 'png'}
@app.route('/upload', methods=['POST'])
def upload_file():
file = request.files['file']
if file and allowed_file(file.filename):
# 保存文件操作
...
重点:
../../../tmp/hack.sh
可咋整。很多人一开始写项目,习惯全网 copy:
pip install flask flask_sqlalchemy flask_whatever
你知道你装的这些包里,有没有人早就偷偷加了后门?去年就爆出过 PyPI 上的“钓鱼包”,名字差一个字母,看都看不出来。
✅ 建议你这样做:
pip list --outdated
定期检查更新。pip-audit
工具自动扫一遍依赖里的漏洞。我当年实习的时候,给搜索框没加处理,结果上线第一天,有人输入了:
' OR 1=1 --
好家伙,直接把数据库翻了个底朝天。
哪怕你不是用 SQL,用户的输入也可能是个恶意脚本、XSS、路径穿越啥的,永远别相信用户输入!
比如:
@app.route('/search')
def search():
q = request.args.get('q')
# 不加转义直接渲染模板
return render_template('search.html', result=q)
你觉得这是搜索,别人当成跑 JavaScript 的地方了。
使用模板引擎自带的转义功能,别手写 HTML!
太棒了!花姐我看到你认真想做好内容,真开心~那我就接着来,咱们再补充几个实用又容易被忽视的安全坑(继续延续之前那种轻松接地气的风格,咱还是走“能听懂+真好用”的路线)
有时候,坑不是 Python 本身的,是你“用错了库”。
举个我亲身经历的例子:我那会儿写个爬虫,图省事,直接用 eval()
来解析 JSON,结果测试数据一多,有人提交了一个字符串:
__import__('os').system('rm -rf /')
差点就把服务器爆掉!我当时那脸色比刚踩完狗屎还难看。你说是不是能写还能炸?
避坑指南:
别用 eval()
来解析字符串,解析 JSON 用 json.loads()
!比什么都靠谱。
import json
data = '{"name": "huajie", "age": 18}'
parsed = json.loads(data)
debug=True
上线,等于给黑客开后门你是不是开发调试的时候,喜欢写:
app.run(debug=True)
没问题,很方便。但上线你还这么写?
恭喜你,这时候的 Flask 自带命令行远程执行能力,别人访问你的网站,只要找到漏洞就能执行系统命令,直接远程控制你的服务器!
怎么做:
# 上线环境记得设置为 False
app.run(debug=False)
如果你是用 Gunicorn + Nginx 之类部署,就压根别用 app.run()
,直接改成启动服务:
gunicorn -w 4 app:app
很多同学一开始把密码写死在代码里,后来意识到不对劲,删了,然后上传 GitHub,觉得自己干得漂亮。
BUT!你以为删了就没了吗?你只是删了现在的版本,历史记录还在!
Git 的 commit
记录一条不落地全存着,别人用 git log
和 git show
就能翻出来。
建议:
.gitignore
屏蔽 .env
、配置文件。git filter-branch
或 BFG Repo-Cleaner
)。我知道你写代码的时候可能会这么干
print("登录信息:", username, password)
甚至还写进日志:
logger.info(f"用户登录:{username},密码:{password}")
你自己调试看着爽,但这要是被别人拿到日志文件,那就是一锅端。
安全做法:
logger.info(f"用户 {username} 登录,密码已隐藏")
很多人写静态文件下载功能时这样写:
@app.route('/download/' )
def download(filename):
return send_from_directory('/uploads', filename)
你以为访问的是 abc.jpg
,别人用 ../../etc/passwd
就能直接下载你服务器配置文件……
路径遍历攻击,在 Web 安全里属于“万年经典款”。
解决方式:
secure_filename
检查文件名。../
之类的东西。from werkzeug.utils import secure_filename
@app.route('/download/' )
def download(filename):
filename = secure_filename(filename)
return send_from_directory('/uploads', filename)
很多 Flask/ Django 初学者写 POST 接口时没有做任何防护,结果别人随便写个网页,引导用户点一下,就偷偷发起请求,操作你的应用。
CSRF(跨站请求伪造)就是这么干的。你以为用户主动提交,其实是别人暗中操作。
最简单防护方式:
Flask-WTF
来自动管理这个功能。from flask_wtf import CSRFProtect
csrf = CSRFProtect(app)
自动为你的表单加上隐藏字段,提交时检查 token,非法来源直接拦截!
这篇文章其实不是为了吓你,而是想告诉你一个简单的事实:
写代码不是最难的,让代码安全可靠才是真挑战。
你可以写出天花乱坠的功能,但只要安全没做好,分分钟就翻车。像你花了两周开发的应用,可能别人五分钟就攻破了,那种感觉,真的想原地爆炸。
花姐也不是安全专家,但我想把自己这些年踩过的坑都告诉你,让你少走弯路。
顺手点赞+在看就是对花姐最大的支持!
你们的每一个点赞和留言,我都会认真看完的。别做孤独的搬砖侠,一起变强才是王道!