- Client IP: only trust X-Forwarded-For from RFC 1918 proxy IPs - Webhook auth: handle nil/blank secret with controlled error, not 500 crash - Sudo redirect: reuse validated return_path? from login to prevent open redirect - Map live: remove redundant inline script (ensureLeaflet hook handles loading) - Bang calls: convert crash-prone exact matches to case in QR live and API controllers
62 lines
1.9 KiB
Elixir
62 lines
1.9 KiB
Elixir
defmodule ToweropsWeb.RemoteIp do
|
|
@moduledoc """
|
|
Shared helpers for extracting and formatting remote IP addresses.
|
|
"""
|
|
|
|
import Plug.Conn, only: [get_req_header: 2]
|
|
|
|
@spec from_conn(Plug.Conn.t()) :: String.t() | nil
|
|
def from_conn(conn) do
|
|
if trusted_proxy?(conn.remote_ip) do
|
|
case get_req_header(conn, "x-forwarded-for") do
|
|
[forwarded | _] ->
|
|
forwarded
|
|
|> String.split(",")
|
|
|> List.first()
|
|
|> String.trim()
|
|
|
|
[] ->
|
|
from_x_real_ip_or_tuple(conn)
|
|
end
|
|
else
|
|
format(conn.remote_ip)
|
|
end
|
|
end
|
|
|
|
@spec from_socket(Phoenix.Socket.t()) :: String.t() | nil
|
|
def from_socket(socket) do
|
|
# Use peer_data stashed into socket assigns at connect time via
|
|
# `connect_info: [:peer_data]`. This is the documented Phoenix API
|
|
# and is adapter-agnostic (works with both Bandit and Cowboy).
|
|
case socket.assigns[:peer_data] do
|
|
%{address: ip} when is_tuple(ip) -> format(ip)
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
@spec format(tuple() | nil) :: String.t() | nil
|
|
def format({a, b, c, d}), do: "#{a}.#{b}.#{c}.#{d}"
|
|
|
|
def format({a, b, c, d, e, f, g, h}) do
|
|
Enum.map_join([a, b, c, d, e, f, g, h], ":", &(&1 |> Integer.to_string(16) |> String.upcase()))
|
|
end
|
|
|
|
def format(_), do: nil
|
|
|
|
defp from_x_real_ip_or_tuple(conn) do
|
|
case get_req_header(conn, "x-real-ip") do
|
|
[real_ip | _] -> real_ip
|
|
[] -> format(conn.remote_ip)
|
|
end
|
|
end
|
|
|
|
# Only trust forwarding headers from known proxy/RFC 1918 addresses.
|
|
# Direct internet clients can spoof x-forwarded-for, so we must not
|
|
# trust it unless the immediate peer is a known reverse proxy.
|
|
defp trusted_proxy?({10, _b, _c, _d}), do: true
|
|
defp trusted_proxy?({172, b, _c, _d}) when b >= 16 and b <= 31, do: true
|
|
defp trusted_proxy?({192, 168, _c, _d}), do: true
|
|
defp trusted_proxy?({127, _b, _c, _d}), do: true
|
|
defp trusted_proxy?({0xFD00, _, _, _, _, _, _, _}), do: true
|
|
defp trusted_proxy?(_), do: false
|
|
end
|