add config options for testing the Origin header in cors requests

- allow_all_cors_origins - whether or not to allow all origins
- allowed_cors_origins - a list of allowed origins (used if allow_all_cors_origins is false)
This commit is contained in:
Tomasz Sowa 2022-08-31 20:42:42 +02:00
parent 778ed01a55
commit a19158cb62
7 changed files with 117 additions and 13 deletions

View File

@ -350,6 +350,9 @@ void Config::AssignValues()
add_header_cache_no_store_in_htmx_request = Bool(L"add_header_cache_no_store_in_htmx_request", true);
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);
}

View File

@ -976,7 +976,17 @@ 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
// used only if allow_all_cors_origins is false
// default: empty
std::vector<std::wstring> allowed_cors_origins;
Config();

View File

@ -1628,8 +1628,23 @@ void slice_by(const std::wstring & str, wchar_t c, std::vector<std::wstring> & o
}
bool is_in_list(const std::wstring & item, const std::vector<std::wstring> & list)
{
return is_in_list_generic(item, list);
}
bool is_in_list(const std::wstring & item, const std::list<std::wstring> & list)
{
return is_in_list_generic(item, list);
}
bool is_in_list(const std::wstring & item, const std::set<std::wstring> & list)
{
return is_in_list_generic(item, list);
}
} // namespace Winix

View File

@ -36,6 +36,8 @@
#define headerfile_winix_core_misc
#include <string>
#include <vector>
#include <set>
#include <sstream>
#include <ctime>
#include <cstdio>
@ -1032,6 +1034,28 @@ void timespec_to_stream_with_unit(timespec & val, pt::Stream & stream);
void slice_by(const std::wstring & str, wchar_t c, std::vector<std::wstring> & out);
bool is_in_list(const std::wstring & item, const std::vector<std::wstring> & list);
bool is_in_list(const std::wstring & item, const std::list<std::wstring> & list);
bool is_in_list(const std::wstring & item, const std::set<std::wstring> & list);
template<typename ContainerType>
bool is_in_list_generic(const std::wstring & item, const ContainerType & list)
{
for(const std::wstring & str : list)
{
if( str == item )
{
return true;
}
}
return false;
}
} // namespace Winix

View File

@ -1536,10 +1536,25 @@ void Request::PrepareSessionCookie()
}
void Request::CheckOriginHeader()
{
pt::Space * origin = headers_in.get_space_nc(L"Origin");
if( origin && origin->is_wstr() && !out_headers.has_key(Header::access_control_allow_origin) )
{
if( function && function->IsCorsOriginAvailable(*origin->get_wstr()) )
{
function->AddAccessControlAllowOriginHeader(*origin->get_wstr());
}
}
}
void Request::PrepareHeaders(bool compressing, int compress_encoding, size_t output_size)
{
PrepareSessionCookie();
CheckOriginHeader();
if( send_as_attachment )
{

View File

@ -595,6 +595,7 @@ private:
int SelectDeflateVersion();
void SelectCompression(size_t source_len, bool & compression_allowed, int & compression_encoding);
void PrepareSessionCookie();
void CheckOriginHeader();
void PrepareHeaders(bool compressing, int compress_encoding, size_t output_size);
void ModifyStatusForRedirect();
void PrepareSendFileHeaderForStaticMountpoint();

View File

@ -151,8 +151,22 @@ bool FunctionBase::IsCorsMethodAvailable(Request::Method method)
bool FunctionBase::IsCorsOriginAvailable(const std::wstring & origin_url)
{
// true by default for all urles
return true;
if( config )
{
if( config->allow_all_cors_origins )
{
return true;
}
else
{
// 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 false;
}
@ -175,10 +189,25 @@ void FunctionBase::AddAccessControlAllowMethodsHeader(Request::Method method)
/*
* origin_url is the value of Origin header sent by the client
* origin_url can be: "null"
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
*
*/
void FunctionBase::AddAccessControlAllowOriginHeader(const std::wstring & origin_url)
{
cur->request->AddHeader(Header::access_control_allow_origin, origin_url);
if( config )
{
if( config->allow_all_cors_origins )
{
cur->request->AddHeader(Header::access_control_allow_origin, L"*");
}
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);
}
}
}
@ -246,27 +275,34 @@ void FunctionBase::MakeOptions()
* (we allow Access-Control-Request-Headers not to be present)
*/
Request::Method method = Request::CheckRequestMethod(cors_method->get_wstr()->c_str());
bool cors_available = false;
if( IsCorsMethodAvailable(method) && IsCorsOriginAvailable(*cors_origin->get_wstr()) )
{
bool cors_available = true;
cors_available = true;
if( cors_headers && cors_headers->is_wstr() )
{
cors_available = AreCorsHeadersAvailable(*cors_headers->get_wstr());
}
}
if( cors_available )
if( cors_available )
{
AddAccessControlAllowMethodsHeader(method);
AddAccessControlAllowOriginHeader(*cors_origin->get_wstr());
AddAccessControlMaxAgeHeader();
if( cors_headers && cors_headers->is_wstr() )
{
AddAccessControlAllowMethodsHeader(method);
AddAccessControlAllowOriginHeader(*cors_origin->get_wstr());
AddAccessControlMaxAgeHeader();
if( cors_headers && cors_headers->is_wstr() )
{
AddAccessControlAllowHeadersHeader(*cors_headers->get_wstr());
}
AddAccessControlAllowHeadersHeader(*cors_headers->get_wstr());
}
log << log3 << "FunctionBase: cors requests are permitted" << logend;
}
else
{
log << log2 << "FunctionBase: cors requests are not permitted" << logend;
}
}
else