🚨您的 Python 应用程序不安全?别骗自己了!

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