[spring-projects/spring-boot]执行器端点上的 URI 属性列表中缺少清理

2020-08-21 55 views
6

问题

我正在使用 Spring Boot 2.2.9。https://github.com/spring-projects/spring-boot/pull/19999引入的更改认为任何包含“uri”、“uris”、“address”或“addresses”的键都是“逗号分隔的 URL”。这并不总是正确的假设。它将尝试从这些 URL 中删除密码,但是如果它不是 URL 格式,它将返回原始内容

预期行为

如果该键不是 URL 格式,则应返回为******.或者至少允许开发人员配置是否要清理 URL。

复制器

Sanitizer sanitizer = new Sanitizer();
System.out.println(sanitizer.sanitize("uris", "[amqp://foo:bar@host/]"));
System.out.println(sanitizer.sanitize("uris", "amqp://foo:bar@host/"));

输出是:

[amqp://foo:bar@host/]
amqp://foo:******@host/

回答

0

你好,这是一个first-timers-only问题。这意味着我们一直在努力让那些以前没有为我们的代码库做出过贡献的人,甚至以前没有为开源做出过贡献的人更容易理解。

如果您就是这样,我们有兴趣帮助您迈出第一步,并可以回答您的问题并为您提供帮助。请注意,我们对自由和开源软件中代表性不足的群体的贡献特别感兴趣!

如果您以前曾贡献过,请考虑将这一贡献留给新人,并查看我们的一般ideal-for-contribution问题。谢谢!

问题

Actuator 端点上公开信息时, Actuatororg.springframework.boot.actuate.endpoint.Sanitizer负责清理敏感信息。envconfigprops

当配置键与配置的模式之一匹配时,整个配置值都会被清理。类似 URI 的键 ( ) 有一种特殊情况"uri", "uris", "address", "addresses",其中Sanitizer尝试仅清理 URL 的密码部分:

something.uri=https://emily:springboot@spring.io/ -> something.uri=https://emily:******@spring.io/
other.uri=https://spring.io/ -> other.uri=https://spring.io/

第一个问题是关于文档。该文档暗示所有配置键都以相同的方式处理。

如果要清理的任何键都是 URI 格式(即schema>://<username>:<password>@<host/),仅对密码部分进行清理。

但实际上用户信息键 ( "uri", "uris", "address", "addresses") 和其他键的处理是不同的:

  • 用户信息键 ( "uri", "uris", "address", "addresses"):仅隐藏 URL 中的密码部分,或返回不变的值
  • 其他键 ( "password", "secret", "key", "token", "vcap_services", "sun.java.command","credentials") 已完全清理

第二个问题是,清理程序用于针对序列化的配置值 - 每当 aList序列化为 JSON 时,它都会序列化为[https://emily:springboot@spring.io/,https://spring.io]而不是https://emily:springboot@spring.io/,https://spring.io。问题是,如果当前URI_USERINFO_PATTERN不支持这种情况并且返回原始值,则不会清理任何内容。

解决方案

首先,应该改进参考文档(位于spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc),以更好地解释密钥的处理方式(有些在所有情况下都经过完全清理,其他的仅包含密码部分(如果存在))。

另外,应该改进URI_USERINFO_PATTERN中的模式Sanitizer以支持列表情况。也许类似的东西"\\[?[A-Za-z]+://.+:(.*)@.+$"会起作用? SanitizerTests 中的所有测试在更改后应该仍然是绿色的,并且我们应该有一个新的测试用例来检查:

"[http://user1:password1@localhost:8080,http://user2@localhost:8082,http://localhost:8083]" -> "[http://user1:******@localhost:8080,http://user2@localhost:8082,http://localhost:8083]";

如果可能,PR 应该针对 2.2.x 分支进行 - 如果 master 对贡献者来说更方便,项目维护者将 rebase PR。

1

这对于我的第一个贡献来说看起来很合理!看看...

6

在这种情况下,方括号内的部分仍然是一个普通的 URL,如果不使用方括号进行解析,它将被正确清理。让正则表达式更灵活以便它可以检测嵌入的 URL 不是更有意义吗?

例如,将其更改为.*[A-Za-z]+:/.+:(.*)@.+

7

这个解决方案肯定也是有道理的。我刚刚按照 @bclozel 的建议以这种方式实现了修复,我希望我已经正确理解:

仅当值与用户信息模式匹配时才清理密码部分,或者完全清理该值并返回**

我能够向自己证明这个解决方案的合理性如下......

可能还有其他模式,例如用大括号而不是方括号包围的 URI,或者我们甚至无法想象的其他格式。如果我们承认我们不可能解释所有可能的模式,那么我们就可以让事情变得简单,并清理任何与最常见模式不匹配的内容。这无疑会导致我们清理不包含密码的值,但它也会防止我们意外暴露包含在未处理模式中的密码。

1

我当然理解你选择的路线。最好谨慎行事,尤其是在考虑安全性时。您知道 AMQP 地址方括号后面的用途吗?

我们还使用 AMQP 并单独指定用户名和密码,因此它不会嵌入在 URL 中。

3

@helloworldless 抱歉,我的第一条评论是错误的 - 我们在团队通话中讨论过这个问题,这个问题比我们想象的更微妙。我已经更新了问题描述和要遵循的步骤。希望现在一切都清楚了。

鉴于在这个问题上不幸的反复,请随时请求帮助或省略部分工作 - 我们很乐意在合并之前完成它。

谢谢!

5

不用担心。我再看一下!

8

经过一些实验后,看起来要找到一个适用于列表和单个值的正则表达式模式将非常棘手。那么如果我们以不同的方式处理单个值和列表呢?单个值的处理方式基本上与当前的方式相同,列表(由方括号标识)的处理方式如下:

  1. 去掉方括号
  2. 用逗号分割元素
  3. 对每个元素应用正则表达式和替换
  4. 用逗号和方括号重新连接元素
4

@helloworldless 在问题描述(即"\\[?[A-Za-z]+://.+:(.*)@.+$")中提出的正则表达式在这种情况下不起作用吗?如果我没记错的话,我们已经用逗号分割了值,然后与模式进行匹配。[模式中的可选选项应该涵盖我们丢失的情况吗?

1

我在这方面花了更多时间,这是我想出的模式:

String proposedPattern = "(\\[?[A-Za-z]+?://.+?:)(.*?)(@.+?)";

我最终需要三个捕获组才能重建这些值。我还必须让匹配者变得懒惰(“不贪婪”)。

我在这里演示了这一点:https://repl.it/@helloworldless/spring-boot-issue-23037。这对于查看正则表达式的剖析也很有用:https://regex101.com/r/sr6uFN/1

我要继续这个吗?

0

我可能在这里遗漏了一些东西。我们不会在整个字符串上运行该模式,因为我们","首先要进行拆分。

我已在本地将模式更改为Pattern.compile("\\[?[A-Za-z]+://.+:(.*)@.+$");并成功运行以下测试:

    @ParameterizedTest(name = "key = {0}")
    @MethodSource("matchingUriUserInfoKeys")
    void uriAsListWithPasswordShouldHaveThoseSanitized(String key) {
        Sanitizer sanitizer = new Sanitizer();
        assertThat(sanitizer.sanitize(key,
                "[http://user1:password1@localhost:8080,http://user2@localhost:8082,http://localhost:8083]")).isEqualTo(
                "[http://user1:******@localhost:8080,http://user2@localhost:8082,http://localhost:8083]");
    }

我是否缺少一个会使该方法无效的测试用例?

8

我的错。我没有正确研究代码。

我现在已经使用了您建议的模式并更新了测试。我还进行了修改Sanitizer#getPattern以处理索引属性键,例如uris[1].请参阅此处的差异:https://github.com/spring-projects/spring-boot/compare/2.2.x...helloworldless :23037-sanitize- uris。

我确实有一个关于更改为 的问题Sanitizer#getPattern。我的更改改变了所有敏感键的行为,而不仅仅是 URI/地址键。例如,它现在将匹配“my.password[0]”,但在此之前不会匹配。这是可以接受的还是我应该进行一些重构以从 URI/地址键(例如“uri”、“地址”)中分离出敏感键(例如“令牌”、“密码”),以便可以处理它们不同的是Sanitizer#getPattern

我还想根据我目前对需求的理解提供更多细节,以确保我们达成共识......

现在实际上有两个属性列表场景需要处理:“本机”属性列表用户提供的列表文字

“本机”属性列表

uris[0]=http://user1:password1@localhost:8080
uris[1]=http://user2:password2@localhost:8082
uris:
  - http://user1:password1@localhost:8080
  - http://user2:password2@localhost:8082

Sanitizer#sanitize使用这些参数调用两次:

  1. 键 =“uris[0]”,值 =“ http://user1:password1@localhost:8080
  2. 键 =“uris[1]”,值 =“ http://user2:password2@localhost:8082

value + $以前,由于此模式中的尾随,因此未正确处理Sanitizer#getPattern

Pattern.compile(".*" + value + "$", Pattern.CASE_INSENSITIVE)

为了解决这个问题,我修改了该模式以匹配方括号中包含的可选索引:

Pattern.compile(".*" + value + "(?:\\[.+])?$", Pattern.CASE_INSENSITIVE)

用户提供的列表文字

我不确定为什么会使用这种格式,但我想有些库可能需要这种格式。

uris=[http://user1:password1@localhost:8080,http://user2:password2@localhost:8082]

注意:此处的引号是必需的,否则它会被解析为“本机”列表

uris: "[http://user1:password1@localhost:8080,http://user2:password2@localhost:8082]"

对于其中任何一个,Sanitizer#sanitize都会使用以下参数调用一次:

  1. 键=“uris”,值=“[ http://user1:password1@localhost:8080,http://user2:password2@localhost:8082 ]”

正如您所指出的,Sanitizer#sanitizeUris在“,”上分裂。所以Sanitizer#sanitizeUri被调用两次:

  1. “[ http://user1:password1@localhost:8080
    1. 由于更改为,现在它作为 URI 进行匹配Sanitizer#URI_USERINFO_PATTERN
  2. http://user2:password2@localhost:8082 ]”
    1. 这从来都不是问题;它已经与现有的相匹配Sanitizer#URI_USERINFO_PATTERN
6

感谢您的第一个贡献@helloworldless - 我接受了您的提交并只是对其进行了一些简化。

我认为这个首次出现的问题比预期的更微妙,但修复看起来是正确的。我最终删除了有关匹配类似数组的键的更复杂的部分,因为我认为这方面存在混乱。Sanitizer当迭代解析的实例时,仅涉及 Actuator端点PropertySource;因此,Sanitizer 从不查看属性/yaml 语法,而是查看键的解析表示("[http://user1:password1@localhost:8080,http://user2:password2@localhost:8082]"此处为列表的字符串表示形式)。

现在已合并到 2.2.x 中,并在以后的分支中向前合并。