From d3b760417897ed91b332e18276239c107e5f5430 Mon Sep 17 00:00:00 2001 From: An Tran Date: Fri, 18 Oct 2024 17:00:03 +1000 Subject: [PATCH] [http_authorization] Check for nil value when decode based64 value Performing a match on a nil value results in an exception being thrown and bypassing the entire authorization validation process. --- CHANGELOG.md | 2 + gateway/src/resty/http_authorization.lua | 5 +- spec/resty/http_authorization_spec.lua | 7 + t/apicast.t | 524 +++++++++++++++++++++++ 4 files changed, 537 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c219375c..5c12e882d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed APIcast send request through proxy server even when `NO_PROXY` is used [PR #1478](https://github.com/3scale/APIcast/pull/1478) [THREESCALE-11128](https://issues.redhat.com/browse/THREESCALE-11128) +- Fixed APIcast panic when parsing invalid base64 encoded value [PR #1505](https://github.com/3scale/APIcast/pull/1505) [THEESCALE-11435](https://issues.redhat.com/browse/THREESCALE-11435) + ### Added - Detect number of CPU shares when running on Cgroups V2 [PR #1410](https://github.com/3scale/apicast/pull/1410) [THREESCALE-10167](https://issues.redhat.com/browse/THREESCALE-10167) diff --git a/gateway/src/resty/http_authorization.lua b/gateway/src/resty/http_authorization.lua index 89a990e47..4a618404f 100644 --- a/gateway/src/resty/http_authorization.lua +++ b/gateway/src/resty/http_authorization.lua @@ -9,8 +9,11 @@ local _M = { local mt = { __index = _M } function _M.parsers.Basic(param) + local userid, password local user_pass = ngx.decode_base64(param) - local userid, password = match(user_pass, '^(.*):(.*)$') + if user_pass then + userid, password = match(user_pass, '^(.*):(.*)$') + end return { userid = userid, diff --git a/spec/resty/http_authorization_spec.lua b/spec/resty/http_authorization_spec.lua index 0f50b7636..4e94a0073 100644 --- a/spec/resty/http_authorization_spec.lua +++ b/spec/resty/http_authorization_spec.lua @@ -60,6 +60,13 @@ describe('HTTP Authorization', function() assert.equal('', auth.userid) assert.equal('pass', auth.password) end) + + it('do not panic with invalid header', function() + local auth = authorization.new('Basic !123!') + + assert.equal(nil, auth.userid) + assert.equal(nil, auth.password) + end) end) describe('Bearer', function() diff --git a/t/apicast.t b/t/apicast.t index 94f089344..213a17201 100644 --- a/t/apicast.t +++ b/t/apicast.t @@ -786,3 +786,527 @@ Authentication failed --- no_error_log [error] + + +=== TEST 24: with user_key in header +--- configuration +{ + "services": [ + { + "backend_version": "1", + "id": 42, + "proxy": { + "credentials_location": "headers", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&user_key=value" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +user_key: value +--- request +GET / +--- response_body +yay, api backend +--- error_code: 200 +--- no_error_log +[error] + + + +=== TEST 25: with invalid user_key in header +--- configuration +{ + "services": [ + { + "backend_version": "1", + "id": 42, + "proxy": { + "credentials_location": "headers", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&user_key=value" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +user_key: !123! +--- request +GET / +--- response_body chomp +Authentication failed +--- error_code: 403 + + + +=== TEST 26: with user_key in query parameters +--- configuration +{ + "services": [ + { + "backend_version": "1", + "id": 42, + "proxy": { + "credentials_location": "query", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&user_key=value" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- request +GET /?user_key=value +--- response_body +yay, api backend +--- error_code: 200 +--- no_error_log +[error] + + + +=== TEST 27: with invalid user_key in query parameters +--- configuration +{ + "services": [ + { + "backend_version": "1", + "id": 42, + "proxy": { + "credentials_location": "query", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&user_key=value" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- request +GET /?user_key= +--- response_body chomp +Authentication failed +--- error_code: 403 + + + +=== TEST 28: with user_key in Basic Authorization header +--- configuration +{ + "services": [ + { + "backend_version": "1", + "id": 42, + "proxy": { + "credentials_location": "authorization", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&user_key=value" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +Authorization: Basic dmFsdWU6Cg== +--- request +GET / +--- response_body +yay, api backend +--- error_code: 200 +--- no_error_log +[error] + + + +=== TEST 29: with invalid user_key in Basic Authorization header +--- configuration +{ + "services": [ + { + "backend_version": "1", + "id": 42, + "proxy": { + "credentials_location": "query", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&user_key=value" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +Authorization: Basic !123! +--- request +GET / +--- response_body chomp +Authentication parameters missing +--- error_code: 401 + + + +=== TEST 30: with app_id and app_key in header +--- configuration +{ + "services": [ + { + "backend_version": "2", + "id": 42, + "proxy": { + "credentials_location": "headers", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&app_id=foo&app_key=bar" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +app_id: foo +app_key: bar +--- request +GET / +--- response_body +yay, api backend +--- error_code: 200 +--- no_error_log +[error] + + + +=== TEST 31: with invalid app_key and app_id in header +--- configuration +{ + "services": [ + { + "backend_version": "2", + "id": 42, + "proxy": { + "credentials_location": "headers", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&app_id=foo&app_key=bar" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +app_id: foo +app_key: !123! +--- request +GET / +--- response_body chomp +Authentication failed +--- error_code: 403 + + + +=== TEST 32: with app_key and app_id in query parameters +--- configuration +{ + "services": [ + { + "backend_version": "2", + "id": 42, + "proxy": { + "credentials_location": "query", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&app_id=foo&app_key=bar" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- request +GET /?app_id=foo&app_key=bar +--- response_body +yay, api backend +--- error_code: 200 +--- no_error_log +[error] + + + +=== TEST 33: with invalid app_id and app_key in query parameters +--- configuration +{ + "services": [ + { + "backend_version": "2", + "id": 42, + "proxy": { + "credentials_location": "query", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&app_id=foo&app_key=bar" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- request +GET /?app_id=foo&app_key=!123! +--- response_body chomp +Authentication failed +--- error_code: 403 + + + +=== TEST 34: with app_id and app_key in Basic Authorization header +--- configuration +{ + "services": [ + { + "backend_version": "2", + "id": 42, + "proxy": { + "credentials_location": "authorization", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&app_id=foo&app_key=bar" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +Authorization: Basic Zm9vOmJhcg== +--- request +GET / +--- response_body +yay, api backend +--- error_code: 200 +--- no_error_log +[error] + + + +=== TEST 35: with invalid app_key/app_id in Basic Authorization header +--- configuration +{ + "services": [ + { + "backend_version": "1", + "id": 42, + "proxy": { + "credentials_location": "query", + "api_backend": "http://test:$TEST_NGINX_SERVER_PORT/", + "proxy_rules": [ + { "pattern": "/", "http_method": "GET", "metric_system_name": "hits", "delta": 2 } + ], + "policy_chain": [ + { "name": "apicast.policy.apicast" } + ] + } + } + ] +} +--- backend + location /transactions/authrep.xml { + content_by_lua_block { + local expected = "service_id=42&usage%5Bhits%5D=2&user_key=value" + require('luassert').same(ngx.decode_args(expected), ngx.req.get_uri_args(0)) + } + } +--- upstream + location / { + content_by_lua_block { + ngx.say('yay, api backend'); + } + } +--- more_headers +Authorization: Basic !123! +--- request +GET / +--- response_body chomp +Authentication parameters missing +--- error_code: 401 + + +