Changeset

13126:d043834f15d2

mod_http: Use RFC 7239 Forwarded header to find original client IP Prefer over X-Forwarded-* since it has an actual specification. Main practical difference is that Forwarded may carry more properties than only the IP address since it is a structured header. Since we parse it into an array, it is easier to do the logical thing and iterate backwards trough proxies until an untrusted one is encountered. Compare the handling of X-Forwarded-For. The 'secure' field now accounts for the full chain of proxies, which must be secure all the way to be considered secure.
author Kim Alvefur <zash@zash.se>
date Sat, 03 Jun 2023 17:10:04 +0200 (19 months ago)
parents 13125:90394be5e6a5
children 13127:f45a29b32f7a
files CHANGES plugins/mod_http.lua
diffstat 2 files changed, 21 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Sat Jun 03 17:10:12 2023 +0200
+++ b/CHANGES	Sat Jun 03 17:10:04 2023 +0200
@@ -42,6 +42,7 @@
 - mod_blocklist: New option 'migrate_legacy_blocking' to disable migration from mod_privacy
 - Ability to use SQLite3 storage using LuaSQLite3 instead of LuaDBI
 - Moved all modules into the Lua namespace `prosody.`
+- Forwarded header from RFC 7239 supported
 
 ## Removed
 
--- a/plugins/mod_http.lua	Sat Jun 03 17:10:12 2023 +0200
+++ b/plugins/mod_http.lua	Sat Jun 03 17:10:04 2023 +0200
@@ -15,7 +15,8 @@
 local moduleapi = require "prosody.core.moduleapi";
 local url_parse = require "socket.url".parse;
 local url_build = require "socket.url".build;
-local normalize_path = require "prosody.util.http".normalize_path;
+local http_util = require "prosody.util.http";
+local normalize_path = http_util.normalize_path;
 local set = require "prosody.util.set";
 local array = require "util.array";
 
@@ -319,6 +320,24 @@
 local function get_forwarded_connection_info(request) --> ip:string, secure:boolean
 	local ip = request.ip;
 	local secure = request.secure; -- set by net.http.server
+
+	local forwarded = http_util.parse_forwarded(request.headers.forwarded);
+	if forwarded then
+		request.forwarded = forwarded;
+		for i = #forwarded, 1, -1 do
+			local proxy = forwarded[i]
+			if is_trusted_proxy(ip) then
+				ip = normal_ip(proxy["for"]);
+				secure = secure and proxy.proto == "https";
+			else
+				break
+			end
+		end
+
+		-- Ignore legacy X-Forwarded-For and X-Forwarded-Proto, handling both seems unfeasible.
+		return ip, secure;
+	end
+
 	local forwarded_for = request.headers.x_forwarded_for;
 	if forwarded_for then
 		-- luacheck: ignore 631