9

SQLAlchemy因密码含有@符号连接MySQL失败

 2 years ago
source link: https://zhang.ge/5167.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

SQLAlchemy因密码含有@符号连接MySQL失败 | 张戈博客

Jager · 8月3日 · 2022年 173次已读

最近新来的实习生小伙子在重构一个我入职时开发的老系统,这个系统之前用的是 Python twisted 框架开发的,这次重构希望可以换到 FastAPI,也就是我上一篇文章分享的Flyer 开发框架

小伙子很给力,花了几天自学了下 FastAPI 和 Flyer 就开始开发了,结果用 Flyer 框架的时候,连接 MySQL 报错如下:

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on '[email protected]' ([Errno -2] Name or service not known

这个报错他一看也就明白,是因为密码里面有一个@符号,导致@符号之后的内容被识别为数据库的连接主机了,小伙子很快就基于 urllib.parse.quote_plus 对密码转义一下就解决了,特意跟我说了下这个问题。

因为我之前有个基于 Flyer 框架的项目也复用了这个数据库用的是同样的账号和密码,没出现过这个问题,所以很奇怪,比较笃定的说肯定是 sqlalchemy 版本问题。

结果一看,他的开发环境的 sqlalchemy 版本是 1.4.39 而我的开发环境还是 1.3.19,让他回退版本测试了下,果然就修复了,看来是 1.3.19 后面的版本引入的‘特性’。

出于对开源社区的反哺,我到 sqlalchemy 的社区提了一个 issue:

can not connect to mysql when has special chars '@' in passwords · Issue #8327 · sqlalchemy/sqlalchemy (github.com)

结果社区大佬响应挺快,说这个是预期的,需要我们将密码做一下 url 转义。

好吧,这个 url 转义肯定是可以做的,也算合理,只是我比较疑惑的是主机里面并没有@符号啊,为啥密码里面不能用@符号呢?特意翻看了下版本发布记录,最终发现这个特性的修改是在 1.4.16 版本:

accommodate for @ in database portion even if user:pass is present · Issue #6482 · sqlalchemy/sqlalchemy (github.com)

[engine] [bug] Fixed issue where an @ sign in the database portion of a URL would not
be interpreted correctly if the URL also had a username:password section.

为了修复 db 实例名称里面可能会有@符号,导致@符号之前的字符都归为密码,说白了和这篇文章说的问题差不多,就是参数提取越界的问题,只是我没想到 db 实例里面还可能会有@符号...

好奇看了下两个版本的差异:

https://github.com/sqlalchemy/sqlalchemy/blob/8a9a2e384db2d4b9fedf8d3b62dbf8937b7ba2b8/lib/sqlalchemy/engine/url.py#L722

https://github.com/sqlalchemy/sqlalchemy/blob/627135e4239c5f1a073b7c21e4958087c4b0e0a0/lib/sqlalchemy/engine/url.py#L705

# 1.4.15
(?::(?P<password>.*))
# 1.4.16
(?::(?P<password>[^@]*))

发现其实就是改了提取密码这个正则表达式,将@符号从密码里面排除了,即密码里面不能有@符号,如果有请自行转义。

从这个小 case 我们可以总结两点经验:

1、使用 url 形式来连接各类服务时(MySQL、pgSQL、MongoDB 等),密码要做好 url 转义以提高程序健壮性;

2、Python 环境中安装和使用开源插件,最好指定实际测试过的版本号,且不要随意修改,防止各种 break 特性的引入。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK