# HG changeset patch # User Alain Mazy # Date 1790008501 -7200 # Node ID 9904dba50f88cb4553b45ec0bbb16b850f57947c # Parent 67080662c302eeee616734585e0d1a50bf92af23 new AllowedHosts configuration diff -r 67080662c302 -r 9904dba50f88 NEWS --- a/NEWS Thu Sep 17 14:12:41 2026 +0200 +++ b/NEWS Mon Sep 21 18:35:01 2026 +0200 @@ -1,3 +1,12 @@ +Pending changes in the mainline +=============================== + +* New configuration "AllowedHosts" that defines a list of trusted hosts for the + "Host", "X-Forwarded-Host", "Forwarded" HTTP headers. + POSSIBLE BREAKING CHANGE: if Orthanc is behind a reverse proxy, make sure to either + define "Host" or "AllowedHosts" in the "DicomWeb" configuration section. + + Version 1.24 (2026-08-19) ========================= diff -r 67080662c302 -r 9904dba50f88 Plugin/Configuration.cpp --- a/Plugin/Configuration.cpp Thu Sep 17 14:12:41 2026 +0200 +++ b/Plugin/Configuration.cpp Mon Sep 21 18:35:01 2026 +0200 @@ -35,6 +35,7 @@ #include #include #include +#include // Assume Latin-1 encoding by default (as in the Orthanc core) @@ -533,14 +534,34 @@ } + static bool IsAllowedHost(const std::string& host, const std::set& allowedHosts) + { + bool foundTrustedHost = false; + for (std::set::const_iterator + allowedHost = allowedHosts.begin(); allowedHost != allowedHosts.end(); ++allowedHost) + { + boost::regex pattern(Orthanc::Toolbox::WildcardToRegularExpression(*allowedHost)); + + if (boost::regex_match(host, pattern)) + { + foundTrustedHost = true; + break; + } + } + + return foundTrustedHost; + } + std::string GetBasePublicUrl(const HttpHeaders& headers) { assert(dicomWebConfiguration_.get() != NULL); std::string host = dicomWebConfiguration_->GetStringValue("Host", ""); + std::set allowedHosts; + dicomWebConfiguration_->LookupSetOfStrings(allowedHosts, "AllowedHosts", ""); bool https = dicomWebConfiguration_->GetBooleanValue("Ssl", false); std::string forwardedHost, forwardedProto; - if (host.empty() && + if (host.empty() && LookupHttpHeader2(forwardedHost, headers, "x-forwarded-host") && LookupHttpHeader2(forwardedProto, headers, "x-forwarded-proto")) { @@ -599,6 +620,22 @@ host = "localhost:8042"; } + if (allowedHosts.size() == 0) + { + if (!boost::starts_with(host, "localhost") && !boost::starts_with(host, "127.0.0.1")) // always trust localhost + { + throw Orthanc::OrthancException( + Orthanc::ErrorCode_InternalError, + std::string("DICOMWeb plugin: no 'Host' defined and no 'AllowedHosts' defined although there are forwarded HTTP headers. Unable to trust the forwarded HTTP headers for host '") + host + "'."); + } + } + else if (!IsAllowedHost(host, allowedHosts)) + { + throw Orthanc::OrthancException( + Orthanc::ErrorCode_InternalError, + std::string("DICOMWeb plugin: no 'Host' defined and the forwarded HTTP headers did not match any of the 'AllowedHosts'. Unable to trust the forwarded HTTP headers for host '") + host + "'."); + } + return (https ? "https://" : "http://") + host + GetPublicRoot(); }