POST requests sent by https to Tomcat via an Apache reverse proxy return status 403 in https

74 views Asked by At

Configuration: An unsecured backend (Sringboot 3) is deployed on a Tomcat 10 (java 17) server. The frontend (Angular 15) is deployed on an Apache 2.4 webserver.

The webserver also serves as a reverse proxy to circumvent the ban on making Ajax requests by the same origin security policy of Cross-Origin Resource Sharing (CORS). Without the reverse proxy, the backend is inaccessible by the frontend.

Http: This configuration works perfectly when the browser accesses the frontend via http.

Https: This configuration also works when the browser accesses the frontend in https for the execution of JavaScript code (Angular) and also when sending and processing the response returned by the webserver from 'GET' requests. On the other hand, when the frontend sends a 'POST' request to the webserver (apache), the webserver returns a 403 (forbidden). Here is what we see in the “network” tab of the browser (devtools):

Header :
Request URL:https://letstryfront.com/api/genjournallinespagedmultisort?page=0&size=10
Request Method:POST
Status Code:403 Forbidden
Remote Address:192.168.56.105:443
Referrer Policy:strict-origin-when-cross-origin

Response Header :
Connection:Keep-Alive
Content-Type:text/plain

Date:Fri, 29 Dec 2023 09:29:11 GMT
Keep-Alive:timeout=5, max=100
Server:Apache/2.4.58 (Unix) OpenSSL/3.2.0
Transfer-Encoding:chunked

Accept:application/json, text/plain, */*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-GB,en-US;q=0.9,en;q=0.8
Connection:keep-alive
Content-Length:2
Content-Type:application/json
Host:letstryfront.com
Origin:https://letstryfront.com
Referer:https://letstryfront.com/journal
Sec-Ch-Ua:"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"
Sec-Ch-Ua-Mobile:?0
Sec-Ch-Ua-Platform:"Windows"
Sec-Fetch-Dest:empty
Sec-Fetch-Mode:cors
Sec-Fetch-Site:same-origin
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36

Surprisingly, when the 'POST' request is sent using curl, the webserver normally performs it with a 200 (success).

curl -X POST -v "https://letstryfront.com/api/genjournallinespagedmultisort?page=5&size=10" -H "accept: */*" -H "Content-Type: application/json" -d "[ { \"sortField\": \"lineno\", \"sortDirection\": \"asc\", \"orderSeq\": 0 }]" -o journallines.json --ssl-no-revoke

The tomcat10 logs show the “Completed 200 OK” but the 403 Forbidden are nowhere to be found.

The Apache webserver logs do, however, show a 403 return from the backend running on tomcat: Status from backend: 403, referer: https://letstryfront.com/journal

Here is the configuration of virtualhosts at the Apache server level:

<VirtualHost *:80>
    ServerName letstryfront.com
    ServerAlias *.letstryfront.com

    SSLProxyEngine on
    SSLProxyCheckPeerCN Off
    SSLProxyCheckPeerName Off
    SSLProxyVerify none

    DocumentRoot "/srv/http/letstryfront"
    <Directory /srv/http/letstryfront>
           Options Indexes FollowSymLinks MultiViews
           Allow from all
           AllowOverride All
           Require all granted
    </Directory>

    <LocationMatch "/api">
      ProxyPreserveHost on
    </LocationMatch>
    ProxyPass /api http://localhost:8080/letstrysome/api
    ProxyPassReverse /api http://localhost:8080/letstrysome/api

    Proxypass /external/geocode/ https://maps.googleapis.com/maps/api/geocode/
    ProxypassReverse /external/geocode/ https://maps.googleapis.com/maps/api/geocode/

    Proxypass /external/v1/ https://api.open-meteo.com/v1/
    ProxypassReverse /external/v1/ https://api.open-meteo.com/v1/

    RewriteEngine On

    RewriteCond %{REQUEST_URI}$1 external/geocode/json
    RewriteCond %{QUERY_STRING} ^(address=.*&)key=.*$
    RewriteRule ^ %{REQUEST_URI}?%1key=REMOVED [NE,PT,END]

    LogLevel trace2
    ErrorLog "/var/log/httpd/letstryfront.com-error_log"
    CustomLog "/var/log/httpd/letstryfront.com-access_log" common

</VirtualHost>

<VirtualHost *:443>
    ServerName letstryfront.com
    ServerAlias 192.168.56.105 *.letstryfront.com

    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/letstryfront.com.crt
    SSLCertificateKeyFile /etc/ssl/private/letstryfront.com.key
    SSLCACertificatePath /etc/ssl/certs/

    SSLProxyEngine on
    SSLProxyCheckPeerCN Off
    SSLProxyCheckPeerName Off
    SSLProxyVerify none

    DocumentRoot "/srv/http/letstryfront"
    <Directory /srv/http/letstryfront>
           Options Indexes FollowSymLinks MultiViews
           Allow from all
           AllowOverride All
           Require all granted
    </Directory>

    <LocationMatch "/api">
      ProxyPreserveHost on
    </LocationMatch>
    ProxyPass /api http://localhost:8080/letstrysome/api
    ProxyPassReverse /api http://localhost:8080/letstrysome/api

    Proxypass /external/geocode/ https://maps.googleapis.com/maps/api/geocode/
    ProxypassReverse /external/geocode/ https://maps.googleapis.com/maps/api/geocode/

    Proxypass /external/v1/ https://api.open-meteo.com/v1/
    ProxypassReverse /external/v1/ https://api.open-meteo.com/v1/

    RewriteEngine On

    RewriteCond %{REQUEST_URI}$1 external/geocode/json
    RewriteCond %{QUERY_STRING} ^(address=.*&)key=.*$
    RewriteRule ^ %{REQUEST_URI}?%1key=REMOVED [NE,PT,END]

    LogLevel trace4
    ErrorLog "/var/log/httpd/letstryfront.com-tls-error_log"
    CustomLog "/var/log/httpd/letstryfront.com-tls-access_log" common

</VirtualHost>

I changed the https virtualhost configuration (443) several times. I suppose it is possible to force POST requests to pass. I'm still testing. Another solution could be to act at the backend level. If anyone has a solution, whether at the backend level (Springboot 3) of the Tomcat 10 server or the Apache 2.4 configuration, I would be interested.

1

There are 1 answers

0
Eric S. On

I looked again at the log and noticed the header sent by curl is different that the one sent by the browser. So I added the RequestHeader unset Origin directive to the https virtualhost configuration file. Now the https virtualhost configuration file looks like this:

<VirtualHost *:443>
  ServerName letstryfront.com
  ServerAlias 192.168.56.105 *.letstryfront.com

  SSLEngine on
  SSLCertificateFile /etc/ssl/certs/letstryfront.com.crt
  SSLCertificateKeyFile /etc/ssl/private/letstryfront.com.key
  SSLCACertificatePath /etc/ssl/certs/

  SSLProxyEngine on
  SSLProxyCheckPeerCN Off
  SSLProxyCheckPeerName Off
  SSLProxyVerify none

  RequestHeader unset Origin
  ...  
</VirtualHost>

After this change I restarted the httpd service and now it's fine. Although this solution works, I was wondering if there is another method to solve the problem, at the backens level.