Self-hosting Zenphoto on Windows 7 (IIS7, PHP & MySQL)

I really like ZenPhoto – it’s a solid photo gallery with an easy point and click web admin GUI.
The main thing I dig is that i can point it at my main photos folder on my hard drive (via a quick symbolic link) and it goes to town dynamically publishing whatever I drop there without any other fiddling… that’s photo sharing nirvana if you ask me. [Update: 25 Oct 2010] After running the gallery for a couple days I’d have to say Apache did a better job at popping the pages back than what I’ve got setup under IIS so far… maybe Apache just deals with these more CGI oriented modules better than IIS can for some fundamental reason… I do have PHP “FastCGI” enabled for IIS… any performance tips would be greatly appreciated 🙂 I’ll have to go find some trace tools to see where it’s spending most of its time.  I guess it could still be caching images since ZenPhoto pulls a new random image for the album cover each time you refresh the page and I did wipe the cache when I migrated over to IIS. [Update: 29 Oct 2010] Setting Admin Options > Image subtab > Full image protection = Unprotected – yields a noticeable speed boost presumably because it skips a bunch of file I/O… now it feels back on par with what I was seeing in Apache… unfortunately, I just don’t remember how I had that setting under Apache. Installs:

  • I installed them all to c:Program Files because that’s my speedy SSD and I want this site to be as performant as possible
    • then i simply SymLink my main photos folder (on a RAID1 volume elsewhere) over the top of c:Program Fileszenphotoalbums
    • here’s an awesome SymLink utility for Windows Explorer!!
  • IIS – I’m on Win7 so it’s IIS7 – Apache’s cool and all but i saw a note somewhere that gave me the impression that on Windows, IIS + PHP via FastCGI module is  going to be more performant than Apache… otherwise, I did previously run it all on Apache just fine via the nifty "XAMPP" stack that installs everything for you in minutes and it "just works" which was honestly much less trouble than getting it all to hang together under IIS7 myself.
  • PHP – there’s a specific Windows/IIS “Fast CGI” version (current version: 5.3.3) (see this for Thread Safe vs Non Thread Safe binaries, non thread safe + IIS FastCGI is most performant)
  • MySQL – and their WorkBench tool is handy (current version: 5.1.51)
    • there’s a lot of environmental tuning questions during the install wizard but i mostly selected default settings
    • I chose to go with a MySQL_Data subfolder for the datafiles
    • configure for TCP/IP access (i don’t yet know how to configure PHP to connect to MySQL over named pipes)
  • ZenPhoto – just an unzip (current version: 1.3.1.2)
  • zpGallerific theme (current version: 1.0)

FIREWALL!!! turn it completely off to begin with so you know whether it’s your main problem or not

    • I had to add these two rules to BitDefender
    • image
    • THE ORDER OF THE RULES MATTERS… MOVE THESE TO THE VERY TOP of the list with the arrow buttons!!
    • helpful: http://forum.bitdefender.com/index.php?showtopic=12764
    • nutshell: to see what’s blocked "Increase Verbosity" and "Show Log" on Activity tab 

Folder Permissions:

  • grant IUSR full permissions to root zenphoto folder (IIS_IUSRS group did NOT work)
    • it was also necessary on the true target of the symlinked albums folder
  • something happened on my win7 box where my albums folder was no longer accessible to zenPhoto/PHP… maybe a Windows Update closed a security loophole or something…

IIS Tweaks:

  • Enable 32bit PHP under IIS on 64bit Windows
    • install IIS6.0 script compatibily
    • cscript %SYSTEMDRIVE%inetpubadminscriptsadsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnW
  • MOD_REWRITE
    • once I got everything fired up I realized that it’d be nice to support my old URLs that I’ve mailed out to everybody already
    • interesting thing was, Apache was doing something cool I didn’t realize… it was mod_rewrite’ing my php urls for me so they looked like pretty folders
    • actually zenphoto was kicking out the pretty urls and mod_rewrite was translating them back into /index.php?album=blah format behind the covers
    • IIS doesn’t do that right out of the box but they have a nice free URL Rewrite module you can drop in to do this very same thing (v2.0 currently)
    • you have to restart IIS Manager GUI after you install to see the “URL Rewrite” icon under the “IIS” section of your web site
    • it has a good wizard for the easy stuff which is all I needed to map “photos/(.*)/” to “photos/index.php?album={R:1}”
    • also under conditionals, input: {REQUEST_FILENAME} => “Is Not a File” & “Is Not a Folder” was crucial to allow the real URLs for direct downloading of images and such to continue working
    • Here’ are all the rewrite rules I needed to apply:
web.config
  1. <?xml version="1.0"?>
  2. <configuration>
  3.     <system.webServer>
  4.  
  5.     <rewrite>
  6.             <rules>
  7.                 <clear/>
  8.                 <rule name="RewriteUserFriendlyURL6" enabled="true" stopProcessing="true">
  9.                     <match url="^page/search/archive/(.*)$"/>
  10.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  11.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  12.                         <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
  13.                     </conditions>
  14.                     <action type="Rewrite" url="index.php?p=search&amp;date={R:1}"/>
  15.                 </rule>
  16.                 <rule name="RewriteUserFriendlyURL5" enabled="true" stopProcessing="true">
  17.                     <match url="^page/([0-9]+)/?$"/>
  18.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  19.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  20.                         <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
  21.                     </conditions>
  22.                     <action type="Rewrite" url="index.php?page={R:1}"/>
  23.                 </rule>
  24.                 <rule name="RewriteUserFriendlyURL4" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
  25.                     <match url="^page/(.*?)$"/>
  26.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  27.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  28.                     </conditions>
  29.                     <action type="Rewrite" url="index.php?p={R:1}" appendQueryString="true"/>
  30.                 </rule>
  31.                 <rule name="RewriteUserFriendlyURL1" enabled="true" stopProcessing="true">
  32.                     <match url="^(.*?)/?$"/>
  33.                     <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
  34.                         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
  35.                         <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
  36.                     </conditions>
  37.                     <action type="Rewrite" url="index.php?album={R:1}" appendQueryString="false"/>
  38.                 </rule>
  39.             </rules>
  40.         </rewrite>
  41.         <directoryBrowse enabled="true"/>
  42.     </system.webServer>
  43.  
  44.   <system.web>
  45.         <compilation targetFramework="4.0" debug="true"/>
  46.         <pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
  47.   </system.web>
  48.  
  49. </configuration>

Create Self-Signed Cert IIS7

PHP Setup:

  • Map an IIS virtual directory to your zenphoto root
  • browse to http://{your domain}/{zenphoto virtual dir}/setup.php
  • it’ll probably bark about a couple settings you have to make manually… no biggie hopefully
  • you’ll have to reset "World Wide Web Publishing Service" to refresh any PHP settings it tells you to twiddle
  • I had to leave file/folder permissions as "loose (0777)"… all of the stricter settings blocked zenphoto subfolder permissions
  • MySQL settings:
    • root login & password
    • 127.0.0.1:3306 (localhost did NOT work!?!)
    • database name ("zenphoto")
    • table prefix = blank (i preferred to go with a separate database w/o table prefixes)
    • it’ll create the zenphoto database for you with a simple click once you get a successful login to MySQL server working
    • *GO* 🙂
  • i went ahead and let it delete the "zp-coresetup*.php" files
  • set admin username & password
  • You’re in!

ZenPhoto admin page settings:

  • just unzip zpGallerific folder into the zenphotothemes folder
  • Theme’s tab – activate zpGallerific
  • Options tab
    • general subtab – Time zone = Europe/Berlin
    • gallery subtab
      • title = The Andersons
      • description = {blank} (set Gallerific subtitle next)
      • sorty by = filename – descending (works for me because i name all folders "yyyy-mm-dd {description}")
    • image subtab – Full image protection = Unprotected (as long as you don’t really care who gets access, this yields a MAJOR speed boost for page rendering times)
    • theme subtab
      • Albums per page = 9
      • Color = Blue
      • Tagline = Cassidy, Anne, BJ & Friends
  • Anonymous

    Thats great… but in this little tutorial of yours you don't touch the .cfg and .log file security issue.

    Zenphoto's default for windows is so insecure that literally anyone that visits your site can access all your logs and cfg. That's pretty bad because people can guess passwords much better having this kind of information.

  • @Anonymous[Jan 18, 2012] – IIS won't serve files with anything but well known sanctioned extensions… Apache must behave this way as well… (I'll confirm that these are excluded when I get home tonight).

    The next thing that strikes me is that even with the MySQL password, they would need access to the database server to use that information. In my case, that's behind a router and I'd never forward that port through.

    This does suggest an area where there could be some more documentation about the recommended environment… mentioning ports and NAT'ed routers, firewalls, etc.

    But then, you must forgive me, this blog is primarily intended as a self reference first… unless/until someone chimes in, I'll spend my time on more obvious ROI's 🙂 When someone does, it is a good opportunity to give it a little more time.

    Anyway, taking those other layers of security into account, is there still a concern?
    If so, please briefly describe an access breach scenario you have in mind so it could be checked.

  • Anonymous

    Sorry I wasn't specific. The problem arises with Windows + Apache. Zenphoto's default .htaccess files simply aren't enough for protecting the site while in a NTFS filesystem.

    Even if it isn't exactly ok visitors being able to see MySQLs root password the bigger problem is the log file, which may lead to unauthorized access through educated guessing based on unsuccessful logon info… and that is possible no matter what other layers of security are in place (besides disabling Zenphoto's logs or reconfiguring Apache and/or Windows permissions, of course).

  • @Anoymous – I only dabble in Apache occasionally so you're not going to get any valuable direction out of me… but I wouldn't mind educating myself a bit, and clarifying this topic for other passersby, if you're up for it…

    The default Zenphoto .htaccess installed on my system is only a list of RewriteRule's… so it doesn't seem like it's really involved in securing the Zenphoto site. Briefly reading Apache's .htaccess tutorial one quickly gets the gist that security matters should be administered at the server .htaccess file level anyway, not at a site level .htaccess file. Could you elaborate on "Zenphoto's default .htaccess files simply aren't enough for protecting the site while in a NTFS filesystem." Or provide references which demonstrate the specific concern?

    What is the proposed malicious access path to either config or log files? I'd be really surprised if it's that difficult to configure Apache to not serve certain raw files/folders… that's basic security we should count on in any web server at this point. So i'm still not tracking on what you think the nefarious access path is here. I guess if we simply say Zenphoto may have buffer-overrun bugs which allow someone access to these files through manipulating Zenphoto's own executables… then yes it's a problem, but this common problem must be solved by counting on eliminating those bugs and eventually having a solid product. It's tough to implement a layer of security that is smart enough to know that this executable that would normally be allowed to access files, is somehow not allowed to access files in an errant circumstance, correct?

    I really don't mean to burden you do death here but it seems like you have a particular vulnerability in mind that would be nice to identify more specifically… i.e. is there a key phrase for this vulnerability? you aware of any writeups that tee up what we should be watching out for?

  • Anonymous

    I'm not saying the vulnerability isn't fixable. It just takes effort, and it's not mentioned on your article… so so called "noobs" may setup a broken site without knowing better, that's all.

    Anyway, going fw in my quest for the perfect Photo Gallery server I just tried Piwingo + "Stripped" extension (for theme) and it's looking good. It's as clean as Zenphoto's default template, plus it adapts dynamically for screen size really well and for me it's a killer feature. I guess I'm done with Zenphoto for now.

    Here's a demo if you want to see what it looks like: http://photomathias.eu/photos/piwigo/