Perl测试中拦截外部URL HTTP请求的标准实现方式是什么?
问题场景
给定以下Perl代码,解析XML时会触发外部实体(XXE)调用嵌入的URL:
use 5.020; use XML::LibXML (); XML::LibXML->load_xml( string => <<'EOF' ); <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://webhook.site/<unique-token>">]> <foo>&xxe;</foo> EOF
我希望编写测试用例,当解析XML字符串触发外部URL调用时让测试失败。思路是拦截指定URL的请求,以此判断测试是否通过,测试框架用Test2::V0,代码框架如下:
use 5.020; use Test2::V0; ok makes_no_http_requests { # 上述XML解析代码 }, 'should not make any HTTP requests'; done_testing; sub makes_no_http_requests { # 如何实现? }
请问在Perl中是否有标准的实现方式?
实现方案
有两种核心思路可以解决这个问题,优先推荐从根源阻止XXE的方式,其次是拦截HTTP请求做测试验证:
1. 直接禁用XML外部实体(最根本的解决方案)
XML::LibXML本身提供了安全配置项,可以从根源上禁止外部实体的加载和网络请求,这比拦截HTTP请求更高效,也是防御XXE漏洞的标准做法。
你可以创建解析器时设置以下参数:
use 5.020; use XML::LibXML (); my $parser = XML::LibXML->new( no_network => 1, # 禁止发起网络请求 expand_entities => 0, # 禁止展开外部实体 load_ext_dtd => 0, # 禁止加载外部DTD文件 ); $parser->load_xml( string => <<'EOF' ); <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://webhook.site/<unique-token>">]> <foo>&xxe;</foo> EOF
对应的测试用例可以直接验证解析过程不会触发网络请求,或者验证解析器的安全参数是否正确配置。
2. 拦截HTTP请求(用于测试现有代码安全性)
如果需要测试现有代码是否存在XXE风险,可以通过Mock HTTP客户端的方式拦截所有请求,具体有两种实现:
方法一:用Test::LWP::UserAgent替换全局HTTP客户端
XML::LibXML底层通常依赖LWP处理网络请求,用这个模块可以替换默认的用户代理,拦截并阻止所有请求:
use 5.020; use Test2::V0; use Test::LWP::UserAgent; use XML::LibXML (); sub makes_no_http_requests { my $code = shift; # 创建Mock客户端,任何请求都会触发报错 my $mock_ua = Test::LWP::UserAgent->new; $mock_ua->map_response(qr/.*/, sub { die "意外发起HTTP请求" }); # 临时替换全局LWP构造函数,执行代码后自动恢复 my $original_ua = LWP::UserAgent->new; local *LWP::UserAgent::new = sub { $mock_ua }; eval { $code->(); }; return !$@; } ok makes_no_http_requests { XML::LibXML->load_xml( string => <<'EOF' ); <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://webhook.site/<unique-token>">]> <foo>&xxe;</foo> EOF }, '解析XML时不应发起HTTP请求'; done_testing;
方法二:重写XML::LibXML的外部实体处理回调
XML::LibXML允许自定义外部实体的处理逻辑,你可以在回调中直接抛出错误,阻止请求执行:
use 5.020; use Test2::V0; use XML::LibXML (); sub makes_no_http_requests { my $code = shift; my $parser = XML::LibXML->new; # 设置外部实体处理回调,一旦触发就报错 $parser->set_ext_ent_handler(sub { die "尝试访问外部实体: $_[1]"; }); # 临时替换XML::LibXML的load_xml方法,使用自定义解析器 local *XML::LibXML::load_xml = sub { $parser->load_xml(@_); }; eval { $code->(); }; return !$@; } ok makes_no_http_requests { XML::LibXML->load_xml( string => <<'EOF' ); <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://webhook.site/<unique-token>">]> <foo>&xxe;</foo> EOF }, '解析XML时不应发起HTTP请求'; done_testing;
内容的提问来源于stack exchange,提问作者j1n3l0
相关产品推荐
相关产品推荐

