Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#46](https://github.com/WSH032/fastapi-proxy-lib/pull/46) - fix: don't use module-level logging methods. Thanks [@dvarrazzo](https://github.com/dvarrazzo)
- [#49](https://github.com/WSH032/fastapi-proxy-lib/pull/49) - fix!: bump `httpx-ws >= 0.7.1` to fix frankie567/httpx-ws#29. Thanks [@WSH032](https://github.com/WSH032)!

### Security

- [#50](https://github.com/WSH032/fastapi-proxy-lib/pull/50) - fix(security): add `localhost` rule to `default_proxy_filter`. Thanks [@WSH032](https://github.com/WSH032)!

### Removed

- [#49](https://github.com/WSH032/fastapi-proxy-lib/pull/49) - Drop support for `Python 3.8`.
Expand Down
15 changes: 12 additions & 3 deletions src/fastapi_proxy_lib/core/_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,12 @@ def check_http_version(
def default_proxy_filter(url: httpx.URL) -> Union[None, str]:
"""Filter by host.

If the host of url is ip address, which is not global ip address, then will reject it.
Reject the following hosts:

- if the host is ip address, and is not global ip address. e.g:
- `http://127.0.0.1`
- `http://192.168.0.1`
- if the host contains "localhost".

Warning:
It will consumption time: 3.22~4.7 µs ± 42.6 ns.
Expand All @@ -383,8 +388,12 @@ def default_proxy_filter(url: httpx.URL) -> Union[None, str]:
str: should rejetc the proxy request.
The `str` is the reason of reject.
"""
host = url.host
if "localhost" in host:
return "Deny proxy for localhost."

try:
ip_address = ipaddress.ip_address(url.host)
ip_address = ipaddress.ip_address(host)
except ValueError:
return None

Expand All @@ -403,7 +412,7 @@ def warn_for_none_filter(proxy_filter: None) -> ProxyFilterProto: ...


def warn_for_none_filter(
proxy_filter: Union[ProxyFilterProto, None]
proxy_filter: Union[ProxyFilterProto, None],
) -> ProxyFilterProto:
"""Check whether the argument `proxy_filter` is None.

Expand Down
31 changes: 27 additions & 4 deletions tests/test_core_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,33 @@ async def _() -> JSONResponse:

def test_func_default_proxy_filter() -> None:
"""Test `fastapi_proxy_lib.core._tool.default_proxy_filter()`."""
# 禁止访问私有IP
assert default_proxy_filter(httpx.URL("http://www.example.com")) is None
assert default_proxy_filter(httpx.URL("http://1.1.1.1")) is None
assert default_proxy_filter(httpx.URL("http://127.0.0.1")) is not None
# prevent access to private ip

def _check(url: str, should_pass: bool) -> None:
httpx_url = httpx.URL(url)
if should_pass:
assert default_proxy_filter(httpx_url) is None
else:
assert default_proxy_filter(httpx_url) is not None

def should_pass(url: str) -> None:
_check(url, True)

def should_not_pass(url: str) -> None:
_check(url, False)

# passed
should_pass("http://www.example.com")
should_pass("http://www.example.com/path")
should_pass("http://1.1.1.1")

# private ip
should_not_pass("http://127.0.0.1")
should_not_pass("http://[::1]")
should_not_pass("http://192.168.0.1")
should_not_pass("http://10.0.0.1")
should_not_pass("http://172.31.0.1")
should_not_pass("http://localhost")


def test_non_filter_warning_for_forward_proxy() -> None:
Expand Down
Loading