We are using nginx for https traffic offloading, proxying to a locally installed jasperserver (5.2) running on port 8080.
internet ---(https/443)---> nginx ---(http/8080)---> tomcat/jasperserver
When accessing the jasperserver directly on its port everything is fine. When accessing the service through nginx some functionalities are broken (e.g. editing a user in the jasperserver UI) and the jasperserver log has entries like this:
CSRFGuard: potential cross-site request forgery (CSRF) attack thwarted (user:%user%, ip:%remote_ip%, uri:%request_uri%, error:%exception_message%)
After some debugging we found the cause for this:
In its standard configuration nginx is not forwarding request headers that contain underscores in their name. Jasperserver (and the OWASP framework) however default to using underscores for transmitting the csrf token (JASPER_CSRF_TOKEN and OWASP_CSRFTOKEN respectively).
Solution is to either:
nginx: allow underscores in headers
server {
...
underscores_in_headers on;
jasperserver: change token configuration name in jasperserver-pro/WEB-INF/esapi/Owasp.CsrfGuard.properties
Also see here:
header variables go missing in production
http://wiki.nginx.org/HttpCoreModule#underscores_in_headers
Answered it myself - hopefully this is of some use to others,too
I had this issue with Jasperserver 5.5 AWS AMI
More specific:
/var/lib/tomcat7/webapps/jasperserver-pro/WEB-INF/esapi/Owasp.CsrfGuard.properties
Change:
org.owasp.csrfguard.TokenName=JASPER_CSRF_TOKEN
org.owasp.csrfguard.SessionKey=JASPER_CSRF_SESSION_KEY
To:
org.owasp.csrfguard.TokenName=JASPERCSRFTOKEN
org.owasp.csrfguard.SessionKey=JASPERCSRFSESSIONKEY
My version of Jasperserver looked slightly different, the CSRFguard files are located in jasperserver/WEB-INF/csrf
I edited the jrs.csrfguard.properties file.
Related
I have a Windows PC with some Flask webapps running on various ports 5000, 5001, ...
I have mapped subdomains with CNAMEs to point to the server and Nginx handles the rerouting (correct wording?) to the appropriate ports: x.server.net -> ip:5000, y.server.net -> ip:5001
It works
Now I want to enable https so I would like to redirect users from http to https.
Further, I discovered that Nginx doesn't handle when users supply a non-standard port, so x.server.net:5001 actually points to the wrong Flask app. So I would also like to redirect non-default ports to the default (80 or 443 depending on http or https). Some of the apps don't need https, so I might mix it.
Can this be done with Nginx or should I use something else? I found others asking this, but the replies are only for Linux as far as I understand (iptables?).
And last but not least, is redirects a safe approach? Can it be ignored by a malicious client?
We followed gudelines in the below link in order to secure the undertow http server (same steps have been followed for fuse 7.2 and we have the same issue).
https://access.redhat.com/documentation/en-us/red_hat_fuse/7.3/html/apache_karaf_security_guide/webconsole
When we start our server then we see entry "Starting undertow https listener on 0.0.0.0:8943". But when we try to visit link https://localhost:8943/hawtio then we get in chrome below error.
This site can’t provide a secure connection localhost uses an unsupported protocol.
ERR_SSL_VERSION_OR_CIPHER_MISMATCH
Unsupported protocol
The client and server don't support a common SSL protocol version or cipher suite.
Our certificate has been tested with the the undertow server of the wildfy and it works without any problem, so we believe that we have followed correctly the guidelines.
Also our rest services can exhange https messages without any problem.
In the below links you can find 2 configuration files.
https://quickfileshare.org/Yfx/org.ops4j.pax.web.cfg
https://quickfileshare.org/Yfy/undertow.xml
Have we overlooked any configuration step?
You have:
<w:engine
enabled-cipher-suites="TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"
enabled-protocols="TLSv1 TLSv1.1 TLSv1.2 SSLv3.0" />
please verify if Chrome supports the above strong cipher suites.
Let's say I have this DNS entry: mysite.sample. I am developing, and have a copy of my website running locally in http://localhost:8080. I want this website to be reachable using the (fake) DNS: http://mysite.sample, without being forced to remember in what port this site is running. I can setup /etc/hosts and nginx to do proxing for that, but ... Is there an easier way?
Can I somehow setup a simple DNS entry using /etc/hosts and/or dnsmasq where also a non-standard port (something different than :80/:443) is specified? Without the need to provide extra configuration for nginx?
Or phrased in a simpler way: Is it possible to provide port mappings for dns entries in /etc/hosts or dnsmasq?
DNS has nothing to do with the TCP port. DNS is there to resolv names (e.g. mysite.sample) into IP addresses - kind of like a phone book.
So it's a clear "NO". However, there's another solution and I try to explain it.
When you enter http://mysite.sample:8080 in your browser URL bar, your client (e.g. browser) will first try to resolve mysite.sample (via OS calls) to an IP address. This is where DNS kicks in, as DNS is your name resolver. If that happened, the job of DNS is finished and the browser continues.
This is where the "magic" in HTTP happens. The browser is connecting to the resolved IP address and the desired port (by default 80 for http and 443 for https), is waiting for the connection to be accepted and is then sending the following headers:
GET <resource> HTTP/1.1
Host: mysite.sample:8080
Now the server reads those headers and acts accordingly. Most modern web servers have something called "virtual hosts" (i.e. Apache) or "sites" (i.e. nginx). You can configure multiple vhosts/sites - one for each domain. The web server will then provide the site matching the requested host (which is retreived by the browser from the URL bar and passed to the server via Host HTTP header). This is pure HTTP and has nothing to do with TCP.
If you can't change the port of your origin service (in your case 8080), you might want to setup a new web server in front of your service. This is also called reverse proxy. I recommend reading the NGINX Reverse Proxy docs, but you can also use Apache or any other modern web server.
For nginx, just setup a new site and redirect it to your service:
location mysite.example {
proxy_pass http://127.0.0.1:8080;
}
There is a mechanism in DNS for discovering the ports that a service uses, it is called the Service Record (SRV) which has the form
_service._proto.name. TTL class SRV priority weight port target.
However, to make use of this record you would need to have an application that referenced that record prior to making the call. As Dominique has said, this is not the way HTTP works.
I have written a previous answer that explains some of the background to this, and why HTTP isn't in the standard. (the article discusses WS, but the underlying discussion suggested adding this to the HTTP protocol directly)
Edited to add -
There was actually a draft IETF document exploring an official way to do this, but it never made it past draft stage.
This document specifies a new URI scheme called http+srv which uses a DNS SRV lookup to locate a HTTP server.
There is an specific SO answer here which points to an interesting post here
On my Ubuntu deployment server Nginx is dropping a custom request header (a token), only if the request is coming from Microsoft Edge or Internet Explorer. Requests coming from Firefox, Chrome or Safari just work fine.
I've done a tcpdump to check the difference between the incoming requests, and the requests look exactly the same (only the User-Agent is different, which seems normal). All the browsers are sending the token to nginx
Because my header contains an underscore, I have in nginx.conf the line
underscores_in_headers on;
I am logging the header in access log of nginx,and it shows up for all browsers but IE.
Nginx is proxying to a Python Flask application, using gunicorn. In the Flask application I immediately log the incoming requests and the token is disappeared if the browser is IE. So apparently nginx drops the header before sending it to gunicorn.
Any advice what can cause this.
TLDR: Do you use a WAF? Maybe a WAF as a service?
I'd suggest you investigate your full infrastructure/routing topology. There may be load balancers/things in the path that you're not taking into account.
We literally just ran into this exact same issue at my work and your post was the only thing on the internet that sounded like our problem. We ended up figuring out the root cause.
Here's a simplified version of our topology from a DNS routing perspective:
newwebsite.company.com --> Web Application Firewall as a Service (if this fails it fails open) --> Nginx+ (with WAF plugin) --> Kubernetes Nginx Ingress Controller --> Custom Angular Javascript frontend hosted on Nginx Pod
legacywebsite.company.com --> F5 load balancer --> Windows IIS Web Server.
(There was a section of the new site that used the same backend server of the legacy website, and we'd see hidden 500 errors if we used Chrome Developer Tools.
We checked IIS logs and found out headers with underscores were getting stripped from the clients HTTP request b4 they'd get to the backend IIS server/we found out that we had to add underscores_in_headers on; to every Nginx Load Balancer in the path and that fixed it! ... or so we thought. It turned out the problem was fixed for every browser except for Internet Explorer / Microsoft Edge. (Your exact scenario)
The crazy thing is if you were on the one url path of the new site that would forward your traffic to the old site's load balancers, then you were going through a crazy amount of load balancers. (The nginx pod that hosted the Angular Javascript frontend would redirect you to the F5 load balancer). We discovered the root cause by process of elimination to get rid of that crazy amount of load balancers in the routing in a way that involved minimal testing. I edited my hostfile for newwebsite.company.com to bypass the WAF as a Service and point straight to the Nginx+ LB acting as a WAF, and it started working/no more 500 errors for IE/Edge.
Our theory is that our WAF as a Service was stripping out a HTTP header that has an underscore (which Win IIS web servers use), and they were only stripping out this HTTP header for Edge/IE. So we've got a ticket with them explaining the situation and directions for reproduceability.
I have the following Web Farm:
1. http: mydomain1.com port: 80
2. http: mydomain2.com port: 80
3. https: port: 443 SSL Certificate: myCertificate
In II7 when you select https binding, the host name will be disabled.
I used the appcmd to bind the host name "admin.mydomain2.com" to the
website.
appcmd set site /site.name:"admin" /+bindings.[protocol='https',bindingInformation='*:443:admin.mydomain2.com']
A new item was added to the bindings.
3. a. https: port: 443 SSL Certificate: myCertificate
b. https: admin.mydomain2.com port:443 SSL Certificate: None
If for example I want to remove the first item (a), is it possible to assign a certificate to the second binding (b)?
Links as answers are not the best way to do it, because often those links go cold. Here's a summary of the answer posted above, along with supporting information from elsewhere.
When it comes to SSL, host headers are really left out in the cold. The purpose of SSL is to encrypt your traffic, and part of that traffic is the HTTP headers sent by the browser to the server. One of those headers would be the "Host" header which IIS uses to determine which site to load up with the request. Since the certificate needs to be loaded to establish the secure connection BEFORE the request headers are sent, IIS has to select the certificate based only upon the IP address and port number, thus rendering the host header useless. This, however, does not relieve us of having to comply with STIG v6724 as it relates to IIS Site configuration. So, instead of allowing you to enter the information even though it's useless, Microsoft attempts to out-wit you by not allowing you to enter it at all. There is, however, a way around this.
Please note that this answer assumes that your certificate has already been generated, added to the certificate store, and added to IIS. It also assumes you do not want any other bindings to your website besides the SSL one.
First, we need to gather some information. We need the hash, the application ID and the host name.
Open IIS, select your server and double click on "Server Certificates" in the bottom section.
Note the "Issued To" address. This is our host name. Save this.
Select your site
Bind your site to port 80 using the http protocol
Remove all other bindings
Bind your site to port 443 using the https protocol
Open a command prompt
netsh http show sslcert
Save the Certificate Hash and the Application ID
Remove the https binding on your site
At the command prompt:
netsh http add sslcert ipport=0.0.0.0:443 certstorename=my certhash=<put Certificate Hash here> appid={<put Application ID here>}
appcmd set site /site.name:"<put site name here>" /+bindings.[protocol='https',bindingInformation='*:443:<put host name here>']
NOTE: Appcmd.exe can be found in c:\windows\system32\insetsrv. You may need to be in that folder for this command to work.
Remove the http binding from your site
NOTE: You can leave the http binding if you want to have your site auto-redirect to https, but that is another topic.
REM Solution to bind https for "Default Web Site" on IIS 7
cmd /c
pushd %systemroot%\System32\inetsrv
set sitename=Default Web Site
appcmd set site /site.name:"%sitename%" /+bindings.[protocol='https',bindingInformation='*:443:']
REM To assign ssl certificate to "Default Web Site" on IIS7
REM Go to inetmgr assign ssl certificate manually to the "Default Web Site" in Edit Bindings
REM Go to DOS command prompt use command (netsh http show sslcert) and get the Certificate Hash (certhash) and Application ID (appid) information
REM Execute the netsh command
netsh http add sslcert ipport=0.0.0.0:443 certhash=******** appid={********}
This blog post might help you. It got me to the point of having the host header defined in IIS and the correct SSL cert assigned to it, and while it worked fine locally if we pointed 127.0.0.1 to the site's address in the hosts file, it just timed out when put in production. Hopefully it'll get you a little further:
http://www.awesomeideas.net/post/How-to-configure-SSL-on-IIS7-under-Windows-2008-Server-Core.aspx
Remove all your bindings, assign the SSL cert with the command line, then add the SSL bindings with host header via the command line.