tornadoweb / tornadoweb/tornado
[Feature request] Options for block local request to prevent SSRF attack.
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 22.2k
- Forks
- 5.6k
- Avg merge
- 3h 42m
- Merged PRs (30d)
- 16
Description
I want to request a resource that provide by user. But I don't want let users access my local network resource to prevent SSRF attack.
Here is my workaround:
@functools.lru_cache(maxsize=100)
def check_sock(x):
s = socket.socket(x[0],x[1])
s.settimeout(1)
ret = s.connect_ex(x[-1])==0
s.close()
return ret
client = tornado.httpclient.AsyncHTTPClient()
if self.block_SSRF == True:
parsed = urllib.parse.urlsplit(_unicode(url))
if parsed.scheme not in ("http", "https"):
raise ValueError("Unsupported url scheme: %s" % url)
netloc = parsed.netloc
if "@" in netloc:
userpass, _, netloc = netloc.rpartition("@")
host, port = httputil.split_host_and_port(netloc)
if port is None:
port = 443 if parsed.scheme == "https" else 80
if re.match(r"^\[.*\]$", host):
host = host[1:-1]
host_ips = list(filter(lambda x:x[1]==socket.SOCK_STREAM,socket.getaddrinfo(host,port)))
host_ips_global = list(filter(lambda x:ipaddress.ip_address(x[-1][0]).is_global,host_ips))
host_ips_connectable = [x for x in host_ips_global if check_sock(x)]
if len(host_ips_global) == 0:
raise HTTPClientError(code=400,msg="SSRF blocked. Request to local network are blocked due to SSRF protection enabled")
if len(host_ips_connectable) == 0:
raise HTTPClientError(code=500,msg="Not connectable")
if "follow_redirects" in request and request["follow_redirects"] == True:
raise HTTPClientError(code=400,msg="SSRF blocked, follow_redirects are not available if SSRF protection enabled")
request["follow_redirects"] = False
host_ip = host_ips_connectable[0]
client = tornado.httpclient.AsyncHTTPClient(hostname_mapping={host:host_ip})
response = await client.fetch(url,**request)
But there still has a lot of problem. For example, if a external server returned me 301 Redirect 127.0.0.1:3679. My server will still access it and cause SSRF exploit.
Or I have to disable follow_redirects and reimplement follow_redirects by my self...
Even if I reimplement follow_redirects, check host ip on every request , Attackers can still use DNS Rebinding to attack my server......
Feautre request
Can I request a feature that there is a hook can let us check request IP before the request establish, and a flag for SSRF protection, to prevent requests access local resources?
In the source code of tornado, I want if the SSRF protection enabled, it will resolve host IP and check it before connection establish.
And connect to resolved IP directly instead resolve it again to prevent DNS Rebinding
if SSRF_protection == True:
host_ip_list = list(set([v[-1][0] for v in socket.getaddrinfo(host,0)])) # one host may resolved to multiple IP addresses.
check_host_ip(host_ip_list) #User can define this function
for index,host_ip in enumerate(host_ip_list):
try:
self.tcp_client.connect(host_ip , port, af=af,
ssl_options=ssl_options,
max_buffer_size=self.max_buffer_size,
callback=self._on_connect)
except Exception as e:
if index == len(host_ip_list) -1:
raise e
# fallback to next ip
else:
self.tcp_client.connect(host , port, af=af,
ssl_options=ssl_options,
max_buffer_size=self.max_buffer_size,
callback=self._on_connect)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading Tornado's HTTP client connection path, including the tcp_client.connect flow shown in the issue; no source file or test is named. Trace redirects, hostname resolution, and connection establishment to determine where an IP-check hook and SSRF option would fit. Done means the requested protection handles redirects and DNS rebinding without requiring callers to reimplement redirects.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, networking, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 28/100