Is there a way to set a Content-Security-Policy header to say don't run javascript from any URL that has a hostname - http-headers

Our web application serves pages and pages of javascript files among other things. We are going through a security scanner and it recommended a Content-Security-Policy header, which isn't a bad thing. But there's an ugly wrinkle. We serve all of our javascript via '/js/...' links and are behind a reverse-proxy server and can be loaded via multiple paths. The consequence of all that is we don't actually know our own URL on the server side.
So I could place the header only if I can say no host names allowed.
If I really needed a hostname I could probably do something like this, but I understand that it's verboten.
<HEAD>
<SCRIPT>
document.write('<META HTTP-EQUIV="Content-Security-Policy" ...>');
</SCRIPT>
<SCRIPT SRC="/js/public/require.js"></SCRIPT>
...
There's a few places where we fetch data (not javascript) from outside paths; I suppose I'll just have to manually add the URLs to the list on those pages.

The consequence of all that is we don't actually know our own URL on the server side.
Opt 1. URL means:
[scheme://] [host] [:port] [/path]
even if you use relative URLs like <SCRIPT SRC="/js/public/require.js"></SCRIPT>, browsers follow Same Origin Policy and use [scheme://] [host] [:port] form the parent window.
Hence you anyway have a host (domain name). Even if you use IP address as host, this IP become a host name (but you loose ability of https:).
Real host always presents, and to know path on the server is not mandatory for CSP.
Therefore you can use script-src 'self' to denote own host name through which web app operates. Alternatively you can use script-src your_domain.com or even script-src your_domain.com/js/ if you wish to allow scripts from /js/ folder only.
Opt 2. Use the 'nonce-value':
Publish the CSP with script-src 'nonce-base64value'; and use base64value as nonce= attribute value in scripts:
<SCRIPT SRC="/js/public/require.js" nonce="base64value"></SCRIPT>
<SCRIPT nonce="base64value">alert('Hello');</SCRIPT>

Related

how to use X-Forwarded-Prefix in file backend

I'm try to configure traefik with file backend to contact a grafana server in a LXC container.
This is my configuration file:
[file]
# rules
[backends]
[backends.backend2.servers.server1]
url = "http://192.168.255.250:3000"
[frontends]
[frontends.frontend2]
entryPoints = ["http"]
backend = "backend2"
passHostHeader = true
[frontends.frontend2.routes]
[frontends.frontend2.routes.route0]
rule = "PathPrefixStrip: /grafana"
Grafana backend listen on /
So, I can contact http://example.com/grafana but I have a redirection to http://example.com/login which does not work. But http://example.com/grafana/login responding (without css, certainly because grafana seems to use relative url).
According to the documentation :
Use a *Strip matcher if your backend listens on the root path (/) but should be routeable on a specific prefix. For instance, PathPrefixStrip: /products would match /products but also /products/shoes and /products/shirts.
Since the path is stripped prior to forwarding, your backend is expected to listen on /.
If your backend is serving assets (e.g., images or Javascript files), chances are it must return properly constructed relative URLs.
Continuing on the example, the backend should return /products/shoes/image.png (and not /images.png which Traefik would likely not be able to associate with the same backend).
The X-Forwarded-Prefix header (available since Traefik 1.3) can be queried to build such URLs dynamically.
It seems that I have to use the X-Forwarded-Prefix header but I do not know how to use it (I did not see anything in the documentation). Maybe you can help me solve this problem ?
Regards
jmc
In fact, the problem does not come from traefik. I just forgot to specify the path in /etc/grafana.ini (root_url field). I thought it was not necessary since the incoming query does not contain the path /grafana (because we use PathPrefixStrip). But in fact, grafana needs it to indicate effective url to client.
Regards.
jmc

Service worker uses incorrect (cached) Content Security Policy

I've discovered an interesting issue when attempting to use Content Security Policy to secure my site and also ServiceWorker to speed it up and let it run offline.
It's a standard Wordpress site and Plugin developers have a naughty habit of using external resources, particularly in the /wp-admin/ section. I don't what to whitelist a ton of stuff on the main site (particularly unsafe-eval, a frequent culprit in the admin section), so what I did was make a main CSP, then in /wp-admin/ I unset and reset a less restrictive set.
Here's a sample of the code I'm using to unset the CSP when you're in the admin area of the site:
<Location /wp-admin/>
<IfModule mod_headers.c>
Header always unset Content-Security-Policy
Header unset Content-Security-Policy
Header set Content-Security-Policy " default-src 'self' ps.w.org;"
</IfModule>
</Location>
And it works fine unless you've been to (or have another tab open to) the main area of the site, at which point the ps.w.org directive (and others) are ignored. A bunch of assets end up blocked, scripts don't work, etc. Refreshing the page while in the admin section temporarily loads the correct CSP, so I know it's being used; it's just being overwritten by the main one. Sometimes the same happens to the main site, loading the admin CSP too.
Is the ServiceWorker caching the CSP or what exactly is going on here? Is there some way to get the ServiceWorker to respect the CSP that page should be sending in it's headers? For now I've just merged the two CSP settings into one and removed my over-broad rules for the admin area but it's not ideal.

SSL for relative paths?

I noticed when accessing a site using HTTPS I get errors in the JS console when trying to include CSS or JS files from a relative path such as this:
<link rel="stylesheet" type="text/css" href="css/demo.css?id=14" />
Mixed Content: The page at 'https://mysiste.com/' was loaded over HTTPS, but requested an insecure stylesheet 'http://mysiste.com/css/demo.css?id=14'. This request has been blocked; the content must be served over HTTPS.
What's the ideal solution for this scenarios?
Should I just force all the HTTP accesses to be redirected to HTTPS?
Or is there a way to tell the server to serve all relative paths using HTTPS?
Relative links will use the protocol and host from the base URL. The base URL is usually the one which can be seen in the URL bar. But it is possible to change the base URL explicitly using the base tag.
This means if you have a base URL with http as protocol set in your page it will still use http instead of https even though you've accessed the site with https and the reference is relative, i.e. like this:
<base href="http://example.com/">
To fix it either remove the base tag or change it to use https instead of http:
<base href="https://example.com">

How to rewrite Location response header in a proxy setup with Apache?

I have a primary proxy which sends requests to a secondary proxy on which OpeenSSO is installed.
If the OpenSSO agent determines that the user is not logged in, it raises a 302 redirect to the authentication server and provides the original (encoded) URL that the user requested as a GET parameter in the redirect location header.
However, the URL in the GET variable is that of the internal (secondary) proxy server, not the original proxy server. Therefore, I would like to edit/rewrite the "Location" response header to give the correct URL.
E.g.
http://a.com/hello/ (Original requested URL)
http://a.com/hello2/ (Secondary proxy with OpenSSO agent)
http://auth.a.com/login/?orig_request=http%3A%2F%2Fa.com%2Fhello2%2F (302 redirect to auth server with requested URL of second proxy server encoded in GET variable)
http://auth.a.com/login/?orig_request=http%3A%2F%2Fa.com%2Fhello%2F (Encoded URL is rewritten to that of the original request)
I have tried pretty much all combinations of headers and rewrites without luck so I'm thinking it may not be possible. The closest I got was this, but the mod_headers edit function does not parse environment variables.
# On the primary proxy.
RewriteEngine On
RewriteRule ^/(.*)$ - [E=orig_request:$1,P]
Header edit Location ^(http://auth\.a\.com/login/\?orig_request=).*$ "$1http%3A%2F%2Fa.com%2F%{orig_request}e"
ProxyPassReverse
ProxyPassReverse should do this for you:
This directive lets Apache adjust the URL in the Location, Content-Location and URI headers on HTTP redirect responses.
I'm not sure why your reverse proxy isn't behaving this way already, assuming you're using a pair of ProxyPass and ProxyPassReverse directives to define it.
Editing the Location Header
If you want to be able to edit the Location header as you describe, you can do it as of Apache 2.4.7:
For edit there is both a value argument which is a regular expression, and an additional replacement string. As of version 2.4.7 the replacement string may also contain format specifiers.
The "format specifiers" mentioned in the docs include being able to use environment variables, e.g. %{VAR}e.
You might also want to consider modifying your application such that the orig_request URL parameter is relativized, thus potentially eliminating the need for Header edits with environment variables.
Relative Path Location Header
You can also try using a relative path in your Location header, which would eliminate the need to explicitly map one domain to the other. This is officially valid as of RFC 7231 (June 2014), but was was widely supported even before that. You can relativize your Location header using Apache Header edit directives (even prior to version 2.4.7, since it wouldn't require environment variable substitution). That would look something like this:
Header edit Location "(^http[s]?://)([a-zA-Z0-9\.\-]+)(:\d+)?/" "/"

Level of obscurity of destination URLs via mod_rewrite

To achieve a single layer of content delivery security, I'm looking into the possibility of obscuring a resource URL via an .htaccess RewriteRule:
RewriteEngine on
RewriteBase /js/
RewriteRule obscure-alias\.js http://example.com/sensitive.js
It would of course be implemented as:
<script type="text/javascript" src="obscure-alias.js"></script>
Because this is not a 301 redirect, but rather a routing scenario similar to that of many of our frameworks we used today, would it be safe to say that this RewriteRule adequately obfuscates the actual URL where this resource is located, or:
Can the destination URL still be found out via some HTTP header sniffing utility
Might a web browser be able to reveal the "Download URL"
I'm going to pre-answer my own questions by saying no to both since the "internal proxy" is taking place on the server-side and not on the client side if I understand it correctly: http://httpd.apache.org/docs/current/mod/mod_rewrite.html. I just wanted to confirm that when Apache goes to serve the destination URL, that it also isn't passing along information to the user agent what the URL was that it rewrote the original request as.
It depends on how you specify the redirect target.
If your http://example.com/ is running on the same server, there will be an internal redirect that is invisible to the client. From the manual:
Absolute URL
If an absolute URL is specified, mod_rewrite checks to see whether the hostname matches the current host. If it does, the scheme and hostname are stripped out and the resulting path is treated as a URL-path. Otherwise, an external redirect is performed for the given URL. To force an external redirect back to the current host, see the [R] flag below.
if the absolute URL points to a remote domain, a header redirect will be performed. A header redirect is visible to the client and will reveal the sensitive location.
To make sure no external redirect takes place, specify a relative URL like
RewriteRule obscure-alias\.js sensitive.js
Note that the sensitive JS file's URL can still be guessed.
To find out whether a request results in a header redirect, log in onto a terminal (eg. on a Linux server) and do
wget --server-response http://www.example.com
If the first HTTP/.... line (there may be more than one) is something that begins with a 3xx, like
HTTP request sent, awaiting response...
HTTP/1.1 302 Moved Temporarily
you are looking at a header redirect.
Possible using proxy throughput.
See http://httpd.apache.org/docs/2.4/rewrite/proxy.html
Also alluded to here as well: mod_rewrite not working as internal proxy