Koken SSL Issue, Resolved

The Problem

I recently started to test using Koken to self-host some of my photography work and I was having a lot of mysterious issues.  My server is configured running nginx and all domains are 100% SSL enabled (using Let’s Encrypt). There is no non-SSL access to any site on my server.  Koken is a PHP application built on the CodeIgniter framework, so for php-fpm is in the configuration mix too.

While Koken looks really nice, nice enough for me to spend time trying to resolve my issues, the support is pretty poor.  There are hundreds of posts related to these issues, which I have resolved with one configuration item in my nginx configuration.  I hope this might solve some problems for a lot of other people since development appears to be very slow.  Koken was sold recently and the new maintainers are not releasing updates that often.

The issues apear isolated to people running Koken on nginx with SSL only access.  Symptoms of the issue are:

  • SSL Mixed Content Warnings
  • Unable to Log in, despite using the correct password
  • Settings do not save
  • Themes will not apply
  • Cannot contact API error messages

Resolution

As it turns out, /api.php  is the primary way the application talks to itself, and /app/site/site.php  is the primary controller for things.  After a bunch of logging and debugging I noticed that site.php  was making a lot of calls to api.php  using a non ssl uri.  For whatever reason these calls never made it to my 301 redirects (HTTP -> SSL) and would just silently fail – resulting in a bunch of strange issues where the site appears to “mostly work”, but a lot of core functionality seems broken.

Upon realizing that the application was calling the api from http instead of https I was hoping I could resolve the issue without modifying the source code.  I did not want to have fiddled with the source such that any time I update it, I would have to remember to patch the files.  After a bit of digging I found that Koken determines if a site is running SSL using the following code snippet from /app/site/Koken.php :

        if ($options['echo'])
        {
            if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on')
            {
                $prefix = 'https:';
            }
            else
            {
                $prefix = 'http:';
            }
            $url = $prefix . '//' . $_SERVER['HTTP_HOST'] . $url;
            echo $url;
        }

There are a number of places throughout the code which make reference to the $_SERVER[‘HTTPS’]  variable, some deal with it properly, some do not.  The above snippet is just one example, to correct the issue in the code itself you would have modify 5 or 6 files.  While I feel the Koken developers can fix this on their end, or at least document the configuration change needed to correct it, the fix is quite simple.

In your fastcgi.conf, or the site configuration file, or wherever you store it, add the following line:

fastcgi_param  HTTPS 'on';

This will properly set the $_SERVER[‘HTTPS’]  variable to something that will work with how Koken expects to reference it (like Apache), and everything will work fine.

The relevant sections of my entire working koken.conf file for nginx looks like this:

server {
    listen      443 ssl http2;
    listen      [::]:443 ssl http2;
    server_name kokensite;

    add_header Strict-Transport-Security "max-age=63072000";
    #add_header X-Frame-Options DENY;

    ssl on;
    ssl_certificate     /path/to/cert/fullchain.pem;
    ssl_certificate_key /path/to/cert/privkey.pem;

    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /path/to/cert/fullchain.pem;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;

    ssl_ecdh_curve secp384r1;

    ssl_session_cache shared:SSL:100m;
    ssl_session_timeout 24h;
    ssl_session_tickets on;
    ssl_session_ticket_key /path/to/ticket.key;
    
    ssl_dhparam /path/to/dhparam4.pem;  

    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';

    resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 216.146.35.35 216.146.36.36 valid=300s;
    resolver_timeout 3s;

    error_log   /path/to/koken/log/koken.error.log;
    # access_log  off;
    access_log  /path/to/koken/log/koken.access.log;

    gzip on;
    gzip_disable msie6;
    gzip_static on;
    gzip_comp_level 9;
    gzip_proxied any;
    gzip_min_length  1100;
    gzip_buffers 16 8k;
    gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

    expires 0;

    root /path/to/koken/html;
    index index.php index.html;
    
    # Do not allow access to hidden files.
    location ~ /\. { deny all; }

    # pass all other PHP requests to main backend
    location ~ \.php$ {
        try_files $uri =404;
        include       /etc/nginx/fastcgi.conf;
        fastcgi_param  HTTPS 'on';
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_index index.php;
        fastcgi_pass  unix:/var/run/php-fpm/php-fpm.sock;
    }
    
    location ~ /\.ht {
        deny all;
    }

    # Set expires header for console CSS and JS.
    # These files are timestamped with each new release, so it is safe to cache them agressively.
    location ~ "console_.*\.(js|css)$" {
        expires max;
    }

    # Catch image requests and pass them back to PHP if a cache does not yet exist
    location ~ "^/storage/cache/images(/(([0-9]{3}/[0-9]{3})|custom)/.*)$" {
        # Cached images have timestamps in the URL, so it is safe to set
        # aggresive cache headers here.
        expires max;
        try_files $uri /i.php?path=$1;
    }

    # Catch .css.lens requests and serve cache when possible
    location ~ "(lightbox-)?settings.css.lens$" {
        default_type text/css;
        try_files /storage/cache/site/${uri} /app/site/site.php?url=/$1settings.css.lens;
    }

    # Catch koken.js requests and serve cache when possible
    location ~ koken.js$ {
        default_type text/javascript;
        try_files /storage/cache/site/${uri} /app/site/site.php?url=/koken.js;
    }

    # Standard site requests are cached with .html extensions
    set $cache_ext 'html';

    # PJAX requests contain the _pjax GET parameter and are cached with .phtml extensions
    if ($arg__pjax) {
        set $cache_ext 'phtml';
    }

    # Do not check for a cache for non-GET requests
    if ($request_method != 'GET') {
        set $cache_ext 'nocache';
    }

    # If share_to_tumblr cookie is preset, disable caching (long story)
    if ($http_cookie ~* "share_to_tumblr" ) {
        set $cache_ext 'nocache';
    }

    # Catch root requests
    location ~ ^/?$ {
        try_files /storage/cache/site/index/cache.$cache_ext /app/site/site.php?url=/;
    }

    # All other requests get passed back to Koken unless file already exists
    location / {
        try_files $uri $uri/ /storage/cache/site/${uri} /storage/cache/site/${uri}cache.$cache_ext /app/site/site.php?url=$uri&$args;
    }

    # Original file directories
    location ~ "^/storage/originals(/(([0-9]{2}/[0-9]{2}))/.*)$" {
        deny all;
    }

}

One thought on “Koken SSL Issue, Resolved

  1. Hi Brian.

    Excellent post – just what the Dr ordered!

    My particular problem was proxy related. Have a front-end Apache proxying to a different VM back-end. Ended up (before adding requestheader set HTTPS on) having mixed urls being used – index.php served correctly with https, with links (and ajax loads) on the page reverting to http – madness. This of course meant that users were seeing the SSL padlock for a fleeting moment, before the site reverted to “unsecure”.

    NOT good when you want to instill confidence, host the cart plugin and expect customers to enter payment details.

    Thanks again.
    –D.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.