preflight OPTIONS check gives 403 with Access-Control-Allow-Origin ”*” - ajax

I have a single page app served on one subdomain and an api on another. I get a 403 Forbidden on the pre-flight check for an XHR POST to the api.
Debugging it in curl shows:
curl -H "Host: backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com" \
-H "Origin: http://frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type" \
-X OPTIONS --verbose http://backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com/api/users
* Trying 54.153.79.158...
* TCP_NODELAY set
* Connected to backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com (54.153.79.158) port 80 (#0)
> OPTIONS /api/users HTTP/1.1
> Host: backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: http://frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: content-type
>
* HTTP 1.0, assume close after body
< HTTP/1.0 403 Forbidden
< Date: Fri, 04 May 2018 21:16:52 GMT
< Server: Apache/2.4.27 (Red Hat) OpenSSL/1.0.1e-fips
< Cache-Control: no-cache, private
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Headers: accept, access-control-allow-origin, content-type
< Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS
< Content-Length: 18
< Content-Type: text/html; charset=UTF-8
< Set-Cookie: 45fe47f0bb613513b8e98d0599b628f9=dd4b5c95b51d6168419458748b392421; path=/; HttpOnly
* HTTP/1.0 connection set to keep alive!
< Connection: keep-alive
<
* Connection #0 to host backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com left intact
Origin not allowed
Yet the Access-Control-Allow-Origin: * in the response suggests that it should allow the Origin: http://frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com named the request.
If I drop the Origin header from the request I get a 200.
I tried explicitly setting the Access-Control-Allow-Origin on the server to match match the Origin of the request but that also gets back a 403:
curl -H "Host: backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com" \
-H "Origin: http://frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type" \
-X OPTIONS --verbose http://backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com/api/users
* Trying 54.153.79.158...
* TCP_NODELAY set
* Connected to backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com (54.153.79.158) port 80 (#0)
> OPTIONS /api/users HTTP/1.1
> Host: backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com
> User-Agent: curl/7.54.0
> Accept: */*
> Origin: http://frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: content-type
>
* HTTP 1.0, assume close after body
< HTTP/1.0 403 Forbidden
< Date: Fri, 04 May 2018 21:32:52 GMT
< Server: Apache/2.4.27 (Red Hat) OpenSSL/1.0.1e-fips
< Cache-Control: no-cache, private
< Access-Control-Allow-Origin: http://frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com
< Access-Control-Allow-Headers: accept, access-control-allow-origin, content-type
< Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS
< Content-Length: 18
< Content-Type: text/html; charset=UTF-8
< Set-Cookie: 45fe47f0bb613513b8e98d0599b628f9=a913ecc9ded73c3ac76fbb38d93e15a7; path=/; HttpOnly
* HTTP/1.0 connection set to keep alive!
< Connection: keep-alive
<
* Connection #0 to host backend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com left intact
Origin not allowed
On the server the .htaccess has the following configuration:
<IfModule mod_headers.c>
Header add Access-Control-Allow-Origin "http://frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com"
Header add Access-Control-Allow-Headers "accept, access-control-allow-origin, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
</IfModule>
Setting the Access-Control-Allow-Origin to "*" doesn't fix the issue.
Why is the response 403 forbidden when the origin seems to be allowed and how might I fix it?
Thanks!
UPDATE
Here is my latest .htaccess which is for the laravel PHP backend:
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
<IfModule mod_headers.c>
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "*"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
</IfModule>

As #roryhewitt suggested in a comment to the question this wasn't an apache .htaccess issue. The problem was the backend API laravel app was giving 403 to any CORS request.
I deleted the mod_headers.c configuration and configured the barryvdh/laravel-cors module used by the backend app using an environment variable CORS_ALLOWED_ORIGINS=frontend-sjm-staging.a3c1.starter-us-west-1.openshiftapps.com. This sets the allowedOrigins as shown here.

Related

nginx proxy_cache key hash change on each another browser request

I was create config of nginx like:
proxy_cache_path /tmp/nginx/static levels=1:2 keys_zone=static_zone:10m inactive=10d use_temp_path$
proxy_cache_key "$request_uri$args";
location ~* \.(css|gif|ico|jpe?g|js(on)?|png|svg|webp|ttf|woff|woff2|txt|map)$ {
proxy_hide_header Date;
proxy_cache_revalidate on;
proxy_pass http://static:8080;
proxy_cache_bypass $cookie_nocache $arg_nocache;
proxy_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
proxy_hide_header "Set-Cookie";
proxy_buffering on;
proxy_cache static_zone;
proxy_cache_valid 200 301 302 30m;
proxy_cache_valid 404 10m;
#expires max;
add_header X-Proxy-Cache $upstream_cache_status;
access_log off;
add_header Cache-Control "public";
add_header Pragma "public";
expires 30d;
log_not_found off;
tcp_nodelay off;
}
On first request from Chrome all work as excepted x-proxy-cache:MISS other request got from disk cache with header x-proxy-cache:HIT. After refresh it's also HIT. But when I open page from other browsers(Opera,Edge) on this machine this request is MISS. In file system nginx create two files with different md5sum hash on a same content. For example filename 438476ac40665c852d3acde1acf769f1 head:
^C^#^#^#^#^#^#^#/^V
W^#^#^#^#��^CW^#^#^#^#'^O
W^#^#^#^#m�,�^#^#�^#�^A^N"5703e3a7-67e"^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#$
KEY: /js/catalog.js
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 12 Apr 2016 15:07:19 GMT
Content-Type: application/javascript
Content-Length: 1662
Last-Modified: Tue, 05 Apr 2016 16:11:19 GMT
Connection: close
Vary: Accept-Encoding
ETag: "5703e3a7-67e"
Accept-Ranges: bytes
The second filename a6f57423c2220fba3ada5f516f6dd91c with a same content and this head:
^C^#^#^#^#^#^#^# ^V
W^#^#^#^#��^CW^#^#^#^#^A^O
W^#^#^#^#m�,�^#^#�^#�^A^N"5703e3a7-67e"^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#^#$
KEY: /js/catalog.js
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 12 Apr 2016 15:06:41 GMT
Content-Type: application/javascript
Content-Length: 1662
Last-Modified: Tue, 05 Apr 2016 16:11:19 GMT
Connection: close
Vary: Accept-Encoding
ETag: "5703e3a7-67e"
Accept-Ranges: bytes
By documentation the name of file must be md5 from key, and there is echo -n '/js/catalog.js'| md5sum is a6f57423c2220fba3ada5f516f6dd91c as name of one of files (it was first request). I don't want to cache in server js|css per each user|browser. Just cache it once and receive from cache to all users requests. P.S. My site use https, http2, version of nginx 1.9.14.
Based on the Vary: Accept-Encoding header that's there, I would guess that Edge and Opera send different "Accept-Encoding" headers for the request. For example, one may simply send "gzip" while the other sends "gzip, deflate". Those are technically different Accept-Encoding request headers.
If you know that the origin won't send meaningfully different encodings that won't work between browsers you can add:
proxy_ignore_headers Vary;
You already have the proxy_ignore_headers, so you can probably just add to that.
Since all major browsers support gzip, the risk is likely very low. However, "webp" is also done via the Accept-Encoding, so that could create surprising results for some images if the origin can handle webp.
TLDR: Request Header Accept-Encoding matters.
Consider its normal value : Accept-Encoding: gzip, deflate, br.
When you change it to Accept-Encoding: gzip, deflate, lolkek then Nginx will store cached response in different file. And those 2 files ( under /var/cache/nginx/) will be of the same content but with different names.
The same issue: https://trac.nginx.org/nginx/ticket/1840

Firefox web push "Invalid URL endpoint"

I try to send webpush to firefox
curl -i -X PUT https://updates.push.services.mozilla.com/push/gAAAAABW5EzHyop8VZSH2jm9LJ7W8ybH3ISlbZHDGnd4RwW7h2Jb0IGTuSsP2BCoBxl0kJp-kXXL164xNzhxkTEztP1-IqVf9040VOEuy_htb1nnp-24W-RGgWgjtGK1kZYAb1k3xmAS
HTTP/1.1 400 Bad Request
Access-Control-Allow-Headers: content-encoding,encryption,crypto-key,ttl,encryption-key,content-type,authorization
Access-Control-Allow-Methods: POST,PUT
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: location,www-authenticate
Content-Type: application/json
Date: Tue, 15 Mar 2016 13:04:44 GMT
Server: cyclone/1.1
Content-Length: 51
Connection: keep-alive
{"errno": 102, "code": 400, "error": "Bad Request"}
Does it mean that I have invalid registration id stored in my database and I should remove it?
The endpoint URL doesn't seem valid, it's usually something like https://updates.push.services.mozilla.com/push/v1/SOME_LONG_ID (note the v1 that your URL doesn't contain).
Indeed, this works:
curl -i -X PUT https://updates.push.services.mozilla.com/push/v1/gAAAAABW5EzHyop8VZSH2jm9LJ7W8ybH3ISlbZHDGnd4RwW7h2Jb0IGTuSsP2BCoBxl0kJp-kXXL164xNzhxkTEztP1-IqVf9040VOEuy_htb1nnp-24W-RGgWgjtGK1kZYAb1k3xmAS
Note that you might want to add the TTL header, otherwise your request might fail (you just need -H "TTL: 60"): https://blog.mozilla.org/services/2016/02/20/webpushs-new-requirement-ttl-header/.

Using PDF Reactor as Web Service

I am discovering PDF reactor and I'd like to use it as a web service. To test a file, I use cURL
curl -v -X POST --header "Content-Type:application/xml" http://localhost:9423/service/rest/convert/async -d #test.html
Is that correct ?
test.html :
<html>
<body>
Coucou, je suis terrien.
</body>
</html>
Thank you for your help,
Cédrik
edit #1:
response from the comman above :
* About to connect() to localhost port 9423 (#0)
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 9423 (#0)
> POST /service/rest/convert/async HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.14.3.0 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: localhost:9423
> Accept: */*
> Content-Type:application/xml
> Content-Length: 50
>
< HTTP/1.1 400 Bad Request
< Content-Type: text/plain
< Date: Tue, 15 Dec 2015 11:47:29 GMT
< Content-Length: 307
< Server: Jetty(9.3.2.v20150730)
<
* Connection #0 to host localhost left intact
* Closing connection #0
JAXBException occurred : élément inattendu (URI : "", local : "html"). Les éléments attendus sont <{http://webservice.pdfreactor.realobjects.com/}configuration>. élément inattendu (URI : "", local : "html"). Les éléments attendus sont <{http://webservice.pdfreactor.realobjects.com/}configuration>.
When using the REST API of PDFreactor via cURL you have to send a configuration XML or JSON to the server which includes configuration for PDFreactor and your document, as described here: http://www.pdfreactor.com/product/doc_html/index.html#d0e688
A sample configuration for XML could look like this:
config.xml:
<tns:configuration xmlns:tns="http://webservice.pdfreactor.realobjects.com/">
<document><html> <body> Coucou, je suis terrien. </body> </html></document>
</tns:configuration>
You can then call the following:
curl -v -X POST --header "Content-Type:application/xml" http://localhost:9423/service/rest/convert/async.xml -d #config.xml
The output will look like the following:
* About to connect() to localhost port 9423
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 9423
> POST /service/rest/convert/async.xml HTTP/1.1
> User-Agent: curl/7.15.5 (x86_64-redhat-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8b zlib/1.2.3 libidn/0.6.5
> Host: localhost:9423
> Accept: */*
> Content-Type:application/xml
> Content-Length: 195
>
> <tns:configuration xmlns:tns="http://webservice.pdfreactor.realobjects.com/"> <document><html><body>Coucou, je suis terrien.</body></html></document></tns:configuration>HTTP/1.1 202 Accepted
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Accept, Content-Length, content-type, Host, User-Agent
< Access-Control-Allow-Methods: GET, PUT, POST, DELETE
< Access-Control-Expose-Headers: Location
< Cache-Control: no-cache
< Date: Wed, 16 Dec 2015 16:34:19 GMT
< Location: http://localhost:9423/service/rest/progress/c2a58dbd-ef9d-4b79-87d9-079c139fe9ed
< Content-Length: 0
< Server: Jetty(9.3.2.v20150730)
* Connection #0 to host localhost left intact
* Closing connection #0
The "Location" response header contains the URL which can be used to retrieve the progress of the conversion, so you can retrieve the progress with (the ID will of course vary):
curl -v http://localhost:9423/service/rest/progress/c2a58dbd-ef9d-4b79-87d9-079c139fe9ed
This will return the conversion progress and if the conversion has finished the "Location" repsonse header will contain a new URL to retrieve the document. You can use ".pdf" to retrieve the PDF binary data or ".xml" to retrieve XML data containing the PDF as base64 encoded String, the number of pages of the document, etc.
curl -v http://localhost:9423/service/rest/document/c2a58dbd-ef9d-4b79-87d9-079c139fe9ed.pdf

Django-rest-framework token auth doesn't work

I'm trying to POST json data to url, decorated with login_required, but django returns redirect to login page
DRF setup:
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
and rest_framework.authtoken in INSTALLED_APPS
I can obtain auth token via curl
$ curl -X POST -d "{\"username\" : 7, \"password\" : 1}" -H "Content-Type: application/json" http://127.0.0.1:9000/extapi/get-auth-token/
{"token":"bc61497d98bed02bd3a84af2235365d0b2b549ff"}
But when i POST to the view, decorated with login_required, django returns http 302 with Location header pointing to the login page.
$ curl -v -X POST -d '{"event":"14","user":"7","action":"1868","unit":"","value":"-1"}' -H "Content-Type: application/json" -H "Authorization: Token bc61497d98bed02bd3a84af2235365d0b2b549ff" http://127.0.0.1:9000/zk2015/events/actions/api/uservotejournal/7/
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 9000 (#0)
> POST /zk2015/events/actions/api/uservotejournal/7/ HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 127.0.0.1:9000
> Accept: */*
> Content-Type: application/json
> Authorization: Token bc61497d98bed02bd3a84af2235365d0b2b549ff
> Content-Length: 64
>
* upload completely sent off: 64 out of 64 bytes
< HTTP/1.1 302 FOUND
* Server nginx/1.4.6 (Ubuntu) is not blacklisted
< Server: nginx/1.4.6 (Ubuntu)
< Date: Fri, 18 Sep 2015 11:14:31 GMT
< Content-Type: text/html; charset=utf-8
< Location: http://127.0.0.1:9000/accounts/login/?next=/zk2015/events/actions/api/uservotejournal/7/
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Cookie
< X-Frame-Options: SAMEORIGIN
< ETag: "d41d8cd98f00b204e9800998ecf8427e"
< Set-Cookie: csrftoken=G85fWrKKsIA5a2uGPIn9fS4pqKrS51jK; expires=Fri, 16-Sep-2016 11:14:31 GMT; Max-Age=31449600; Path=/
<
* Connection #0 to host 127.0.0.1 left intact
I've tried to set breakpoints in rest_framework.authentication.SessionAuthentication and rest_framework.authentication.TokenAuthentication, but they were never fired
What is wrong in my setup? Help, please.
You are not passing the Authorization in Header in the curl
curl -X POST -d "{\"username\" : 7, \"password\" : 1}" -H "Authorization: Token bc61497d98bed02bd3a84af2235365d0b2b549ff" http://127.0.0.1:9000/extapi/get-auth-token/
The point is that request.user is AnonymousUser in drf.APIView.dispatch(), but is defined as authorized user in drf.APIView.post() and other similar methods.
This differs from django: request.user is defined as authorized user in django.views.View.dispatch()
Also that is the cause, why django.contrib.auth.decorators.login_required is not compatible whith drf views.

Nginx mod_rewrite $request_uri manipulation

I would like to do some redirects but involving the $args.
I am trying to to the following:
rewrite /aaa?a=1&aa=2 /bbb?b=1&bb=2 permanent;
But it does not work. The line below works fine, though
rewrite /aaa /bbb permanent;
I added those lines to my config file:
proxy_set_header x-request_uri "$request_uri";
proxy_set_header x-args "$args";
And I can see those headers:
GET /aaa?a=1&aa=2 HTTP/1.0
Host: www.example.com
x-request_uri: /aaa?a=1&aa=2
x-args: a=1&aa=2
Connection: close
User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.15.3 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
Accept: */*
What I am doing wrong? is there a way to accomplish redirect taking full $request_uri in consideration?
I've got the answer on irc.freenode.net #nginx:
Mod_rewrite does not match against url-with-args only without, use if or map instead.
I managed to get it working with if:
if ( $request_uri = '/aaa?a=1&aa=2' ){
return 301 $scheme://$host/bbb?b=1&bb=2;
}
Response header:
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.0.15
< Date: Wed, 02 Jul 2014 20:05:34 GMT
< Content-Type: text/html
< Content-Length: 185
< Connection: keep-alive
< Location: http://www.example.com/bbb?b=1&bb=2
< x-uri: /aaa?a=1&aa=2

Resources