# PYTHON-LANG-SEC-060: HTTP Request Without TLS (requests library)

> **Severity:** MEDIUM | **CWE:** CWE-319 | **OWASP:** A02:2021

- **Language:** Python
- **Category:** Python Core
- **URL:** https://codepathfinder.dev/registry/python/lang/PYTHON-LANG-SEC-060
- **Detection:** `pathfinder scan --ruleset python/PYTHON-LANG-SEC-060 --project .`

## Description

Using the requests library with http:// URLs transmits all request and response data
in plaintext over TCP without TLS encryption. Authentication headers, session tokens,
API keys, user data, and all other HTTP content are visible to network observers and
MITM attackers.

The requests library makes HTTPS requests trivially easy — simply use https:// in the
URL. The library handles TLS certificate verification by default. There is rarely a
legitimate reason to use http:// URLs in production application code, except for
localhost health checks or explicitly non-sensitive endpoints.

This rule audits requests.get(), requests.post(), requests.put(), requests.patch(),
requests.delete(), requests.head(), and requests.Session() calls to flag HTTP URLs.


## Vulnerable Code

```python
import requests as http_requests
import urllib.request
import ftplib
import telnetlib

# SEC-060: requests with HTTP
resp = http_requests.get("http://example.com/api")
http_requests.post("http://example.com/data", data={"key": "val"})
```

## Secure Code

```python
import requests

# INSECURE: HTTP URL in requests call
# response = requests.get("http://api.example.com/data")
# response = requests.post("http://api.example.com/login", data=credentials)

# SECURE: Use HTTPS URLs
def fetch_api_data(resource_id: str) -> dict:
    response = requests.get(
        f"https://api.example.com/data/{resource_id}",
        timeout=30,
    )
    response.raise_for_status()
    return response.json()

# SECURE: Use a session with base URL and HTTPS only
class ApiClient:
    BASE_URL = "https://api.example.com"

    def __init__(self, api_key: str):
        self.session = requests.Session()
        self.session.headers.update({"Authorization": f"Bearer {api_key}"})

    def get(self, path: str) -> dict:
        response = self.session.get(f"{self.BASE_URL}{path}", timeout=30)
        response.raise_for_status()
        return response.json()

```

## Detection Rule (Python SDK)

```python
from rules.python_decorators import python_rule
from codepathfinder import calls, QueryType

class RequestsLib(QueryType):
    fqns = ["requests"]


@python_rule(
    id="PYTHON-LANG-SEC-060",
    name="HTTP Request Without TLS",
    severity="MEDIUM",
    category="lang",
    cwe="CWE-319",
    tags="python,requests,http,insecure-transport,CWE-319",
    message="HTTP URL used in requests call. Use HTTPS for sensitive data transmission.",
    owasp="A02:2021",
)
def detect_requests_http():
    """Detects requests library calls (audit for HTTP URLs)."""
    return RequestsLib.method("get", "post", "put", "delete", "patch", "head", "request")
```

## How to Fix

- Replace all http:// URLs with https:// URLs in requests calls.
- Use HTTPS for all API endpoints, even internal service-to-service communication.
- Set a base URL with HTTPS in requests.Session() and use relative paths to prevent accidentally mixing HTTP and HTTPS.
- Configure HSTS on your servers so clients are redirected to HTTPS even if they accidentally use HTTP URLs.
- Add URL validation in your application that rejects or upgrades HTTP URLs to HTTPS before making requests.

## Security Implications

- **API Key and Token Interception:** API keys, OAuth tokens, and authentication headers transmitted in HTTP requests are
visible in plaintext to anyone on the network path, enabling immediate credential theft
and unauthorized API access.

- **Sensitive Data Exposure:** Request bodies, query parameters, and response data containing user information,
financial data, health records, or other sensitive content are exposed to network
observers without TLS encryption.

- **HTTP Response Tampering:** Without TLS, responses can be modified in transit. An attacker can inject malicious
content, alter API responses to change application behavior, or redirect to malicious
endpoints by manipulating redirect responses.

- **Mixed Content and Redirect Downgrade:** Applications that start with HTTPS but follow redirects to HTTP URLs can be downgraded
to HTTP mid-session. The requests library follows redirects by default, potentially
following a redirect from https:// to http:// and exposing session credentials.


## FAQ

**Q: Is there any legitimate reason to use http:// in a requests call?**

Very rarely. Legitimate exceptions include: connecting to localhost health check
endpoints that don't expose sensitive data, connecting to a local proxy that handles
TLS termination (though even then https:// is preferred), and testing HTTP redirect
behavior. Any http:// URL in production code should be explicitly documented with
the reason it cannot use HTTPS.


**Q: Does the requests library follow HTTP redirects to HTTPS automatically?**

requests follows redirects by default (allow_redirects=True). If an HTTP URL redirects
to HTTPS, the library will follow the redirect. However, the initial HTTP request
including any headers sent with it is still transmitted in plaintext before the
redirect occurs. Always use the final HTTPS URL directly.


**Q: What is HTTP Strict Transport Security and does it help here?**

HSTS is a server-side security header that tells browsers to always use HTTPS for
a domain. The requests library does not implement HSTS by default, so server-side
HSTS does not prevent the Python client from making an initial HTTP request. Fix
the URL in the client code rather than relying on server-side HSTS.


**Q: How do I detect and alert on HTTP URLs in production?**

Add URL validation to your HTTP client wrapper that raises an error or logs a warning
when an HTTP (non-HTTPS) URL is used. This can be implemented as a custom requests
Session subclass that overrides request() to validate URLs before making calls.


**Q: Does this rule catch HTTP URLs stored in environment variables?**

If the URL is read from an environment variable (os.environ.get("API_URL")), static
analysis cannot determine whether the URL uses HTTP or HTTPS at the call site.
Supplement static analysis with URL validation in the application code that rejects
or upgrades HTTP URLs at runtime.


**Q: What about HTTP URLs for webhook receivers or test endpoints?**

Webhook receivers that accept incoming HTTP requests are different from outbound HTTP
requests. This rule covers outbound requests made by the Python application. Webhook
endpoints should be served over HTTPS to protect the webhook payload from interception.


## References

- [CWE-319: Cleartext Transmission of Sensitive Information](https://cwe.mitre.org/data/definitions/319.html)
- [Python requests documentation](https://requests.readthedocs.io/en/latest/)
- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Security_Cheat_Sheet.html)
- [OWASP Top 10 A02:2021 Cryptographic Failures](https://owasp.org/Top10/A02_2021-Cryptographic_Failures/)
- [HTTP Strict Transport Security (HSTS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)

---

Source: https://codepathfinder.dev/registry/python/lang/PYTHON-LANG-SEC-060
Code Pathfinder — Open source, type-aware SAST with cross-file dataflow analysis
