Piwigo Security – protect your images

This is second post on the topic of Piwigo Security. I have discussed use of .htaccess file and how to prevent some of the common exploits earlier.

Recent discussion at the forum about image protection made me to take a look at how images are stored and what visitors can do to otherwise protected resources stored on PWG site.

Protect originals

This is probably most important step and would insure your original files are secure and cannot be accessed from outside.

Note1: You will have to disable access to HD versions of your photos. Make sure plugins and themes you are using do not try to override settings set below
Note2: I assume you do have FTP access to your site

  1. Open your “local/config/config.inc.php” file. If you do not have one yet (unlikely), you need to create one.
  2. Add the following two lines
    // one of '', 'images', 'all'
    $conf['original_url_protection'] = 'images';

    I like to keep original comment reference to the settings, but you can remove comment if desired

  3. Save the file
  4. Go to “galleries/” FTP folder and create .htaccess file with one line in it
    deny from all
  5. Save the file
  6. A bit awkward, but… go to “Users->Manage“, select all active users and under Action select “High definition enabled” and “No“, then “Apply action“. This would hide “Original” photo size where applicable.
  7. Login as admin in PWG and go to “Tools->Maintenance” and click on “Purge compiled templates
  8. Test your gallery to make sure everything is still working

Protect derivatives

As a next step we would also want to hide from curious folks content of derivatives folder.
What is derivatives in PWG? Derivatives are any photo resizes, photo thumbs, and video thumbs or any other forms of derived image data used in the gallery. Effectively it is precompiled cached data which is used to present your art work to your site’s public or registered visitors.

Please note that by doing so we do not protect, restrict access to derivative itself when it is permission protected. Current implementation of i.php does not take in consideration user permissions and only works as simple caching mechanism. If visitor can “guess” file’s location and name, image would be accessible (PWG 2.7 and below)

Since all data is store in one place we would want to make it so that it would be harder for anyone to “guess” pictures they cannot otherwise see.

  1. Open your “local/config/config.inc.php” file.
  2. Add the following two lines
    // Specify url format // 0-'auto', 1-'derivative' 2-'script'
    $conf['derivative_url_style'] = 2;
  3. Save the file
  4. Go to “_data/I/” FTP folder and create .htaccess file with one line in it
    deny from all
  5. Save the file
  6. Login as admin in PWG and go to “Tools->Maintenance” and click on “Purge compiled templates
  7. Test your gallery to make sure everything is still working
Posted in Piwigo | 2 Comments

gdThumb 1.0.11 has been released – hot fix

+ ADMIN: Optimized use of GreyDragon Theme to remove code duplication
– Fixed issue with state of “Block for Panoramic Photo Page” not recognized properly

Posted in Piwigo | Leave a comment

Piwigo and htaccess

As a long time WordPress and Gallery 3 user I become accustomed to some detailed guidelines on how htaccess file is managed and what can be added there to improve site speed and security.

Unfortunately, there is no significant documentation about that in Piwigo. So I have decided to compile sample htaccess rules you can use to achieve some of the goals above.

By default Piwigo does not come with htaccess file, so there should be no apparent conflict with any of the existing auto generated content.

DISCLAIMER: As with any htaccess please make use it on your own risk because your individual setup may vary from one rules below are tested for. Please read associated comments for each rule to see if it is applicable to your setup. Assumption is made that htaccess support is enabled for the server, you have FTP access and min version of PHP is 5.0+

Note: you need adjust site and email references in content below – look for yoursite.com

htaccess

# Set some reasonable defaults for PHP.  Most of these cannot be set
# inside the script itself.  For hosts that don't have .htaccess
# support but do support per-dir php.ini files, these settings are
# mirrored in php.ini
#
<IfModule mod_php5.c>
php_flag short_open_tag            On
php_flag magic_quotes_gpc          Off
php_flag magic_quotes_sybase       Off
php_flag magic_quotes_runtime      Off
php_flag register_globals          Off
php_flag session.auto_start        Off
php_flag suhosin.session.encrypt   Off
php_value upload_max_filesize   20M
php_value post_max_size     100M
</IfModule></code>
AcceptPathInfo on
############ disable directory browsing, includes, etc
#
Options +MultiViews -ExecCGI -Includes -Indexes FollowSymLinks
IndexIgnore *
############ Set default character set, turn off server signature, set server admin email, enable MP4 support
#
SetEnv TZ America/New_York
SetEnv SERVER_ADMIN webmaster@yoursite.com
ServerSignature off
AddDefaultCharset UTF-8
DefaultLanguage en-US
AddType audio/mp4 m4a
# Disallow script execution
AddHandler cgi-script .pl .py .jsp .asp .htm .shtml .sh .cgi
<FilesMatch "^php5?\.(ini|cgi)$">
Order Deny,Allow
Deny from All
Allow from env=REDIRECT_STATUS
</FilesMatch>
<IfModule mod_rewrite.c>
RewriteEngine On
# Safe Request Methods
RewriteCond %{REQUEST_METHOD} !^(GET|HEAD|POST|PROPFIND|OPTIONS|PUT)$ [NC]
RewriteRule .* - [F,NS,L]
# Bogus Graphics Exploit
RewriteCond %{HTTP:Content-Disposition} \.php [NC]
RewriteCond %{HTTP:Content-Type} image/.+ [NC]
RewriteRule .* - [F,NS,L]
# Trackback Spam
RewriteCond %{REQUEST_METHOD} =POST
RewriteCond %{HTTP_USER_AGENT} ^.*(opera|mozilla|firefox|msie|safari).*$ [NC]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.+/trackback/?\ HTTP/ [NC]
RewriteRule .* - [F,NS,L]
</IfModule>
# Increase security by uncommenting this block.  It keeps browsers
# from seeing support files that they shouldn't have access to.  We
# comment this out because Apache2 requires some minor configuration
# in order for you to use it.  You must specify "AllowOverride Limit"
# in your Apache2 config file before you uncomment this block or
# you'll get an "Internal Server Error".
<FilesMatch "(\.(class|fla|gitignore|inc|ini|sql|txt)|(README|LICENSE|.build_number))$">
Order deny,allow
Deny from all
</FilesMatch>
<FilesMatch "robots.txt">
Order allow,deny
Allow from all
</FilesMatch>
<IfModule mod_deflate.c>
# compress the files
AddOutputFilter DEFLATE js css php
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/x-javascript application/x-httpd-php
# removes some bugs
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png)$ no-gzip dont-vary
Header append Vary User-Agent env=!dont-vary
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "\.(js|css|xml|gz)$">
Header append Vary Accept-Encoding
</FilesMatch>
</IfModule>
<IfModule mod_gzip.c>
#
# http://www.schroepl.net/projekte/mod_gzip/config.htm
mod_gzip_on                   Yes
mod_gzip_can_negotiate        Yes
mod_gzip_static_suffix        .gz
AddEncoding              gzip .gz
mod_gzip_update_static        No
mod_gzip_command_version      '/mod_gzip_status'
mod_gzip_keep_workfiles       No
mod_gzip_minimum_file_size    500
mod_gzip_maximum_file_size    500000
mod_gzip_maximum_inmem_size   60000
mod_gzip_min_http             1000
mod_gzip_handle_methods       GET POST
mod_gzip_item_exclude         reqheader  "User-agent: Mozilla/4.0[678]"
mod_gzip_dechunk              Yes
mod_gzip_item_include         file       \.css$
mod_gzip_item_include         file       \.js$
</IfModule>
# Improve performance by uncommenting this block.  It tells the
# browser that your images don't change very often so it won't keep
# asking for them.  If you get an error after uncommenting this, make
# sure you specify "AuthConfig Indexes" in your Apache config file.
#
<IfModule mod_expires.c>
# Turn on Expires and set default to 0
ExpiresActive On
ExpiresDefault A0
# Set up caching on media files for 1 year (forever?)
<FilesMatch "\.(flv|ico|pdf|avi|mov|ppt|doc|mp3|wmv|wav|m4a)$">
ExpiresDefault A29030400
Header append Cache-Control "public"
</FilesMatch>
# Set up caching on media files for 1 month
<FilesMatch "\.(jpg|jpeg|png|gif|swf)$">
ExpiresDefault A2678400
Header append Cache-Control "public"
</FilesMatch>
# Set up 2 Hour caching on commonly updated files
<FilesMatch "\.(xml|txt|html|js|css)$">
ExpiresDefault A7200
Header append Cache-Control "proxy-revalidate"
</FilesMatch>
# Force no caching for dynamic files
<FilesMatch "\.(php|cgi|pl|htm|html)$">
ExpiresActive Off
Header set Cache-Control "private, no-cache, no-store, proxy-revalidate, no-transform"
Header set Pragma "no-cache"
</FilesMatch>
</IfModule>
####################### BEGIN Url Rewrite section
#
<IfModule mod_rewrite.c>
Options +FollowSymlinks
RewriteEngine On
RewriteBase /
########## Error Documents
#
#ErrorDocument 400 /error400.php
#ErrorDocument 401 /error401.php
#ErrorDocument 403 /error403.php
#ErrorDocument 404 /error404.php
#ErrorDocument 500 /error500.php
#
############################################
########## Site protection logic
#
########## SEF Section
#
#  RewriteCond %{REQUEST_URI} (/|\.htm|\.php|\.html|/[^.]*)$  [NC]
#  RewriteCond %{REQUEST_FILENAME} !-f
#  RewriteCond %{REQUEST_FILENAME} !-d
#  RewriteRule (.*) /error404.php
#
############################################
########## Stop hot linking
#
#  RewriteCond %{HTTP_REFERER} !^$
#  RewriteCond %{HTTP_REFERER} !^http://(*\.)?yoursite.com/.*$ [NC]
#  RewriteRule \.(gif|jpg|png|js|css|flv|swf)$ - [F]
#
############################################
########## Rewrite rules to block out some common exploits
#
# Block out any script trying to set a mosConfig value through the URL
#  RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|\%3D) [OR]
# Block out any script trying to base64_encode crap to send via URL
#  RewriteCond %{QUERY_STRING} base64_encode.*\(.*\) [OR]
# Block out any script that includes a <script> tag in URL
#  RewriteCond %{QUERY_STRING} (\<|%3C).*script.*(\>|%3E) [NC,OR]
# Block out any script trying to set a PHP GLOBALS variable via URL
#  RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
# Block out any script trying to modify a _REQUEST variable via URL
#  RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
# Send all blocked request to homepage with 403 Forbidden error!
#  RewriteRule ^(.*)$ index.php [F,L]
#
############################################
########## Block PHP injection attempts
#
RewriteCond %{QUERY_STRING} ^(.*)=http: [NC]
RewriteRule ^(.*)$ - [F,L]
#
############################################
########## Remove Query_String. May conflict with some plugins and logic
#
#  RewriteCond %{THE_REQUEST} ^GET\ /.*\;.*\ HTTP/
#  RewriteCond %{QUERY_STRING} !^$
#  RewriteRule .* http://yoursite.com%{REQUEST_URI}? [R=301,L]
#
############################################
########## From evil bots
# Update this list from http://www.invision-graphics.com/robotstxt_badbots.html
#
RewriteCond %{HTTP_USER_AGENT} ^BlackWidow [OR]
RewriteCond %{HTTP_USER_AGENT} ^Bot\ mailto:craftbot@yahoo.com [OR]
RewriteCond %{HTTP_USER_AGENT} ^ChinaClaw [OR]
RewriteCond %{HTTP_USER_AGENT} ^Custo [OR]
RewriteCond %{HTTP_USER_AGENT} ^DISCo [OR]
RewriteCond %{HTTP_USER_AGENT} ^Download\ Demon [OR]
RewriteCond %{HTTP_USER_AGENT} ^eCatch [OR]
RewriteCond %{HTTP_USER_AGENT} ^EirGrabber [OR]
RewriteCond %{HTTP_USER_AGENT} ^EmailSiphon [OR]
RewriteCond %{HTTP_USER_AGENT} ^EmailWolf [OR]
RewriteCond %{HTTP_USER_AGENT} ^Express\ WebPictures [OR]
RewriteCond %{HTTP_USER_AGENT} ^ExtractorPro [OR]
RewriteCond %{HTTP_USER_AGENT} ^EyeNetIE [OR]
RewriteCond %{HTTP_USER_AGENT} ^FlashGet [OR]
RewriteCond %{HTTP_USER_AGENT} ^GetRight [OR]
RewriteCond %{HTTP_USER_AGENT} ^GetWeb! [OR]
RewriteCond %{HTTP_USER_AGENT} ^Go!Zilla [OR]
RewriteCond %{HTTP_USER_AGENT} ^Go-Ahead-Got-It [OR]
RewriteCond %{HTTP_USER_AGENT} ^GrabNet [OR]
RewriteCond %{HTTP_USER_AGENT} ^Grafula [OR]
RewriteCond %{HTTP_USER_AGENT} ^HMView [OR]
RewriteCond %{HTTP_USER_AGENT} HTTrack [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^Image\ Stripper [OR]
RewriteCond %{HTTP_USER_AGENT} ^Image\ Sucker [OR]
RewriteCond %{HTTP_USER_AGENT} Indy\ Library [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^InterGET [OR]
RewriteCond %{HTTP_USER_AGENT} ^Internet\ Ninja [OR]
RewriteCond %{HTTP_USER_AGENT} ^JetCar [OR]
RewriteCond %{HTTP_USER_AGENT} ^JOC\ Web\ Spider [OR]
RewriteCond %{HTTP_USER_AGENT} ^larbin [OR]
RewriteCond %{HTTP_USER_AGENT} ^LeechFTP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mass\ Downloader [OR]
RewriteCond %{HTTP_USER_AGENT} ^MIDown\ tool [OR]
RewriteCond %{HTTP_USER_AGENT} ^Mister\ PiX [OR]
RewriteCond %{HTTP_USER_AGENT} ^Navroad [OR]
RewriteCond %{HTTP_USER_AGENT} ^NearSite [OR]
RewriteCond %{HTTP_USER_AGENT} ^NetAnts [OR]
RewriteCond %{HTTP_USER_AGENT} ^NetSpider [OR]
RewriteCond %{HTTP_USER_AGENT} ^Net\ Vampire [OR]
RewriteCond %{HTTP_USER_AGENT} ^NetZIP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Octopus [OR]
RewriteCond %{HTTP_USER_AGENT} ^Offline\ Explorer [OR]
RewriteCond %{HTTP_USER_AGENT} ^Offline\ Navigator [OR]
RewriteCond %{HTTP_USER_AGENT} ^PageGrabber [OR]
RewriteCond %{HTTP_USER_AGENT} ^Papa\ Foto [OR]
RewriteCond %{HTTP_USER_AGENT} ^pavuk [OR]
RewriteCond %{HTTP_USER_AGENT} ^pcBrowser [OR]
RewriteCond %{HTTP_USER_AGENT} ^RealDownload [OR]
RewriteCond %{HTTP_USER_AGENT} ^ReGet [OR]
RewriteCond %{HTTP_USER_AGENT} ^SiteSnagger [OR]
RewriteCond %{HTTP_USER_AGENT} ^SmartDownload [OR]
RewriteCond %{HTTP_USER_AGENT} ^SuperBot [OR]
RewriteCond %{HTTP_USER_AGENT} ^SuperHTTP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Surfbot [OR]
RewriteCond %{HTTP_USER_AGENT} ^tAkeOut [OR]
RewriteCond %{HTTP_USER_AGENT} ^Teleport\ Pro [OR]
RewriteCond %{HTTP_USER_AGENT} ^VoidEYE [OR]
RewriteCond %{HTTP_USER_AGENT} ^Web\ Image\ Collector [OR]
RewriteCond %{HTTP_USER_AGENT} ^Web\ Sucker [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebAuto [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebCopier [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebFetch [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebGo\ IS [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebLeacher [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebReaper [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebSauger [OR]
RewriteCond %{HTTP_USER_AGENT} ^Website\ eXtractor [OR]
RewriteCond %{HTTP_USER_AGENT} ^Website\ Quester [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebStripper [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebWhacker [OR]
RewriteCond %{HTTP_USER_AGENT} ^WebZIP [OR]
RewriteCond %{HTTP_USER_AGENT} ^Wget [OR]
RewriteCond %{HTTP_USER_AGENT} ^Widow [OR]
RewriteCond %{HTTP_USER_AGENT} ^WWWOFFLE [OR]
RewriteCond %{HTTP_USER_AGENT} ^Xaldon\ WebSpider [OR]
RewriteCond %{HTTP_USER_AGENT} ^Zeus [OR]
RewriteCond %{HTTP_USER_AGENT} ^RepoMonkey\ Bait\ &\ Tackle/v1.01 [OR]
RewriteCond %{HTTP_USER_AGENT} ^RepoMonkey [or]
RewriteCond %{HTTP_USER_AGENT} ^Zeus\ 32297\ Webster\ Pro\ V2.9\ Win32\ [or]
RewriteCond %{HTTP_USER_AGENT} ^Webster\ Pro [or]
RewriteCond %{HTTP_USER_AGENT} ^EroCrawler [or]
RewriteCond %{HTTP_USER_AGENT} ^LinkScan/8.1a\ Unix [or]
RewriteCond %{HTTP_USER_AGENT} ^Keyword\ Density/0.9 [or]
RewriteCond %{HTTP_USER_AGENT} ^Kenjin\ Spider [or]
RewriteCond %{HTTP_USER_AGENT} ^Cegbfeieh
RewriteRule ^.* - [F,L]
#
############################################
####################### END Url Rewrite section
</IfModule>
<files .htaccess>
########## Begin - Deny access (firewall)
order allow,deny
deny from all
########## End - Deny access (firewall)
</files>
###################Do not have any whitespace/empty lines after this line#################
Posted in Piwigo | Leave a comment

Gallery 3 to Piwigo – plugins and features wish list

This is evolving post which would be changing over time to collect your wish list of items you loved in Gallery 3 and want to get ported to Piwigo.

Something missing? Post a comment and I will adjust list to include it.

  • disqus comments (G3 code)
  • Facebook comments
  • Album Tree (G3 code)
Posted in Gallery 3 Development, Piwigo | 3 Comments