allejo / php-vcr-sanitizer
通过排除API密钥、密码和凭据,将隐私引入php-vcr的录制中
Requires
- php: >=7.1.3
- php-vcr/php-vcr: ^1.4
- symfony/event-dispatcher: ^4.0|^5.0|^6.0
Requires (Dev)
- ext-curl: *
- ext-json: *
- ext-sockets: *
- donatj/mock-webserver: ^2.1
- mikey179/vfsstream: ^1.6
- php-curl-class/php-curl-class: ^9.0
- phpunit/phpunit: ^7.5|^9.5
README
php-vcr 是一个用于记录和重放外出请求的工具,然而它已经将“隐私感知”标记为“即将推出”很长时间了。每当我测试我的API时,记录中经常会包含一些敏感信息,如密钥或密码。到目前为止,我一直在使用一个单独的脚本,在提交到版本控制之前始终删除敏感数据。
我不愿意总是清理数据,因此这是一个快速且简单的解决方案,直到php-vcr官方支持“私人”录制。
目录
安装
通过 Composer 安装此包。
composer require --dev allejo/php-vcr-sanitizer
使用
在您的VCR实例开启后,调用 VCRCleaner::enable()
并传递您不希望在固定文件中记录的任何URL参数或头部。
VCR::turnOn(); VCR::insertCassette('...'); VCRCleaner::enable(array( 'request' => array( 'ignoreHostname' => false, 'ignoreQueryFields' => array( 'apiKey', ), 'ignoreHeaders' => array( 'X-Api-Key', ), 'bodyScrubbers' => array( function($body) { return preg_replace('/<password.*?<\/password>/', 'hunter2', $body); } ), 'postFieldScrubbers' => array( function(array $postFields) { $postFields['Secret'] = 'REDACTED'; return $postFields; } ), ), 'response' => array( 'ignoreHeaders' => array('*'), 'bodyScrubbers' => array(), ), ));
配置
此库允许您清理录制中的请求和响应部分,以确保只有非敏感数据写入您的磁带。您可以通过下面的配置选项定义此清理器的行为。
清理请求
request.ignoreHostname
- 当设置为true时,请求中的URL内的主机名将在url
字段中替换为[]
,并且在头部中的Host
将设置为null。request.ignoreQueryFields
- 定义您要完全从记录中删除的URL中的GET参数。request.ignoreHeaders
- 定义在记录中会自动设置为null的头部。在数组中使用星号(例如*
)可以用来从请求中删除所有头部。request.bodyScrubbers
- 一个回调函数数组,这些回调函数将请求正文作为字符串提供。每个回调 必须 返回修改后的正文。回调按数组中出现的顺序依次调用,一个回调的值会传递给下一个。request.postFieldScrubbers
- 一个回调函数数组,这些回调函数将请求POST字段作为数组提供。每个回调 必须 返回修改后的POST字段数组。回调按数组中出现的顺序依次调用,一个回调的值会传递给下一个。
清理响应
php-vcr库官方不支持修改其响应,因此此库使用反射来修改响应的内容。虽然此功能由 本项目 正式支持,但如果由于php-vcr内部更改导致此功能出现问题,请耐心等待。
response.ignoreHeaders
- 与request.ignoreHeaders
相同,但用于响应体。response.bodyScrubbers
- 与request.bodyScrubbers
相同,但用于响应体。
禁用清理器
为什么没有 VCRCleaner::disable()
?没有简单且非侵入式的方法来恢复VCR到其原始状态。可能还是直接为某些批次的单元测试重新配置VCR更简单。
清理工作原理
当VCR寻找回放记录时,VCRCleaner使用修改后的“匹配器”来检查除了你标记为敏感的字段之外的所有内容。
主机名
如果你的URL端点的hostname是敏感的且不应被记录,你可以让sanitizer忽略hostname,它们将在url
字段中被替换为[]
,并且host
头将被设置为null。
- request: method: GET url: 'https://[]/search' headers: Host: null X-Type: application/vcr response: status: http_version: '1.1' code: '404' message: 'Not Found' headers: ~ body: "...response body..."
头部
假设你将X-Api-Key
头设置为SuperToast
。在你的记录中,你指定的头将被保存为null。
- request: method: GET url: 'https://www.example.com/search' headers: Host: www.example.com X-Api-Key: null X-Type: application/vcr response: status: http_version: '1.1' code: '404' message: 'Not Found' headers: ~ body: "...response body..."
URL参数
注意在记录中apiKey=yourSecretApiKey
已被移除。在你的VCR回放期间,它将寻找没有apiKey
参数的匹配请求。
# Your cURL call to: https://www.example.com/search?q=keyword&apiKey=yourSecretApiKey # gets recorded like so, - request: method: GET url: 'https://www.example.com/search?q=keyword' headers: Host: www.example.com response: status: http_version: '1.1' code: '404' message: 'Not Found' headers: ~ body: "...response body..."
正文内容
与忽略头或URL参数不同,从请求和响应体中清除信息使用了回调函数数组。每个函数的结果会传递给下一个函数。
注意请求体中的password=hunter2
已被移除。回调函数将body作为字符串参数,必须返回修改后的结果。
VCRCleaner::enable(array( 'request' => array( 'bodyScrubbers' => array( function ($body) { $parameters = array(); parse_str($body, $parameters); unset($parameters['password']); return http_build_query($parameters); }, ), ), ));
# You POST request to `https://www.example.com/search` with a body of # `username=AzureDiamond&password=hunter2` gets recorded like so, - request: method: POST url: 'https://www.example.com/search' headers: Host: www.example.com body: 'username=AzureDiamond' response: status: http_version: '1.1' code: '404' message: 'Not Found' headers: ~ body: '...response body...'
POST字段内容
在发送POST请求时,你的VCR有时会记录post_fields
参数中的数据而不是body
;例如,当在cURL中使用CURLOPT_POSTFIELDS
且没有将CURLOPT_POST
设置为true
时。在这些情况下,此选项可用于清理敏感内容。注意,与body
字段不同,post_fields
是一个数组。
VCRCleaner::enable(array( 'request' => array( 'postFieldScrubber' => array( function (array $postFields) { $postFields['Secret_Key'] = ''; return $postFields; }, ), ), ));
# You POST request to `https://www.example.com/search` with a post field of # `['data'=> 'hello world', 'Secret_Key' => 'abc']` gets recorded like so, - request: method: POST url: 'https://www.example.com/search' headers: Host: www.example.com post_fields: data: 'hello world' Secret_Key: '' response: status: http_version: '1.1' code: '404' message: 'Not Found' headers: ~ body: '...response body...'