diff --git a/lib/crowdsec.lua b/lib/crowdsec.lua index 1c58eb0..462d15c 100644 --- a/lib/crowdsec.lua +++ b/lib/crowdsec.lua @@ -933,6 +933,13 @@ function csmod.Allow(ip) end end end + -- a background request (fetch, XHR, service worker update) keeps the page the visitor + -- navigated to: this is where the visitor is sent back once the captcha is solved + local fetch_mode = ngx.var.http_sec_fetch_mode + if previous_uri ~= nil and state_id == flag.VERIFY_STATE and source == remediationSource + and fetch_mode ~= nil and fetch_mode ~= "navigate" then + uri = previous_uri + end local succ, err, forcible = ngx.shared.crowdsec_cache:set("captcha_"..ip, uri , 60, bit.bor(flag.VERIFY_STATE, remediationSource)) if not succ then ngx.log(ngx.ERR, "failed to add key about captcha for ip '" .. ip .. "' in cache: "..err) diff --git a/t/25_stream_captcha_background_request.t b/t/25_stream_captcha_background_request.t new file mode 100644 index 0000000..a671bb1 --- /dev/null +++ b/t/25_stream_captcha_background_request.t @@ -0,0 +1,121 @@ +# a background request (Sec-Fetch-Mode other than navigate) must not replace +# the page the visitor navigated to before the captcha is solved + +use Test::Nginx::Socket 'no_plan'; + +run_tests(); + +__DATA__ + +=== TEST 25: Stream mode captcha keeps the navigated page + +--- init + +use LWP::UserAgent; + +my $ua = LWP::UserAgent->new; + +open my $out_fh, '>', 't/servroot/logs/perl.init.log' or die $!; + +my $req = HTTP::Request->new(GET => 'http://127.0.0.1:1984/t'); +$req->header('X-Forwarded-For' => '1.1.1.10'); +my $resp = $ua->request($req); +if (!$resp->is_success) { + print $out_fh "Initialization failed with HTTP code " . $resp->code . "\n"; + exit 1; +} + +sleep(6); + +$req = HTTP::Request->new(GET => 'http://127.0.0.1:1984/page'); +$req->header('X-Forwarded-For' => '1.1.1.1'); +$req->header('Sec-Fetch-Mode' => 'navigate'); +$resp = $ua->request($req); +if (!$resp->is_success || $resp->decoded_content !~ /CrowdSec Captcha<\/title>/i) { + print $out_fh "Navigation did not get the captcha\n"; + exit 1; +} + +print $out_fh "Initialization completed successfully.\n"; +close $out_fh or warn "Could not close filehandle: $!"; + +--- main_config +load_module /usr/share/nginx/modules/ndk_http_module.so; +load_module /usr/share/nginx/modules/ngx_http_lua_module.so; + +--- http_config + +lua_package_path './lib/?.lua;;'; +lua_shared_dict crowdsec_cache 50m; +lua_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; + + +init_by_lua_block +{ + cs = require "crowdsec" + local ok, err = cs.init("./t/conf_t/14_stream_crowdsec_nginx_bouncer.conf", "crowdsec-nginx-bouncer/v1.0.8") + if ok == nil then + ngx.log(ngx.ERR, "[Crowdsec] " .. err) + error() + end + ngx.log(ngx.ALERT, "[Crowdsec] Initialisation done") +} + +access_by_lua_block { + local cs = require "crowdsec" + cs.Allow(ngx.var.remote_addr) +} + +init_worker_by_lua_block { + cs = require "crowdsec" + local mode = cs.get_mode() + if string.lower(mode) == "stream" then + ngx.log(ngx.INFO, "Initilizing stream mode for worker " .. tostring(ngx.worker.id())) + cs.SetupStream() + end +} + +server { + listen 8081; + + location = /v1/decisions/stream { + content_by_lua_block { + local args, err = ngx.req.get_uri_args() + if args.startup == "true" then + ngx.say('{"deleted": [], "new": [{"duration":"1h00m00s","id":4091593,"origin":"CAPI","scenario":"crowdsecurity/vpatch-CVE-2024-4577","scope":"Ip","type":"captcha","value":"1.1.1.1"}]}') + else + ngx.say('null') + end + } + } +} + + +--- config + +location ~ ^/(?:t|page)$ { + set_real_ip_from 127.0.0.1; + real_ip_header X-Forwarded-For; + real_ip_recursive on; + content_by_lua_block { + ngx.print("ok") + } + log_by_lua_block { + print("DEBUG CACHE:captcha_1.1.1.1:" .. tostring(ngx.shared.crowdsec_cache:get("captcha_1.1.1.1"))) + } +} + +--- more_headers +X-Forwarded-For: 1.1.1.1 +Sec-Fetch-Mode: cors +--- request +GET /t + +--- error_code: 200 +--- grep_error_log eval +qr/DEBUG CACHE:captcha_1\.1\.1\.1:[^ ,]*/ +--- grep_error_log_out +DEBUG CACHE:captcha_1.1.1.1:nil +DEBUG CACHE:captcha_1.1.1.1:/page +DEBUG CACHE:captcha_1.1.1.1:/page +--- wait: 1