Django rest framework: Mixed content - django

I am quite new in Django rest framework. I have a project which contain Django restframework API inside a Django site project(with simple frontend code) and it works fine in my local environment. However in the production domain(HTTPS) it shows as below:
Mixed Content: The page at 'https://<my production domain>/audience/labeling_jobs/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://<my production domain>/audience/labeling_jobs/api/jobs/?page=2'. This request has been blocked; the content must be served over HTTPS.
I have setup the configuration about SSL/HTTPS according to Django document SSL/HTTPS beforehand but it still got this error.
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True
It seems that the Django rest cannot get the correct HTTPS, but it works fine with other django paths, they can be showed on the production page.
Is there anything I have missed?
EDIT:
Below is the code which request the django rest api:
the function is trying to shows the django labeling_jobs data via restframework API, and paging.
{% block custom_script %}
<script type="module">
const Jobs = {
data() {
return {
job_api_url: '{% url "labeling_jobs:api-job-detail" %}',
data: '',
next_page_url: null,
previous_page_url: null,
}
},
mounted() {
this.getJobs()
},
computed: {
jobs() {
return this.data.results
}
},
methods: {
async getJobs(url) {
url = url ? url : this.job_api_url
const response = await axios.get(url)
this.data = await response.data
this.next_page_url = this.data.next
this.previous_page_url = this.data.previous
},
next_page() {
console.log(this.next_page_url)
if (this.next_page_url) {
this.getJobs(this.next_page_url)
}
},
previous_page() {
console.log(this.previous_page_url)
if (this.previous_page_url) {
this.getJobs(this.previous_page_url)
}
}
}
}
const app = Vue.createApp(Jobs)
app.config.compilerOptions.delimiters = ['[[', ']]']
app.mount('#wrapper')
</script>
{% endblock %}
EDIT 2
I found the problem is something related to the paging django restframework, when I place the API url on the browser with production host view them under django rest ui, it is OK, but it fails when I try to move to next page.

I have fixed this problem,
by adding <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> in the head of base.html and adding the following config in the settings.py :
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('X-FORWARDED-PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
And it can show the contents by sending the correct HTTPS url without redirect to the wrong HTTP domain name.
But I still think it is not a best answer, I will dive into django restframework pagination maybe with relative url to try to get something more robust.

Related

CSRF cookie not set in react native axios

I am trying to do my first app in react native and django rest framework.
I created a simple server based on tutorial with default login page.
I am trying to make a POST to this login page, but I am getting error:
Forbidden (CSRF cookie not set.): /api-auth/login/
My POST looks like:
const sendLoginCredencials = (data) => {
axios.defaults.headers.common['X-CSRF-TOKEN'] = data.csrf_token;
axios.post('http://127.0.0.1:8000/api-auth/login/',
{
username: 'username',
password: 'passwd',
},
)
.then((response) => {
console.log(response)
})
.catch(error => {
console.error('error', error);
});
}
Settings.py has this lines:
from corsheaders.defaults import default_headers
CORS_ALLOW_HEADERS = list(default_headers) + [
'X-CSRF-TOKEN',
]
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_NAME = "csrftoken"
CSRF_HEADER_NAME = 'X-CSRF-TOKEN'
I have tested many answers and combinations. None of them worked and I admit that I feel confused. Could anyone tell me how to set the header correctly and what excatly I should set in settings.py ?
Best regards!

Django doesn't create session cookie in cross-site json request

I want to make cross-site JavaScript call from third-party domain (in this case my desktop/localhost server) to my remote Django server hosted on my_domain.com/ and calling REST WS exposed on my_domain.com/msg/my_service with using session/cookies for storing session state.
But when I call this service (hosted on remote Django server) from my desktop browser or localhost Django server (JS is in index.html), Django doesn't create session cookie and on remote server are doesn't store session state. But when i call this service from Postman or from same localhost JS to localhost instance of same Django service it works and session is created.
My JS script in index.html which make call to WS send_message:
fetch('http://my_domain.com/ws/my_service', {
method:"POST",
credentials: 'include',
body:JSON.stringify(data)
})
.then(res => res.json())
.then(json => {
showResponse(json.message);
})
When I run this script from my desktop browser or my localhost server it runs correctly with cookies and sessions parameters.
Django my_service implementation view
#csrf_exempt
def my_service(request):
if request.method == "POST":
message_bstream= request.body
request.session.set_expiry(0)
message= json.loads(message_bstream)
out,sta=state_machine_response(message["message"],int(request.session["state"]))
request.session["state"] =sta
respo={"message":out}
response = HttpResponse(json.dumps(respo), content_type="application/json")
response.set_cookie(key='name', value='my_value', samesite='None', secure=True)
#return JsonResponse(respo, safe=False, status=200)
return response
else:
return HttpResponseNotFound ("Sorry this methode is not allowed")
Or I try generate response like
return JsonResponse(respo, safe=False, status=200)
My settings.py
INSTALLED_APPS = [
...
'corsheaders',
]
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:8000',
)
CORS_ALLOWED_ORIGINS = [
'http://localhost:8000',
]
CSRF_TRUSTED_ORIGINS = [
'http://localhost:8000',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
Please do you have any idea?
You can't save cookies from a third-party API call unless you use SameSite=None with the Secure option in the Set-Cookie header. You can achieve this for the sessionid and CSRF cookie with the following settings:
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
In this case, you must use the HTTPS protocol scheme.
Another solution would be to use a proxy, which is helpful in a localhost development environment. This is an example using vue.js on the vue.config.js file:
const { defineConfig } = require('#vue/cli-service')
if (process.env.NODE_ENV == "development"){
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: {
"^/api/": {
target: process.env.VUE_APP_BASE_URL,
changeOrigin: true,
logLevel: "debug",
pathRewrite: { "^/api": "/api" }
}
}
},
})
}
Some useful doc https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#samesitenone_requires_secure

Django CSRF and axios post 403 Forbidden

I use Django with graphene for back-end and Nuxt for front-end. The problem appears when I try post requests from nuxt to django. In postman everything works great, in nuxt I receive a 403 error.
Django
# url.py
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', GraphQLView.as_view(graphiql=settings.DEBUG,
schema=schema)),
]
# settings.py
CORS_ORIGIN_WHITELIST = 'http://localhost:3000'
CORS_ALLOW_CREDENTIALS = True
CSRF_USE_SESIONS = False
CSRF_COOKIE_HTTPONLY = False
CSRF_COOKIE_SAMESITE = None
NuxtJs
# nuxt.config.js
axios: {
baseURL: 'http://127.0.0.1:8000/',
debug: false,
progress: true,
credentials: true
},
# plugins/axios.js
await $axios.onRequest((config) => {
config.headers.common['Content-Type'] = 'application/json'
config.xsrfCookieName = 'csrftoken'
config.xsrfHeaderName = 'X-CSRFToken'
const csrfCookie = app.$cookies.get('csrftoken')
config.headers.common['X-CSRFToken'] = csrfCookie
console.log(config)
# store/contact.js
import { AddMessage } from '../queries/contact.js'
export const actions = {
async send() {
const message = await this.$axios({
url: 'api/',
method: 'POST',
data: AddMessage
})
}
}
# queries/contact.js
export const AddMessage = {
query: `
mutation AddContact($input: AddMessageInput!){
addMessage(input: $input){
message{
name
email
body
terms
}
}
}
`,
variables: `
{
"input":{
"name": "test",
"email": "test#test.com",
"body": "test",
"terms": true,
}
}
`,
operationName: 'AddMessage'
}
Somethig that
Here are request headers from axios post. Something strange for me is the cookie with a wrong value. The good value of token is present in X-CSRFToken header.
Here is the log from axios post request. Another strange thing for me is the undefined headers: Content-Type and X-CSRFToken
Thank you!
I resolved this problem and I want to share the solution here.
The problem with wrong cookie value was generated by the front end app that managed (I don't remember how) to get csrf cookie from the back end app. In X-CSRFToken header was token received from response's Set-cookie header and in Cookie header was the cookie from back end app.
After I changed localhost with 127.0.0.1 and added
config.xsrfCookieName = 'csrftoken' in axios plugin
I was able to separate the apps, save and use cookies independent.
The second problem, with undefined headers was generated by axios. These 2 line of code resolved the problem. These were added also in axios onRequest method.
config.xsrfHeaderName = 'X-CSRFToken'
config.headers['Content-Type'] = 'application/json'

How to fix mixed content error in Swagger?

I am running Django RF backend application on Gunicorn.
When trying to fetch data from Swagger I get "TypeError: Failed to fetch"
In console this error is reported:
Mixed Content: The page at 'https://****.com/swagger/' was loaded over HTTPS, but requested an insecure resource 'http://****.com/v2/products/'. This request has been blocked; the content must be served over HTTPS.
I tried everything I found and could think of including:
Adding
secure_scheme_headers = {
'X-FORWARDED-PROTOCOL': 'ssl',
'X-FORWARDED-PROTO': 'https',
'X-FORWARDED-SSL': 'on'}
to Gunicorn
and
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
to Django settings.
But nothing helps.
Swagger for Django: drf-yasg==1.12.1
I found the solution. In Django settings add
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# Security Headers
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_SECONDS = 3600

Can't access csrf cookie sent by server via Set-Cookie (Django + Angular2)

I'm experimenting with an app running on Angular 2 in the frontend and Django as API and I can't access the CSRF cookie sent from Django.
As you can see, I am getting back the cookie from the server but I cannot access it from the browser (document.cookie) or Angular 2 (using the ng2-cookies module).
Everything was working while testing on localhost, now client and server are running on different hosts and this might be the reason of the problem.
Client running on https://django-angular2-movies.firebaseapp.com/
Server running on https://glacial-shore-18891.herokuapp.com
This is part of the settings I'm using on Django:
settings.py
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CSRF_COOKIE_SECURE = False
CSRF_TRUSTED_ORIGINS = ['django-angular2-movies.firebaseapp.com']
ALLOWED_HOSTS = ['*']
Other things that I have tried, but didn't help:
Removing the CSRF_TRUSTED_ORIGINS setting
Adding CSRF_COOKIE_DOMAIN = 'django-angular2-movies.firebaseapp.com'
Using { withCredentials: true } in the request sent from the client
Using the #ensure_csrf_cookie decorator on Django views
What am I doing wrong?
Thanks