Add domain allowlist and private IP blocking to prevent SSRF attacks#5
Conversation
Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
…nt entry Co-authored-by: abhimehro <84992105+abhimehro@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request enhances the folder URL validation function to prevent Server-Side Request Forgery (SSRF) attacks by implementing domain allowlisting and blocking private IP addresses. The changes address a security vulnerability where the previous implementation only validated the HTTPS protocol without checking the target host.
Key changes:
- Added domain allowlist validation restricting URLs to github.com and githubusercontent.com (including subdomains)
- Implemented IP address validation to block private, loopback, link-local, and reserved IP ranges for both IPv4 and IPv6
- Added comprehensive error logging for different validation failure scenarios
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
main.py
Outdated
| # First, check if the hostname is an IP address | ||
| # If it is, ensure it's not a private IP (reject all direct IP access) | ||
| try: | ||
| ip = ipaddress.ip_address(host) | ||
| if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_reserved: | ||
| log.warning(f"Skipping URL with private/internal IP address: {url}") | ||
| return False | ||
| except ValueError: | ||
| # Not an IP address, it's a domain name - continue with domain validation | ||
| pass |
There was a problem hiding this comment.
This validation only checks the hostname at URL parsing time, which doesn't protect against DNS rebinding attacks. An attacker could register a subdomain under github.com or githubusercontent.com (if possible) or exploit Time-of-Check-Time-of-Use (TOCTOU) issues where a domain resolves to a safe IP during validation but to a private IP during the actual HTTP request. Consider implementing additional protections such as: 1) Resolving the hostname to IP addresses before making requests and validating those IPs, or 2) Configuring the httpx client with connection restrictions to block private IP ranges at the connection level.
| ALLOWED_DOMAINS = { | ||
| "github.com", | ||
| "githubusercontent.com", | ||
| } |
There was a problem hiding this comment.
The ALLOWED_DOMAINS constant is defined inside the function, which means it's recreated on every function call. Consider moving this to a module-level constant (e.g., after the imports or with other constants like API_BASE) to improve performance and make it easier to maintain and test. This would also make it more discoverable for future configuration needs.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The folder URL validation only checked HTTPS protocol, allowing SSRF attacks against internal services via URLs like
https://localhost:8080/adminorhttps://192.168.1.1/.Changes
github.comandgithubusercontent.com(including subdomains)Implementation
Attack Vectors Mitigated
https://localhost:8080/admin,https://127.0.0.1/https://192.168.1.1/,https://10.0.0.1/,https://172.16.0.1/https://[::1]/,https://[fd00::1]/https://evil.com/maliciousOriginal prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.