From e2303b71c4e2871529be2a0b0819d477a3e9dcd3 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 | 1 + gateway/src/resty/http_authorization.lua | 5 +- spec/resty/http_authorization_spec.lua | 7 + t/apicast.t | 524 +++++++++++++++++++++++ 4 files changed, 536 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b87c7b7d6..cb441a759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fix APIcast using stale configuration for deleted products [PR #1488](https://github.com/3scale/APIcast/pull/1488) [THREESCALE-10130](https://issues.redhat.com/browse/THREESCALE-10130) - Fixed Mutual TLS between APIcast and the Backend API fails when using a Forward Proxy [PR #1499](https://github.com/3scale/APIcast/pull/1499) [THREESCALE-5105](https://issues.redhat.com/browse/THREESCALE-5105) - Fixed dns cache miss [PR #1500](https://github.com/3scale/APIcast/pull/1500) [THEESCALE-9301](https://issues.redhat.com/browse/THREESCALE-9301) +- 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 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 + + +