[OJS-3.2.0] OJS generates Protocol-relative URLs in notification e-mails

Hi,

We’re preparing an upgrade from OJS-3.1.2 to OJS-3.2.0. Upgrading the database went without problems, and most of the OJS interface seems to be working smoothly. Yet, there’s one issue that at first glance seems to be related to the base_url setting. Even though the config.inc.php file lists a fully qualified URL:

base_url = "http://journal.domain.org/journalPath"

…it seems that OJS is generating Protocol-relative URLs from it (i.e. URLs starting with a double slash, omitting the HTTP part). I’m not sure if this is a feature or a quirk, but it is proving unhelpful in the context of e-mail notifications.

I’ll illustrate the problem by copying a fragment of an e-mail notification generated by OJS:

Review assignment updated.

Link: //journal.domain.org/index.php/journalPath/reviewer/submission/20

Since the protocol is missing from this URL, this is rather unhelpful when viewed in the context of an e-mail client. First off, this protocol-relative URL in above message is not parsed as a link at all by Thunderbird, so users have to copy/paste it in a browser themselves (provided they won’t overlook the fact that the e-mail actually contains any links). Also, the link to the journal in the footer (which is displayed as a clickable link) does nothing when clicked, since the e-mail client doesn’t know what to do without any protocol in the URL. This is the case for all URLs being generated in the notification templates ({$contextUrl}, {$passwordResetUrl}, {$submissionReviewUrl}, …).

When checking the journal path in the OJS interface, via “Administration” > “Hosted Journals” > “Edit”, I find the Path field pre-filled with this domain (also lacking the protocol). Yet, it is not editable.

2020-04-03%2021_33_34-Settings%20Wizard

This didn’t happen in OJS-3.1, so I’m wondering if this is expected behaviour in OJS-3.2 ? I can imagine it could be a strategy to make OJS agnostic of the context in which it is being deployed (HTTP vs HTTPS), so it will always produce working links on the website. Yet, for notification e-mails, this is a step back.

Or could it indicate some configuration issues that should be fixed on our side?

Best,

Ron

Hi @rvdb,

Do you have any configuration directives of the form base_url[xyz] in your config.inc.php?

Regards,
Alec Smecher
Public Knowledge Project Team

Hi @asmecher,

Thanks for your answer. No, config.inc.php only contains the base_url directive. Should we add a journal-specific one, too?

Also, by checking the activity log, I’ve noticed that the link in the footer of the e-mails actually doesn’t have an @href attribute at all, it’s just:

<a>Journal Title</a>

Best,

Ron

Hi @rvdb,

Have you customized your installation in any way that might be causing the protocol-relative URLs to be used? OJS should only be using protocol-relative URLs in compiled CSS, I think.

Regards,
Alec Smecher
Public Knowledge Project Team

Hi @asmecher,

Not that I’m aware of: we haven’t touched the PHP code at all. We’re deploying an OJS docker container which reverse proxies OJS behind a Nginx server: could that have any influence?

Best,

Ron

Looks somehow related to OAI baseURL does not corresponding with URL - #12 by Juan_Pablo_Giron_Rui ?!

1 Like

Hi @rvdb,

Hmm… Essentially OJS has two modes of operation for generating URLs:

  • When there are base_url[journalPathHere] directives in config.inc.php, this forces the URL, including the protocol. (Note that this is different from the plain base_url directive without [somethingAfterIt]).
  • Otherwise, OJS attempts to auto-detect details from the server environment (PHP’s $_SERVER global).

Protocol-relative URLs are generated only in a very specific circumstances – at the moment (also true for the 3.2.0 release), only when compiling CSS from LESS sources that may be served via HTTP and HTTPS.

So I see two possibilities…

  1. There’s some modification to the installation that uses protocol-relative URLs more broadly, or
  2. Something about your environment is not providing a protocol to the PHP environment at all.

You might test the latter by looking at the output of a <?php print_r($_SERVER); script.

Regards,
Alec Smecher
Public Knowledge Project Team

Hi @asmecher,

Thanks for your suggestions. Unless Docker is pulling something else, this should be a vanilla OJS installation; we haven’t modified anything, in any case.

This is the output for $_SERVER:

Array
(
    [HTTP_HOST] => journal.domain.org
    [HTTP_CONNECTION] => close
    [HTTP_X_REAL_IP] => 94.226.235.105
    [HTTP_X_FORWARDED_FOR] => 94.226.235.105
    [HTTP_X_FORWARDED_PROTO] => https
    [HTTP_X_FORWARDED_SSL] => on
    [HTTP_X_FORWARDED_PORT] => 443
    [HTTP_USER_AGENT] => Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
    [HTTP_ACCEPT] => text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    [HTTP_ACCEPT_LANGUAGE] => en-US,en;q=0.5
    [HTTP_ACCEPT_ENCODING] => gzip, deflate, br
    [HTTP_UPGRADE_INSECURE_REQUESTS] => 1
    [HTTP_CACHE_CONTROL] => max-age=0
    [HTTP_COOKIE] => OJSSID=7imjoknflkv56ec0javajurs1v
    [PATH] => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    [SERVER_SIGNATURE] => <address>Apache/2.4.38 (Debian) Server at journal.domain.org Port 80</address>

    [SERVER_SOFTWARE] => Apache/2.4.38 (Debian)
    [SERVER_NAME] => journal.domain.org
    [SERVER_ADDR] => 172.26.0.11
    [SERVER_PORT] => 80
    [REMOTE_ADDR] => 172.26.0.3
    [DOCUMENT_ROOT] => /var/www/html
    [REQUEST_SCHEME] => http
    [CONTEXT_PREFIX] => 
    [CONTEXT_DOCUMENT_ROOT] => /var/www/html
    [SERVER_ADMIN] => webmaster@localhost
    [SCRIPT_FILENAME] => /var/www/html/info.php
    [REMOTE_PORT] => 34048
    [GATEWAY_INTERFACE] => CGI/1.1
    [SERVER_PROTOCOL] => HTTP/1.1
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => 
    [REQUEST_URI] => /info.php
    [SCRIPT_NAME] => /info.php
    [PHP_SELF] => /info.php
    [REQUEST_TIME_FLOAT] => 1586981782.312
    [REQUEST_TIME] => 1586981782
)

Does that provide any clue to you?

Best,

Ron

Hi Ron,

Sorry to arrive late to this post.
Some initial questions:

  • What docker image are you using?
  • Are you completely sure the nginx proxy is working fine?

We are still on beta, but I’m testing the images published in docker-ojs and they are working fine.

If it used to work on 3.1 try with this one and confirm is still working:

Then move to this (or just change the version in your docker-compose.yml) and confirm it fails:

Both images are build upon the same template (same php version and modules), so if you can confirm the issue, we can be sure problem will be related with OJS 3.2 changes.

I checked in one of my dockerized testing journals (with 3.2.0-2) and the protocol is still there:

imagen

Anyway, if I have to guess, I should review twice the proxy configuration.
From my (limited) experience, proxies are hell. :wink:

Cheers,
m.

Did you try to set:

 absolute_redirect off;

in nginx?

Source: https://stackoverflow.com/a/58453860

Thanks @marc for chiming in!
Just tried setting absolute_redirect off; but to no avail.

Problem here is that the emails are showing this behavior, e.g. resetting a password will result in … Reset my password: //journal.tei-c.de/index.php/jTEI/login/resetPassword/pstadler?confirm=xxx
(You are invited to register yourself at journal.tei-c.de to receive an email notification)

So, I wonder how a proxy setting might affect the email text?

But it’s not only the email… your “Hosted Journals Path” is showing it wrongly (without the protocol).

Do you mind to test with this two docker-composes?

This is a vanilla 3.1.2-4:
https://raw.githubusercontent.com/pkp/docker-ojs/master/versions/3_1_2-4/alpine/apache/php73/docker-compose.yml

And this is the same with 3.2.0-2:
https://raw.githubusercontent.com/pkp/docker-ojs/master/versions/3_2_0-2/alpine/apache/php73/docker-compose.yml

Both composes use the same LAMP stack, with same Apache2 configuration, same config variables… so they are expected to work in the same way.

If they work differently, we can focus in OJS 3.2.
Otherwise, we need to dig deeper in the proxy.

Hi @marc, just to let you know we’ll be giving the official Docker images a try this week, and are hoping to report back soon.

1 Like

So, after extensive debugging last night @rvdb and I think we were finally able to solve the issue. Hurray :slight_smile:

First, I need to confess that we indeed were running a modified version were I had applied the ‘patch’ from OJS 3 behind reverse proxy - how to achieve? - #9 by hermann to set $allowProtocolRelative = true. Without it, OJS would create http URLs for Javascript and CSS files, hence the client received an ugly and not-working page due to mixed-content issues. That was the original situation when we posted that issue here.

Testing with the ‘official’ OJS Docker images showed the same result, i.e. http URLs for Javascript and CSS files behind our Nginx proxy.

@asmecher’s hint about the $_SERVER variable and a related typo3 issue (Bug #29693: Respect HTTP_X_FORWARDED_PROTO in SSL check - TYPO3 Core - TYPO3 Forge – seems they have been there as well) brought us on the track that the HTTPS key was probably missing from the $_SERVER variable. [Looking for it now I think the relevant part might be pkp-lib/PKPRequest.inc.php at c5f40d7d390d0aedb9a4f9063914652aa6e3dc6c · pkp/pkp-lib · GitHub]

Long story short: the remedy for us was to add SetEnvIf X-Forwarded-Proto "^https$" HTTPS=on to the apache config which will properly inject that HTTPS key to the $_SERVER variable IFF the proxy sends the X-Forwarded-Proto header.

1 Like

Hi @ps_tadler,

Thanks for following up!

I believe the default behaviour of OJS 3.2.0 and above is to use protocol-relative URLs in compiled CSS (in order to avoid the situation you mentioned where the protocol is baked in, causing problems when both http and https are served). The Javascript itself shouldn’t include any baked-in URLs.

In any case, glad to hear it’s resolved!

Regards,
Alec Smecher
Public Knowledge Project Team

Hi,
There are some posts in the forum talking about issues related with “reverse proxy”. IMHO, with Cloud and docker this will be a more and common scenario so I think we need a solution for this.

To take the whole picture, those are some examples:

I though was fair continue talking about this problem here because this post is one of the most recent, well explained and include a fix that just need to be implemented (patch posted here is not generic enough).

Although I agree with Clinton with this:

This will not be possible/feasible/desirable/comfortable in some scenarios and (please correct me) I think the problem here is that OJS is not working as it should.

IMHO (and I have a limited perspective) OJS is asking about the protocol to SCRIPT_URI, when it need to honor the protocol specified at HTTP_X_FORWARDED_PROTO (if exists).

As published before in this thread typo3 are also dealing with similar issues and they fixed it the way specified:
https://forge.typo3.org/issues/29693

Tomorrow I will also dig into Drupal and Wordpress to be sure we are moving in the same direction, but if you agree, I can make a PR for 3.2 and 3.3 to fix this issue and make “reverse-proxiers” life easier. :slight_smile:

The patch is as simple as adding a condition here:

To check if HTTP_X_FORWARDED_PROTO exists, and if yes, set the protocol to be whatever you find in the variable.

Waiting for your comments,
m.

As promised I checked Drupal and WP and they have different approaches.

Drupal implemented the full X-FORWARD-* directive that you can set in the config file:

This feature was introduced 12 years ago and is still active. This make sense because Drupal is a CMS that quite common to see behind load balancers, haproxies or intensive caching (vagrant) and so on where you don’t want/need the ssl complexity and performance is the priority.

In the other hand, as far as I see, WP don’t implement it in core but offer 3 different solutions:

I think in WP they also full implement “relative protocol” so in some cases it would be an additional solution.

Although in the original typo3 discussion posted by @ps_tadler they said they are against this solution for security concerns, I think typo3 finally implements it.

Anyway, both (WP and typo3) propose the use of Apache/Nginx rules behind the proxy to change the environment variables, so I give a try to this just adding this rule in the destination apache’s virtualhost to “Let apache know we’re behind a SSL reverse proxy” and works like a charm:

SetEnvIf X-Forwarded-Proto "https" HTTPS=on

I need to dig into this deeper, but first test let me login, navigate upload and so on… without touching the original OJS code.

If somebody can confirm this solution with his/her own infrastructure (you have an equivalent rule in nginx) I would encourage to document and propose this as a final solution for “reverse proxies” because IMHO this is better than ssl end-to-end.

Anyway, even is not as urgent now, with with the change of context in the infrastructures, I still think would be nice to offer a full implementation of the X-FORWARD-tags in OJS one day to let admins get full control about how things will be seen behind the proxy.