如何在 golang 服务中最小化 SQL 注入下游服务的风险?
How to minimize risk of SQL injection in downstream services in a golang service?
我正在用 golang 编写一个半面向外部的 Web 服务,让用户可以查询有关其帐户的信息,这些信息分布在多个内部遗留服务中。
我的服务将用户输入的字符串传递到多个后端 RESTful API,这些 API 根据字符串进行 MySQL 查找以生成结果,这些结果将传递回我的服务以提供给用户。
从历史上看,这些遗留后端服务没有暴露给用户输入,所以我不确定它们是否有适当的防护措施来防止 SQL 注入。
通常我会阻止 SQL 注入 Prepared Statements 以防止数据库引擎将用户字符串视为可解析的,但在这种情况下我不控制数据库调用 - 它们'离下游很远,现在审计它们是不切实际的。
我可以在我的 golang 代码中做些什么来尽可能地清理用户输入以最小化 SQL 注入漏掉的风险?这最终是一个权宜之计,直到可以审计所有下游数据库调用的注入安全性。
编辑:出于所有实际目的,用户输入可以是任意字符串,但它不应该 是可执行代码。我的服务需要来自用户的字段值,而不是代码。
在过去的一份工作中,我为我们的服务维护了一个允许临时查询的工具。它允许管理人员请求报告,而无需等待数周的代码部署(回到部署需要数周的古怪日子)。
我们不通过使服务接受任意字符串作为输入并以 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注射防御就满意了
在您的情况下,您的代码可能无法直接访问遗留服务的数据库,因此您不能执行 prepare/execute 并使用绑定参数。您必须提交包含集成值的静态查询。
在其他语言中,您可以通过 转义 使任何字符串值安全地插值到 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
请注意,这比仅使用正则表达式替换要复杂一些,因为您需要考虑多字节字符集。
添加到比尔关于数据输入的回答中。为了确保输出数据的卫生,比如网络服务,请查看 html/template:
Package template (html/template) implements data-driven templates for
generating HTML output safe against code injection.
我正在用 golang 编写一个半面向外部的 Web 服务,让用户可以查询有关其帐户的信息,这些信息分布在多个内部遗留服务中。
我的服务将用户输入的字符串传递到多个后端 RESTful API,这些 API 根据字符串进行 MySQL 查找以生成结果,这些结果将传递回我的服务以提供给用户。
从历史上看,这些遗留后端服务没有暴露给用户输入,所以我不确定它们是否有适当的防护措施来防止 SQL 注入。
通常我会阻止 SQL 注入 Prepared Statements 以防止数据库引擎将用户字符串视为可解析的,但在这种情况下我不控制数据库调用 - 它们'离下游很远,现在审计它们是不切实际的。
我可以在我的 golang 代码中做些什么来尽可能地清理用户输入以最小化 SQL 注入漏掉的风险?这最终是一个权宜之计,直到可以审计所有下游数据库调用的注入安全性。
编辑:出于所有实际目的,用户输入可以是任意字符串,但它不应该 是可执行代码。我的服务需要来自用户的字段值,而不是代码。
在过去的一份工作中,我为我们的服务维护了一个允许临时查询的工具。它允许管理人员请求报告,而无需等待数周的代码部署(回到部署需要数周的古怪日子)。
我们不通过使服务接受任意字符串作为输入并以 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注射防御就满意了
在您的情况下,您的代码可能无法直接访问遗留服务的数据库,因此您不能执行 prepare/execute 并使用绑定参数。您必须提交包含集成值的静态查询。
在其他语言中,您可以通过 转义 使任何字符串值安全地插值到 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
请注意,这比仅使用正则表达式替换要复杂一些,因为您需要考虑多字节字符集。
添加到比尔关于数据输入的回答中。为了确保输出数据的卫生,比如网络服务,请查看 html/template:
Package template (html/template) implements data-driven templates for generating HTML output safe against code injection.