导语:在本系列文章的前面一部分我们从使用 Postman 开始,创建了集合和请求,并通过 Burp Suite 设置为了 Postman
的代理,这样我们就可以使用 Burp 的模糊测试和请求篡改的功能。
在本系列文章的前面一部分我们从使用 Postman 开始,创建了集合和请求,并通过 Burp Suite 设置为了 Postman 的代理,这样我们就可以使用 Burp 的模糊测试和请求篡改的功能。 在接下来的这部分内容中,我们将深入研究一些稍微高级一点的 Postman 功能,这些功能你肯定会用到的。
集合变量
Postman 中的变量几乎可以用于请求中的任何字段。 语法是在它们的两边使用两层花括号。有几个地方我可以使用变量定义它们。 如果它们是静态的,也许我会将它们设置为集合变量。 例如,我一直使用 http://localhost:4000 作为我的测试主机。 如果我将测试 API 的端口从4000改为4001,我不希望一个个编辑每个请求的 URL。接下来我介绍一下我们该如何将它移动到一个集合变量中。首先,在菜单侧栏中打开"集合"列表中编辑该集合的对话框。 我可以单击… 按钮或者右键单击集合名称。这两种操作,我们会得到相同的上下文菜单,然后选择编辑(Edit)。
这将打开一个编辑集合的对话框。 默认视图包括集合的名称和描述的文本框,但是在这两个字段之间还有一行选项卡。
其中一个标签叫做变量(Variables)。这就是我们想要的,点击这个标签会打开另一个对话框,用于编辑变量。
Postman 集合变量编辑界面
它有一个表格,其中包含某个变量的变量名称、 初始值列和 当前值列。 这两个值列之间的差异与 Postman 的付费功能进行同步有关。 在这里重要的一个点是,你将输入初始值,然后选项卡进入当前值字段。 这将自动将当前初始值填充到当前值字段中,并且它将如图所示。 现在我有了一个名为 API_host 的集合变量,其值为 http://localhost:4000。 在完成了变量的编辑之后需要点击更新按钮。
现在是时候修改我的请求,并引用该变量,而不是使用硬编码的主机名和端口。
Postman 中的请求,将 URL 更改为指向一个变量
我只是简单地用占位符替换了每个 URL 中对应的部分: {{API_host}},把鼠标悬停在占位符上可以展开这个变量,会显示变量值和范围。 这里有一些颜色编码也可以帮助我们。 当变量有效时,文本会变成橙色,但是如果我输入一个无效的变量名,文本将变成红色。
我仍然需要对每个请求进行一次更新,让它们使用某个变量。 但是在将来,如果我改变了端口,或者如果我切换到了 HTTPS,或者如果我将我的测试 API 部署到一个完全不同的主机上; 那么我就可以回到集合变量那里并更新变量的值,我的所有请求都会相应地发生改变。
现在,集合变量对于相对静态的字段以及不会经常发生改变的字段是很适合的,但是如果我在一个多租户的解决方案中测试多个环境和部署,甚至多个租户呢? 我可能会使用相同的请求集合,但是使用不同的变量集合。 那么在这种情下环境变量就可以处理这个问题。
环境变量(Environment Variables)
你可能已经注意到了窗口右上角的界面。 让我们打开看看:
在 Postman 中的环境变量界面
-
环境选择器下拉菜单。 可以选择一个环境。
-
快速查看按钮,点击后可以查看你的环境中设置的内容。
-
管理环境按钮,这里是真正进行编辑环境的地方。
首先,我们需要点击管理环境按钮。 这会打开一个较大但空白的对话框,底部有一个 Add 按钮。 点击这个添加按钮。
你会看到另一个对话框。 这一个看起来几乎和集合变量对话框一样,除了它有一个名字。
在这里,我把我的命名为 LocalTest。
我还添加了许多其他的变量,其中一个叫做bearer_token,值为 foo。 另一个是 user_id值为1。
一旦完成编辑,我们点击对话框底部附近的添加按钮,然后关闭管理环境对话框。 在我可以在这个环境中使用这个变量之前,还有最后一个重要的、经常被忘记的步骤:我们需要从环境选择器下拉菜单中选择这个环境。
现在这些额外的变量可以像上面的 API_host 变量一样进行访问: {{bearer_token}} 和 {{user_id}}
路由参数
在现代 API 中使用路由参数是很常见的。 这些是作为 URL 主路径的一部分所提供的值。例如,考虑 http://localhost:4000/user/42/preferences 这个 URL:
这样的 URL 中的数字42实际上是一个参数,很可能是本例中的用户 ID。 当服务器端应用程序路由传入请求时,服务端会提取该值,并使其随时可用于最终处理请求和构造响应的函数。 这是一个路由参数。 这对于编辑参数或是在 Postman 中使用也比较简单。语法是将参数以冒号(:)后跟参数名的形式直接放入 URL 中。 对于 Postman 中的这个示例请求,我将其输入为{{API_host}}/user/:userId/preferences。 然后,在请求的参数( Params)选项卡上,我可以看到它被列出并设置了具体的值。 在下图中,我将其设置为在前面的环境变量中指定的用户 id 变量。
我也可以把我的变量直接写到 URL 中,但在我看来,这种方式更干净。
无记名令牌(Bearer Tokens)
想象一下这样一个场景: 你发出某种类型的授权请求,它用一个 bearer token 进行响应,然后你需要在所有其他请求中使用该令牌。 这样做的手动方式可能只是发出验证请求,然后从响应中复制并粘贴令牌到另外一个环境变量。 所有其他的请求都可以使用这个环境变量。
这样也可以正常发出请求,如果你有一个短期的令牌,那么这种做法可能是个痛苦。 对于这个问题,有一个更优雅的解决方案。 想想下面的响应:
我们已经发出了请求,并且收到了一个包含令牌的 JSON 响应。 现在,我想用自动化的方式使用新的bearer token来更新我的环境变量。 在请求界面上,有几个标签可以做到。 最右边的一个叫做测试。 这主要用于自动检查响应,以确定 API 是否失效,就像单元测试一样。 但是我们可以通过一些 JavaScript 语句来达到我们的目的。
我添加上面的脚本,单击保存,然后再次运行我的请求。 似乎所有的事情都和第一次一模一样。 但是如果我使用快速查看( Quick Look)按钮来查看环境变量时……
我们可以看到当前值已经被自动更新。 这是第一步——我现在将值存储在一个我可以轻松引用的地方,但是它不会将Bearer token 这个令牌放入我的请求中。 我有两个方法。 第一个方法是,如果打开请求的 Authorization 选项卡,我们可以从类型下拉列表中设置一个Bearer token,并将其指向我的变量。
这种方法是可行的,但是我需要在每个请求上都进行相同的设置。 但是每个新的请求的默认授权类型是继承父类身份验证 (Inherit auth from parent)。 在本例中,父类是个集合。 因此,如果我将这个请求切换回默认的类型,那么我就可以进入编辑集合(Edit Collection)进行设置(与我进入 Collection 变量的上下文菜单相同) ,然后进入 Collection 的 Authorization 选项卡。
这个功能展示的接口几乎与请求中的 Authorization 接口相同,我可以以相同的方式设置身份验证到信息。 现在的不同之处在于它是在集合中管理的。 在默认情况下,我创建的每个新的请求都将包含该bearer token,除非我故意更改了该请求的类型。 例如,我的身份验证请求可能不需要bearer token,因此我需要在请求的 Authorization 选项卡上将类型设置为 No Auth。
偶尔,我会遇到一些应用程序,需要从响应中包含的 XML 或 HTML 主体中提取一些值。 在这种情况下,内置的 xml2Json 函数有助于解析响应内容。
使用 xml2Json 将 HTML 主体整合到 JSON 对象中
还有一个需要注意的功能是 Pre-request Script 选项卡,它使用相同的基本脚本接口。 正如你可能期望的那样,它在请求发送之前执行。 我的一些同事使用这个功能来设置bearer token令牌,这是一个完全有效的方法,只不过不是我平时所使用的方法。 当你只是需要一次性操作的时候,这种方法也会很有帮助,尽管这种情况我一般遇不到。
在接下来的内容中,我们将通过整合一些 Burp Suite 扩展插件来真正加强渗透测试的工具链。 我写的这个系列文章实际上是在处理工具问题。 对于 API 安全测试来说,有很多背景知识需要掌握,从对 OAuth 的深刻理解,到 JWT 和 SAML 之类的标记文本结构,再到对 CORS 的真正理解。
现在让我们使用 burpsuite 的一些扩展插件来扩展 API 安全测试工具链。 我们将介绍一些有用的组件,用于处理 JWTs (JSON Web 令牌) ,以及更有效地查找授权过程中的问题。 这些扩展也不仅限于 API 测试,它们还可以帮助我们进行常规的 web 应用程序测试工作。
设置 Jython
其中的一些扩展需要在 Burp Suite 中设置 Python 环境,而 Burp Suite 在默认情况下是不配置Python 环境信息的。 如果你已经配置过了,那么你可以继续下一章节的操作。 如果没有配置过,你可以打开 Extender-Options 并设置 Jython 的独立 JAR 的位置。 如果你需要这个下载jar包,你可以从 jython.org 上找到。
设置好路径后,导航到 BApp Store 选项卡,就可以开始下载我们将要使用的扩展插件了。 我想重点说明的两个插件是 JSON Web Token Attacker 和 Autorize。
现在让我们更详细地看看这两个插件。
JSON Web Token Attacker
目前已经有许多处理身份验证和授权的方法。 JWT 可能是现代 API 上最常用的方法。 如果在下图中检查Bearer token,你将注意到我们正在使用的 JWT 的几个明显标志: 三个片段,由冒号字符分隔。 Base64编码的头部和声明部分,后面跟着加密签名。
这个令牌可以很容易地在 Burp suite 内置的解码器中解码。 只需选择令牌和 Base64 就可以解码整段文本。 上图中的文本解码后,如下:
{"alg":"HS256","typ":"JWT"}.{"userid":1,"tokenid":"fac15939-de30-481d-9d13-6ae89ecd7370","iat":1552444637,"exp":155244823N30.Ü¢R¥çe4r-ø£m-³Ì@
虽然 Burp 的 Decoder 可以用这种方式以最小的损坏对文本进行解码,但我希望对其进行修改和重新编码。 这样做将产生以下结果:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9LnsidXNlcmlkIjoxLCJ0b2tlbmlkIjoiZmFjMTU5MzktZGUzMC00ODFkLTlkMTMtNmFlODllY2Q3MzcwIiwiaWF0IjoxNTUyNDQ0NjM3LCJleHAiOjE1NTI0NDgyM04zMC7colISpedlNHItBvijbS0TmYWzzIFAAOmykTIWurMtDzVJ
这与原始值相似,但绝对不同。 这是因为 JWT 不是单个 base64 编码的字符串。 标头和声明部分是分开编码的,去掉了任何填充信息(由一个或两个等于号表示)。
在 JWT 实现中有一些缺陷,尽管大多数缺陷比较少见。 对这些缺陷的利用包括篡改 JWT 并重新进行编码。 虽然这可以手动完成,但 JSON Web Token Attacker 能更容易完成这个过程。我们只需从请求中复制值,然后切换到该插件添加的 Burp 中的 JOSEPH 选项卡。 接下来,打开 Manual 子选项卡,并将值粘贴到输入框中。 然后点击加载按钮。
之后会显示一个攻击选择的下拉框界面。 这里有两种攻击方式: 密钥混淆(Key Confusion)和签名排除(Signature Exclusion)。 虽然对这些攻击的深入研究超出了本文要说明的范围,但我会给出一个简介的总结,就是这两种攻击中的任何一种都会将头部的 alg 值更改为不同的算法; 要么将其切换为 None 值(表示不需要签名) ,要么将其从不对称加密切换到对称加密,这样我们就可以用公钥生成有效的签名。 其中任何一种方式的安全性的影响是,加密签名通常是保证令牌完整性的唯一方法。 如果攻击者能够破解该签名,则可以修改该令牌,使其具有攻击者想要的任何声明。 在这个示例中,这可能意味着攻击者会更改用户 ID 获取其他账户的会话并使用该帐户。
让我们来看一下“签名排除”攻击方式的操作步骤,并逐步完成基本的手动攻击:
-
加载输入
-
选择签名排除
-
加载
-
选择一个有效载荷one没有.
-
更新
-
获取生成的值并将其用于 Repeater。如果你从一个有效的 JWT 作为攻击的开始,并且篡改的版本被接受了,那么你的攻击就成功了。之后你就可以继续篡改声明部分了。
密钥混淆的实际用法与签名排除几乎是一样的,只是你需要提供公钥并对其进行转换。 通常的做法是签名算法选择 RS256,并且公钥可用于验证签名。
另一个你可能已经注意到的事情是,你的 Burp 中的代理历史现在有高亮显示的功能和一个新的上下文菜单。
这是一个用于半自动攻击的插件。 我个人不喜欢这样使用这个插件,部分原因是因为我发现它有时会变得混乱,生成格式错误的请求,还有一部分原因是因为我喜欢严格控制我发送的流量。 另外,对于实现 JWT 失效黑名单的系统的一个罕见的情况是,格式不正确的令牌会将会话列入黑名单。
尽管如此,即使是手动攻击模式也极大地简化了生成修改过的 JWT 的过程。 这使得这种方法经常成为 API 测试的有用工具。 我发现,产生这些漏洞利用的实现方式似乎并不常见,但是考虑到它们的严重性,应该始终对这些漏洞进行测试。
自动化(Autorize)
在 API 中可能会出现各种授权问题。 除非 API 是完全公开的,否则你至少拥有经过身份验证的访问权限和未经过身份验证的访问权限。 如果存在某个特定用户拥有的资源或数据的概念,那么验证一个经过身份验证的用户不能访问他们不拥有的任何资源是非常有意义的。 有些 API 还具有多个垂直级别的访问权限,特别是在有组织概念的情况下。 一个用户可能比另一个用户有权访问更多的数据或功能。 同样,这取决于测试人员来确认是否强制执行了这种权限配置。 最后,在多租户系统中(例如,每个客户机组织都有自己独立的空间) ,租户之间的数据或访问泄漏是最大的安全问题之一。
对于所有这些情况,通用的测试策略是将资源和功能映射到具有正确权限的用户。 然后,我们用不同的访问令牌或会话重新发出相同的请求,并比较我们的访问结果。 当权限模型具有任何真正的复杂性时,这个验证过程可能会非常耗时。 自动化可以显著提高测试效率。 让我们从导航到 Burp 中的 Autorize 选项卡作为开始,这样我们就可以完成最低限度的设置。
-
在切换经过身份验证的上下文时,我们要替换的标头有一个大文本框。这可以包括 Cookie 和任何其他请求标头,它们要像在正常请求中一样进行设置。
-
对于 cookies,你可以点击“从最后一个请求获取 cookie ”的按钮来自动填充该文本框。
-
高亮显示的按钮 “Autorize is…” 用于切换插件的开和关。 当关闭时,自动化将什么也不做。 当启动时,命中 proxy* 的传入请求将使用你提供的标头或 cookie 进行替换并重新发出请求,同时省略这些标头或 cookie。
- 界面底部的拦截过滤器选项卡可用于确定包含哪些请求。
现在已经设置好了 Autorize,你可以通过 Postman 发出新的请求,并注意左侧的 Autorize 区域会被填充。
我的示例 API 恰好具有相同的响应,从显示响应长度的三个数字列(分别为74和6)可以看出这一点。 第一个是对未修改请求的响应,第二个是替换了令牌的请求,第三个是未经过身份验证的响应。 剩下的两列用交通信号灯一样的颜色表示强制执行。 图中的黄色部分代表的是一种不确定的响应,而红色和绿色的部分分别用于表示授权是否得到强制执行或执行成功。
现在,可以使用底部的"未经过身份验证的请求探测器"和"强制请求探测器"选项卡来调整认证何时生效。 这能够让你根据你对 API 行为的理解创建匹配条件,并且可以极大地改进结果对响应的解释。 对于较大的 API,或者那些你预期要进行多次测试的 API,这么做当然是值得的。 但是,对于一次性测试,我通常不对此进行调优,而是手动检查响应或长度不一致的情况。
可以使用 Autorize UI 右侧的选项卡(类似于 Burp 中的典型请求和响应选项卡)来检查修改过的和未经身份验证的请求和响应。 这对于研究请求之间为什么存在差异、 API 做了什么以及对服务端来说是否是正确的请求至关重要。
总结
虽然本系列文章的内容不够全面,但是希望这篇文章能够提供足够充分的介绍,让我们开始使用这些插件,这些插件在大多数 API 测试中都很有用,至少在现代 API 中是如此。 自动化对于测试 API 和 SOAP 服务也是非常好用的,而 JSON Web Token Attacker 则非常特定于某种已经变得相当流行的并且是常见的身份验证方式,特别是它在 OAuth2 实现中的使用。