From 6138497fe097d94398436d4869ddc0f2a73ea5e7 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Fri, 9 Sep 2022 01:01:39 +0200 Subject: [PATCH] change how origin header is treated Now check whether the origin is in allowed_origins table, and if not check allow_all_origins config parameter. While here: - add are_cors_preflight_requests_available - if true then preflight requests are available (default false) --- winixd/core/config.cpp | 5 +- winixd/core/config.h | 23 ++++---- winixd/functions/functionbase.cpp | 96 +++++++++++++++++++------------ winixd/functions/functionbase.h | 3 +- 4 files changed, 76 insertions(+), 51 deletions(-) diff --git a/winixd/core/config.cpp b/winixd/core/config.cpp index 6c51056..0604567 100644 --- a/winixd/core/config.cpp +++ b/winixd/core/config.cpp @@ -357,8 +357,9 @@ void Config::AssignValues() request_queue_job_limit = Size(L"request_queue_job_limit", 1024); - allow_all_cors_origins = Bool(L"allow_all_cors_origins", false); - ListText(L"allowed_cors_origins", allowed_cors_origins); + ListText(L"allowed_origins", allowed_origins); + allow_all_origins = Bool(L"allow_all_origins", true); + are_cors_preflight_requests_available = Bool(L"are_cors_preflight_requests_available", false); ListText(L"access_control_expose_headers", access_control_expose_headers); access_control_allow_credentials = Bool(L"access_control_allow_credentials", false); } diff --git a/winixd/core/config.h b/winixd/core/config.h index 3715e44..3bea054 100644 --- a/winixd/core/config.h +++ b/winixd/core/config.h @@ -1002,18 +1002,21 @@ public: // if the limit is reached then the http status 503 Service Unavailable is returned size_t request_queue_job_limit; - // whether or not all origins are allowed in cors requests - // default: false; - // if false then we check allowed_cors_origins table to check whether the origin is available (origin is sent in Origin header), - // if allow_all_cors_origins is false and allowed_cors_origins is empty then by default we do not allow cors requests - // (but you can still allow it in your function/controller by overriding IsCorsOriginAvailable(...) method) - bool allow_all_cors_origins; - // list of allowed origins in cors requests - // can be set per controller in a method: virtual bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url) - // used only if allow_all_cors_origins is false + // can be set per controller in a method: virtual bool FunctionBase::IsOriginAvailable(const std::wstring & origin_url) // default: empty - std::vector allowed_cors_origins; + std::vector allowed_origins; + + // whether or not all origins are allowed if allowed_origins is empty + // default: true + // this is true by default because Origin header is sent not only in cors requests + // (you can still allow it in your function/controller by overriding IsOriginAvailable(...) method) + bool allow_all_origins; + + // whether or not cors preflight requests are available + // https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request + // default: false + bool are_cors_preflight_requests_available; // list of additional headers sent in Access-Control-Expose-Headers header // can be set per controller in a method: virtual void FunctionBase::AddAccessControlExposeHeadersHeader() diff --git a/winixd/functions/functionbase.cpp b/winixd/functions/functionbase.cpp index 9fae7fc..accd3af 100644 --- a/winixd/functions/functionbase.cpp +++ b/winixd/functions/functionbase.cpp @@ -158,20 +158,23 @@ bool FunctionBase::IsCorsMethodAvailable(Request::Method method) } -bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url) +bool FunctionBase::IsOriginAvailable(const std::wstring & origin_url) { if( config ) { - if( config->allow_all_cors_origins ) - { - return true; - } - else + if( !config->allowed_origins.empty() ) { // origin_url can be a "null" string // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin // but in such a case the "null" should be put to the config as well - return is_in_list(origin_url, config->allowed_cors_origins); + return is_in_list(origin_url, config->allowed_origins); + } + else + { + if( config->allow_all_origins ) + { + return true; + } } } @@ -212,15 +215,18 @@ void FunctionBase::AddAccessControlAllowOriginHeader(const std::wstring & origin { if( config ) { - if( config->allow_all_cors_origins ) + if( !config->allowed_origins.empty() ) { - cur->request->AddHeader(Header::access_control_allow_origin, L"*"); + // method IsOriginAvailable(..) was called beforehand so now we assume + // that the origin_url is permitted (and is valid as a header) + cur->request->AddHeader(Header::access_control_allow_origin, origin_url); } else { - // method IsCorsOriginAvailable(..) was called beforehand so now we assume - // that the origin_url is permitted - cur->request->AddHeader(Header::access_control_allow_origin, origin_url); + if( config->allow_all_origins ) + { + cur->request->AddHeader(Header::access_control_allow_origin, L"*"); + } } } } @@ -307,12 +313,47 @@ void FunctionBase::AddCorsNormalRequestHeaders(const std::wstring & origin) } +void FunctionBase::CheckCorsPreflightRequest(const std::wstring & origin, const std::wstring & method_string) +{ + pt::Space * cors_headers = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Headers"); + Request::Method method = Request::CheckRequestMethod(method_string.c_str()); + + if( IsCorsMethodAvailable(method) ) + { + bool cors_headers_available = true; + std::wstring * headers = nullptr; + + if( cors_headers && cors_headers->is_wstr() ) + { + headers = cors_headers->get_wstr(); + cors_headers_available = AreCorsHeadersAvailable(*headers); + } + + if( cors_headers_available ) + { + AddCorsPreflightRequestHeaders(origin, method, headers); + } + else + { + if( headers ) + { + log << log2 << "FunctionBase: these cors headers: " << *headers << " are not permitted in cors requests" << logend; + } + } + } + else + { + log << log2 << "FunctionBase: this method: " << method_string << " is not permitted in cors requests" << logend; + } +} + + void FunctionBase::AddResponseHeadersForOrigin(const std::wstring & origin) { if( cur->request->method == Request::Method::options ) { pt::Space * cors_method = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Method"); // FastCGI changes '-' to '_' - pt::Space * cors_headers = cur->request->headers_in.get_space_nc(L"Access_Control_Request_Headers"); + cur->request->http_status = Header::status_204_no_content; if( cors_method && cors_method->is_wstr() ) { @@ -321,35 +362,14 @@ void FunctionBase::AddResponseHeadersForOrigin(const std::wstring & origin) * https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request * (we allow Access-Control-Request-Headers not to be present) */ - Request::Method method = Request::CheckRequestMethod(cors_method->get_wstr()->c_str()); - cur->request->http_status = Header::status_204_no_content; - if( IsCorsMethodAvailable(method) ) + if( config->are_cors_preflight_requests_available ) { - bool cors_headers_available = true; - std::wstring * headers = nullptr; - - if( cors_headers && cors_headers->is_wstr() ) - { - headers = cors_headers->get_wstr(); - cors_headers_available = AreCorsHeadersAvailable(*headers); - } - - if( cors_headers_available ) - { - AddCorsPreflightRequestHeaders(origin, method, headers); - } - else - { - if( headers ) - { - log << log2 << "FunctionBase: these cors headers: " << *headers << " are not permitted in cors requests" << logend; - } - } + CheckCorsPreflightRequest(origin, *cors_method->get_wstr()); } else { - log << log2 << "FunctionBase: this method: " << *cors_method->get_wstr() << " is not permitted in cors requests" << logend; + log << log2 << "FunctionBase: cors requests are disabled" << logend; } } else @@ -374,7 +394,7 @@ void FunctionBase::CheckOriginHeader() if( origin && origin->is_wstr() ) { - if( IsCorsOriginAvailable(*origin->get_wstr()) ) + if( IsOriginAvailable(*origin->get_wstr()) ) { AddResponseHeadersForOrigin(*origin->get_wstr()); } diff --git a/winixd/functions/functionbase.h b/winixd/functions/functionbase.h index 16e96fa..7750b8a 100644 --- a/winixd/functions/functionbase.h +++ b/winixd/functions/functionbase.h @@ -114,7 +114,7 @@ public: virtual void AddAllowMethodsHeader(); virtual bool IsCorsMethodAvailable(Request::Method method); - virtual bool IsCorsOriginAvailable(const std::wstring & origin_url); + virtual bool IsOriginAvailable(const std::wstring & origin_url); virtual bool AreCorsCredentialsAvailable(); virtual bool AreCorsHeadersAvailable(const std::wstring & headers); @@ -126,6 +126,7 @@ public: virtual void AddAccessControlExposeHeadersHeader(); virtual void AddCorsPreflightRequestHeaders(const std::wstring & origin, Request::Method method, const std::wstring * request_headers); virtual void AddCorsNormalRequestHeaders(const std::wstring & origin); + virtual void CheckCorsPreflightRequest(const std::wstring & origin, const std::wstring & method_string); virtual void AddResponseHeadersForOrigin(const std::wstring & origin); virtual void CheckOriginHeader();