{"id":281,"date":"2015-07-25T15:02:58","date_gmt":"2015-07-25T15:02:58","guid":{"rendered":"https:\/\/ca.non.co.il\/?p=281"},"modified":"2017-06-09T15:56:15","modified_gmt":"2017-06-09T15:56:15","slug":"securing-private-piwigo-albums","status":"publish","type":"post","link":"https:\/\/ca.non.co.il\/index.php\/securing-private-piwigo-albums\/","title":{"rendered":"Securing Private Piwigo Albums"},"content":{"rendered":"<p>TL;DR if you just wanna see the code and simple install instructions see the <a href=\"https:\/\/github.com\/yonjah\/piwigo-privacy\">Github repo<\/a><\/p>\n<p class=\"info-box\">\nUpdate &#8211; The original code had a major rewrite to be used as a plugin that will not require any NGINX\/Apache special features. Yet it can use advance features like X-Accel-Redirect\/X-send-files if available.<br \/>\nThough the ideas and methods described in this article should work and are still good to understand the concept, a new and better implementation is available. Information about the new implementation is available in the Github repo.\n<\/p>\n<p>As many other people I was looking for a solution to control and share my private photos with family and friends. Google, Facebook and other 3rd parties will allow you to share your content easily with your friends. But do you really know who has access to your private data ? who controls it ? and who owns it ?<br \/>\n<!--more--><\/p>\n<p>Once you upload your images to a 3rd party providers you trust them to keep it private and secure, you could argue that this companies are doing a good and probably better job than many others, but even if we assume their system is 100% secure do you really want to share all your data with all your facebook friends ? do all the people you want to give access to your content have a Google account ?<\/p>\n<p>So for me the obvious choice was to host my own image gallery.<br \/>\nI thought I might need to write something from scratch, but luckily <a href=\"http:\/\/piwigo.org\/\">Piwigo<\/a> covers 99% of my use case so for now it seems like a good solution.<\/p>\n<p>But one of the issues I had with Piwigo is that even for private content all the static files are available to be accessed directly with no user authentication Piwigo protects the file by &#8220;randomizing&#8221; part of the file name which is probably secure for most use cases but will not protect your content in any way if someone decides to pass the links forward (and I would prefer it to be 20 chars of real random data and not 8 chars of the file md5 hash). <\/p>\n<p>When looking at security we should always consider our adversary. In this case we are considering very limited and week one. If you have data you want to keep private, don&#8217;t upload it to the Internet, better yet don&#8217;t keep it on a computer that is connected to the Internet or even better keep it only in a usb stick which is kept in a locked safe (and some prefer to keep it in a shoe box next to the safe). What I&#8217;m trying to say is that since we are any way want this data to be available to multiple parties online it is going to be very hard to keep it private from any one who will really want access to it. so the only thing I&#8217;m actually trying to prevent is my private images ending up on Google image search (I wish I had the time to write end to end encrypted fie sharing solution simple enough for my grandma to use but I currently don&#8217;t).<\/p>\n<p>This issue is a known problem with Piwigo that <a href=\"http:\/\/blog.dragonsoft.us\/2014\/10\/15\/piwigo-security-protect-your-images\/\">others<\/a> have tried to solve this issue but I couldn&#8217;t find a full solution that will work with videos and. Since Piwigo does all the hard work of access management I was sure there was an easy way to do it.<\/p>\n<h2>NGINX to the rescue<\/h2>\n<p>So apparently NGINX has this great little feature to called <a href=\"http:\/\/nginx.org\/en\/docs\/http\/ngx_http_auth_request_module.html\">auth_request<\/a> that guess what authorizes a request, using this directive NGINX will first proxy the request to another url and if that url returns successfully the request will be served if not it will be rejected. notice that NGINX doesn&#8217;t serve back the proxy result but actually continues to serve the original location, which means that we can still take advantage of NGINX when serving our static content (like cache, gzip and so on)<\/p>\n<p>unfortunately we need to compile NGINX with the &#8216;&#8211;with-http_auth_request_module&#8217; flag to have auth_request so you&#8217;ll need to download <a href=\"http:\/\/nginx.org\/en\/download.html\">NGINX source<\/a> and compile it yourself to get it working.<br \/>\nI used the following configuration for my server &#8211;<\/p>\n<pre class=\"lang:sh decode:true\">\r\n.\/configure \\\r\n    --user=www-data                       \\\r\n    --group=www-data                      \\\r\n    --prefix=\/etc\/nginx                   \\\r\n    --sbin-path=\/usr\/sbin\/nginx           \\\r\n    --conf-path=\/etc\/nginx\/nginx.conf     \\\r\n    --pid-path=\/run\/nginx.pid             \\\r\n    --lock-path=\/run\/nginx.lock           \\\r\n    --error-log-path=\/var\/log\/nginx\/error.log \\\r\n    --http-log-path=\/var\/log\/nginx\/access.log \\\r\n    --with-http_gzip_static_module        \\\r\n    --with-http_stub_status_module        \\\r\n    --with-http_ssl_module                \\\r\n    --with-pcre                           \\\r\n    --with-file-aio                       \\\r\n    --with-http_realip_module             \\\r\n    --without-http_scgi_module            \\\r\n    --without-http_uwsgi_module           \\\r\n    --with-http_auth_request_module\r\n<\/pre>\n<p>But you might need to install some extra libs depending on what you already have installed on your system<\/p>\n<p>once the configuration pass you can build and install <\/p>\n<pre class=\"lang:sh decode:true\">\r\nmake\r\nsudo make install\r\n<\/pre>\n<p>now once we have NGINX with auth_request working we can go to the fun stuff.<br \/>\nHere is how to use auth_request to validate any request that goes to Piwigo uploads and derivatives location &#8211;<\/p>\n<pre class=\"lang:php decode:true\">\r\nlocation \/upload {\r\n\tauth_request \/auth.php;\r\n}\r\n\r\nlocation \/_data\/i {\r\n\tauth_request \/auth.php;\r\n}\r\n\r\nlocation = \/auth.php {\r\n\tfastcgi_pass unix:\/var\/run\/php5-fpm.sock;\r\n\tfastcgi_index  auth.php;\r\n\tfastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;\r\n\tfastcgi_param  SCRIPT_NAME  $fastcgi_script_name;\r\n\tinclude fastcgi_params;\r\n}\r\n<\/pre>\n<p>Pretty straight forward.<br \/>\nAs you can see we are using the \/auth.php file to validate the request.<\/p>\n<p>now every request being made to \/upload will only goes throe if auth.php returns a successful response. so what we need now is to have a script named auth.php in our server root that will know if the user can actually access the file.<\/p>\n<p>I wont go into all the details of writing this <a href=\"https:\/\/github.com\/yonjah\/piwigo-privacy\/blob\/master\/auth.php\">script<\/a>,but it wasn&#8217;t too complex. What I basically did is reversing the logic in Piwigo action.php detecting the image db record from the requested url and then testing to see if the user is allowed to access the file.This script will return 401 errors instead of 404 since we don&#8217;t want someone to try guessing which files we might have on the server. I&#8217;m not very fluent with Piwigo internals so I hope I haven&#8217;t missed any edge cases.<\/p>\n<p>As a bonus we can take our NGINX site config a step farther and actually redirect the user to a default image if she doesn&#8217;t have access to the image she is asking.<\/p>\n<pre class=\"lang:php decode:true\">\r\nlocation \/upload {\r\n\tauth_request \/auth.php;\r\n\tauth_request_set $auth_redirect '\/default.png';\r\n\terror_page 401 = \/auth_401;\r\n}\r\n\r\nlocation = \/auth_401 {\r\n\tif ($auth_redirect) {\r\n\t\treturn 302 $auth_redirect;\r\n\t}\r\n\treturn 401;\r\n}\r\n<\/pre>\n<p>If you haven&#8217;t you might want to take some more steps to harden your Piwigo system  (like ssl and <a href=\"https:\/\/freeandthings.wordpress.com\/2015\/06\/17\/fail2ban-configuration-for-piwigo-failed-logins\/\">fail2ban<\/a> and preventing direct access to php files in subfolders)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>TL;DR if you just wanna see the code and simple install instructions see the Github repo Update &#8211; The original code had a major rewrite to be used as a plugin that will not require any NGINX\/Apache special features. Yet it can use advance features like X-Accel-Redirect\/X-send-files if available. Though the ideas and methods described [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-281","post","type-post","status-publish","format-standard","hentry","category-web-development","last-post"],"_links":{"self":[{"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/posts\/281","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=281"}],"version-history":[{"count":11,"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/posts\/281\/revisions"}],"predecessor-version":[{"id":485,"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/posts\/281\/revisions\/485"}],"wp:attachment":[{"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=281"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=281"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ca.non.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=281"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}