Mercurial > hg > orthanc-dicomweb
changeset 790:9904dba50f88 default tip
new AllowedHosts configuration
| author | Alain Mazy <am@orthanc.team> |
|---|---|
| date | Mon, 21 Sep 2026 18:35:01 +0200 |
| parents | 67080662c302 |
| children | |
| files | NEWS Plugin/Configuration.cpp |
| diffstat | 2 files changed, 47 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- 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) =========================
--- 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 <boost/regex.hpp> #include <boost/lexical_cast.hpp> #include <boost/algorithm/string/predicate.hpp> +#include <boost/regex.hpp> // 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<std::string>& allowedHosts) + { + bool foundTrustedHost = false; + for (std::set<std::string>::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<std::string> 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(); }
