在过去的工作中,我为我们的服务维护了一个允许临时查询的设施。它允许管理人员请求报告,而无需等待数周的代码部署(在部署需要数周时间的古怪日子)。
我们不通过让服务接受任意字符串作为输入并将它们作为 SQL 执行来支持临时报告查询。这非常不安全,我相信你知道。
它的工作方式是将报告查询以及所需的查询参数数量存储在数据库中。
CREATE TABLE ManagerQueries (
id INT PRIMARY KEY,
query TEXT NOT NULL,
description TEXT NOT NULL,
num_params TINYINT UNSIGNED NOT NULL DEFAULT 0
);
INSERT INTO ManagerQueries
SET query = 'SELECT COUNT(*) FROM logins WHERE user_id = {0} AND created_at > {1}',
description = 'Count a given user logins since a date',
num_params = 2;
管理器前端可以通过其主键请求查询,而不是通过在 Web 请求中指定任意 SQL 字符串。
只有 DBA 以及可能知道如何编写安全查询的其他开发人员或管理人员才能向此存储库添加新查询,因此可以确保查询已经过测试和审查。
当通过 UI 请求报告查询时,它会强制用户提供查询参数的值。在我们的例子中,它从数据库中读取 SQL,执行prepare(),然后绑定execute() 的值。这样SQL注入防御就满足了。
在您的情况下,您的代码可能无法直接访问旧服务的数据库,因此您无法进行准备/执行和使用绑定参数。您必须提交包含集成值的静态查询。
在其他语言中,您可以通过转义使任何字符串值安全地插入到 SQL 查询中。请参阅 MySQL C API 函数mysql_real_escape_string()。
数值更容易。您无需转义任何内容,只需确保数值是真正的数字即可。将动态值转换为数字后,可以安全地插回任何 SQL 字符串。
不幸的是,我认为 golang SQL 包不支持任何转义功能。这已被要求作为一项功能,但据我所知,尚无支持的实现。请参阅此处的讨论:https://github.com/golang/go/issues/18478
因此您可能必须实现自己的转义功能。例如,您可以在官方 MySQL C 实现之后对其进行建模:https://github.com/mysql/mysql-server/blob/8.0/mysys/charset.cc#L716
请注意,这比仅使用正则表达式替换有点棘手,因为您需要考虑多字节字符集。