11. 预解析Statement
Introduction
在看一个注入错误的时候发现有一招叫Statement, 想起来当时看驱动的时候也总是看到stmt
字样, 看多了觉得鬼畜, 老样子从场景入手, 现在假设你想查: select * from users where id = 5
, 那么在你的程序里你就会这么写:
过两天安全部找你, 说这里有注入风险, 让你给修一下, 你看了一下决定过段时间再说, 3个月过去了安全工单还在那里(
SQL语句解析
Statement中文翻译叫"参数化查询", 烂翻译(不意外), 我叫它"预解析", 我们都知道SQL语句到达MySQL服务器经过的第一层是语句解析, 任何语句都需要经过语义解析才能开始被执行, 一条语句会被拆成很多很多个小块, 每个小块都有一个自己的含义, 比如你想要执行的动作, 想要查询的表/库, 想要的字段.
比如在我们上面的例子中你想要执行的动作就是SELECT, 你想要查询的表是users, 筛选条件是id=5, 就这么些小块, 拆好了以后这些小块最终会被送给引擎层, 去磁盘里查你想要的数据.
预解析为什么防范注入
所谓预解析, 就是select * from users where id=?
这个语句解析后产生的小块, 经过预解析后的语句, 明确要求, 这里应该是一个数字, 表示id. 而SQL注入呢? 会填进来一个超级复杂的语句, 根本不符合解析过的语句要求, 自然是报错的.
这种行为很像一个"模板", 一个模板要求你这里只能填数字, 你填写一个复杂语句并不符合模板要求, 虽然注入语句整体是通顺的, 但并不符合模板要求
看看b(指包,抓包看看)
模板生成过程
这条语句执行以下会让MySQL服务器做以下事情: 帮我预解析一下这个模板, 然后服务器收到了以后会生成一个StatementID
模板使用过程
然后你通过这条语句, 开始利用刚刚定义出的模板, 传递一个参数225, 告诉MySQL服务器, 我要使用刚刚的模板, 这里的参数是225
Statement一定比直接查询要好吗?
你也看见了如果使用模板查询, 第一步是先生成模板, 然后使用模板, 最后还要关闭模板, 回收这个ID, 比较麻烦
除此之外Statement有一种问题叫reprepare问题, 就是你使用stmt的链接对象, 跟你创建的链接对象必须是同一条链接对象. 怎么说呢? 你知道你的go-sql-driver
会为你维护一个连接池,
你通过连接对象-A创建了stmt, 稍后你想要使用这个链接对象的时候, 我们必须再把A找回来, 如果A忙, 那就要重新创建一遍, 加大成本, 叫reprepare.
因此只在面临用户输入参数的时候使用Statment就好了, 剩余的时候不用这么麻烦的
Last updated
Was this helpful?