https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-SECURE_HSTS_SECONDS
https://stackoverflow.com/questions/56572638/hsts-and-https-settings-invalid
https://stackoverflow.com/questions/41483068/djangos-httpresponseredirect-is-http-instead-of-https/41488430#41488430
If web browser / load-balancer reverse proxy to django server(or dev server), if proxy server establishes connection with django server via https, then it does not matter what client request is (http/https), the connection between django and load-balancer will always be secure, django is_secure() checks client request via https:// or connection will always return true. If load-balancer and django server connection is not https, (which is by default), then is_scure() will always return False, causing SECURE_HSTS_SECONDS in settings.py in django to enforce HSTS connection wont work.
https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-SECURE_PROXY_SSL_HEADER
when SECURE_HSTS_SECONDS is sucessfully set up in django , django server will respond request with
Response Headers:
This will enforce browser to access the current domain through https for the next minuet(60 seconds)
To fix the issue
1) In load-balancer, web browser, need to have (https://stackoverflow.com/questions/41483068/djangos-httpresponseredirect-is-http-instead-of-https/41488430#41488430)
X-Forwarded-Proto
browser header on when reverse proxy request to django dev server.
(https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto#:~:text=The%20X%2DForwarded%2DProto%20(,your%20proxy%20or%20load%20balancer.)
The X-Forwarded-Proto (XFP) header is a de-facto standard header for identifying the protocol (HTTP or HTTPS) that a client used to connect to your proxy or load balancer.
To configure this for nginx (https://stackoverflow.com/questions/41483068/djangos-httpresponseredirect-is-http-instead-of-https/41488430#41488430) :
proxy_redirect off;
proxy_set_header X-Forwarded-Proto $scheme;
To configure this for apache2 2.4 :
https://webmasters.stackexchange.com/questions/97005/setting-x-forwarded-proto-under-apache-2-4
https://serverfault.com/questions/257616/requestheader-with-apache-environment-variable
RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
RequestHeader set X-Forwarded-SSL expr=%{HTTPS}
If it doesn't work, you may need to install and enable the module mod_headers.
For docker-compose httpd, mode_headers, ensure http.conf has
LoadModule headers_module modules/mod_headers.so
For RequesetHeader for specific site, go to sites/your-site-ssl.conf (this is specified in httpd.conf like
# To Load Customer VirtualHost Configuration files
IncludeOptional conf/sites/*.conf)
add
<VirtualHost _default_:443>
# Notify API server the client request being proxy reveresed is http / https (https to trigger hsts in API server)
RequestHeader set X-Forwarded-Proto expr=%{REQUEST_SCHEME}
RequestHeader set X-Forwarded-SSL expr=%{HTTPS}
# Proxy reverse rules
ProxyPreserveHost On
# ProxyPass / http://django_ip:8000/
# ProxyPassReverse / http://django_ip:8000/
# Fortidemo API endpoint
ProxyPassMatch .*/saml/.* http://django_ip:8000
ProxyPassReverse .*/saml/.* http://django_ip:8000
ProxyPassMatch .*/api/.* http://django_ip:8000
ProxyPassReverse .*/api/.* http://django_ip:8000
# Django css end point
ProxyPassMatch .*/static/.* http://django_ip:8000
ProxyPassReverse .*/static/.* http://django_ip:8000
</VirtualHost>
2) In django settings.py
add the following :
# httpd has x-forwarded proto to indicate client http/https being forwarded, this indcates django only accept https
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Increase its value after test is success
# After everything goes well , good default value is to set it to a month ( 2592000):
https://learndjango.com/tutorials/django-best-practices-security#:~:text=The%20SECURE_HSTS_SECONDS%20setting%20is%20set,month%2C%202%2C592%2C000%20seconds%2C%20instead.
SECURE_HSTS_SECONDS = 60
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_PROXY_SSL_HEADER
is (https://docs.djangoproject.com/en/4.1/ref/settings/#std-setting-SECURE_HSTS_SECONDS)
A tuple representing an HTTP header/value combination that signifies a request is secure. This controls the behavior of the request object’s is_secure() method.
By default, is_secure() determines if a request is secure by confirming that a requested URL uses https://. This method is important for Django’s CSRF protection, and it may be used by your own code or third-party apps.
If your Django app is behind a proxy, though, the proxy may be “swallowing” whether the original request uses HTTPS or not. If there is a non-HTTPS connection between the proxy and Django then is_secure() would always return False – even for requests that were made via HTTPS by the end user. In contrast, if there is an HTTPS connection between the proxy and Django then is_secure() would always return True – even for requests that were made originally via HTTP.
In this situation, configure your proxy to set a custom HTTP header that tells Django whether the request came in via HTTPS, and set SECURE_PROXY_SSL_HEADER so that Django knows what header to look for.
Set a tuple with two elements – the name of the header to look for and the required value. For example:
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
This tells Django to trust the X-Forwarded-Proto header that comes from our proxy and that the request is guaranteed to be secure (i.e., it originally came in via HTTPS) when:
the header value is 'https', or
its initial, leftmost value is 'https' in the case of a comma-separated list of protocols (e.g. 'https,http,http'
T
No comments:
Post a Comment