Local avatars served via HTTP when browsing HTTPS?

(Lee_Ars) #1

When viewing my forum via HTTPS I get a mixed content warning, showing that some resources are served unencrypted. Chrome’s dev console shows that the culprits appear to be the locally-hosted avatars; it appears that something in the application js file is forcing them to HTTP instead of HTTPS. Might it be possible to replace the hard-coded HTTP with a protocol-agnostic string instead?


(Edited to add—this is with a current git pull as of about an hour ago)

(Michael - DiscourseHosting.com) #2

When checking if we had the same problem, we came across a similar issue, and as I thought they might share a root cause I dived into this.

The avatars on our test setup were served from a CDN. So I disabled the CDN in order to test your issue.

After disabling the CDN, the avatar images were still served from the CDN URL. We had to re-upload the avatar to get rid of this. This was definitely not a caching issue - we tested from different browsers and made sure we restarted Discourse and flushed redis.

After re-uploading the avatar it was served from http and we got a mixed content issue as well.

Turns out:

  1. when storing an avatar, the base_url_no_prefix function is executed in order to determine the URL

    def self.base_url_no_prefix
    default_port = 80
    protocol = “http”

     if SiteSetting.use_ssl?
       protocol = "https"
       default_port = 443

So there’s your solution, did you enable the use_ssl parameter? (It’s new for me that this was required as well). But there is some bad news:

  1. The avatar URL is stored in the database upon upload. That means that if you switch SSL or CDN, existing avatars will NOT use the new URL until they’re re-uploaded. The same goes for images in posts, but I think the ‘rebake’ rake task takes care of that.

(Lee_Ars) #3

Thanks for taking a peek. No, I don’t have use_ssl toggled on; I’d prefer to allow user the flexibility of using either http or https (and I also firmly believe in the OSI model–my web application shouldn’t care or really even be aware of what transport the web server beneath it is using).

(Sam Saffron) #4

@zogstrip can you have a look at this?

(Michael - DiscourseHosting.com) #5

I completely agree with you. If Discourse generates local URL’s, they should be protocol agnostic.

There are a few more places I suspect, in pretty_text.rb is a line

context.eval("Discourse.BaseUrl = 'http://#{RailsMultisite::ConnectionManagement.current_hostname}';")

Which has the additional quirk that it ignores SiteSetting.force_hostname and takes the first configured hostname from the database config - which causes force_hostname to not work well and at least makes the software behave a bit schizophrenic :smile:

(Jeff Atwood) #6

True, but they cannot be protocol relative URLs in emails we send, since many major email clients (including current iOS and Android default mail clients, as well as MSFT Outlook latest version) fail when presented with //example.com/img.jpg links. Not kidding! We found out the hard way…

User uploaded_avatar_template is absolute
(Michael - DiscourseHosting.com) #7

That’s why I said local :smile: trying to say: URL’s where you’re trying to point to the same server as you’re visiting. That said, I’m pondering why are those URLs absolute at all, why not make them completely relative ?

Back to emails. Currently, it takes the first configured hostname from the database config in order to generate the URL in an email, while in other places it uses force_hostname. So now there are two settings both not coincidentally marked as experimental / dangerous / no touchy because they both do not work completely well: force_hostname and use_ssl.

So my $0.02:

  • use protocol agnostic or maybe even relative URL’s where possible
  • combine force_hostname and use_ssl into one setting called discourse_url or something like that and use that whenever you cannot use local URLs (for instance in emails)

(Jeff Atwood) #8

I can’t think of any reason the local avatar URLs should be absolute. @zogstrip?

I know we need absolute versions for, say, email notifications and such.

(Régis Hanol) #9

There is no reason for the local avatar URLs to be absolute. Will change that and make sure we have two versions of the URLs.

(5an1ty) #10

Sorry to bump this thread, but is this a planned change or should it be working already?

(Régis Hanol) #12

Yep, still planned :wink:

(Claus Strasburger) #13

A hint for anyone who’s annoyed by this: We configured our nginx to re-route any http requests to https — to make sure nobody broadcasts their password over open WIFIs and such.

We have discourse running on another box and the main box handling all the ssl stuff, which has the advantage of being completely app-agnostic

example nginx:

server {
    listen 80;
    server_name forum.foobar.com;

    rewrite ^(.*) https://forum.foobar.com$1 permanent;

server {
    listen 443;
    server_name forum.foobar.com;
    ssl on;
    ssl_certificate /etc/ssl/private/forum.foobar.com.crt;
    ssl_certificate_key /etc/ssl/private/forum.foobar.com.key;
    keepalive_timeout 75;
    location / {
        set $dest_fix $http_destination;
        if ( $http_destination ~* ^https(.*)$ )
            set $dest_fix http$1;
        proxy_set_header Host $host;
        proxy_set_header Destination $dest_fix;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://ip.to.discourse.machine:3000 ;

(5an1ty) #14

So does that fool your browser into thinking all is OK? Or will Chrome still see HTTP resources even though they get redirected to the HTTPS ones?

(Claus Strasburger) #15

The config I set in nginx will redirect every HTTP request to a HTTPS URL (keeping the rest of the URL the same).
That way, the browser knows it is being redirected but doesn’t care, because in the end it gets HTTPS content.

Note that some browsers display a warning with this approach, too, because the HTTP redirect could be manipulated (I think Firefox does).

nginx is doing this by responding with a HTTP 301 Moved Permanentlywhenever somebody requests a http url:

cfstras@mybox ~ $ curl -i http://forum.foobar.com
HTTP/1.1 301 Moved Permanently
Server: nginx/1.2.1
Date: Tue, 08 Oct 2013 21:37:37 GMT
Content-Type: text/html
Content-Length: 184
Connection: keep-alive
Location: https://forum.foobar.com/  # --- this is where the redirect happens! ---

<head><title>301 Moved Permanently</title></head>
<body bgcolor="white">
<center><h1>301 Moved Permanently</h1></center>

cfstras@mybox ~ $

(5an1ty) #16

FYI after doing some research, a better way of forcing the redirect instead of using rewrite is:

return 301 https://mydomain.com$request_uri;

Also Chrome and Firefox still complain about insecure content.

(Claus Strasburger) #17

Yes, unfortunately. They come from embedded HTTP content.

(Michael - DiscourseHosting.com) #18

Yes. So people, this is not the way to go.
We’ll have to wait for someone to come up with a fix in the code.

(Jacob) #19

Has this been addressed yet? Hoping to use https.

(eriko) #20

The links to the advatar in the page seems to be using relative links now. This is with a checkout from this morning.

<img width="45" height="45" src="/uploads/default/1/656dfea01fcfa7be.jpg" class="avatar" title="erik_admin">

(5an1ty) #21

For me they still aren’t protocol agnostic with the the latest git pull. Do I still need to execute a command to refresh my avatar URL’s?