fixed SSRF bypass, tightened error handling

- SSRF: disable automatic redirects, manually follow up to 5 hops with
  IP re-validation at each step to prevent redirect-to-localhost bypass
- Identity file: enforce 0600 permissions on tinyweb_identity at load
  and creation to prevent other users from reading the private key
- Error messages: replace raw exception strings with generic messages
  to avoid leaking internal paths/hostnames to the UI
- DB connections: wrap all get_db() usage in try/finally to guarantee
  close() even when handlers throw mid-operation
This commit is contained in:
lichenblankie 2026-03-26 11:18:47 -07:00
parent 4899819597
commit 449174b0ca
3 changed files with 310 additions and 256 deletions

13
db.py
View file

@ -201,7 +201,18 @@ def get_site_name():
def fetch_page(url):
_validate_url_target(url)
resp = requests.get(url, timeout=10, headers={"User-Agent": "TinyWeb/1.0"})
resp = requests.get(url, timeout=10, headers={"User-Agent": "TinyWeb/1.0"}, allow_redirects=False)
# Follow redirects manually, re-validating each target
max_redirects = 5
while resp.is_redirect and max_redirects > 0:
redirect_url = resp.headers.get("Location")
if not redirect_url:
break
redirect_url = urljoin(url, redirect_url)
_validate_url_target(redirect_url)
url = redirect_url
resp = requests.get(url, timeout=10, headers={"User-Agent": "TinyWeb/1.0"}, allow_redirects=False)
max_redirects -= 1
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")