All posts by Brian

Executable blocking on Windows 2003

I was recently attempting to install PHP using an installer (MSI) I downloaded to my local workstation and copied to one of our servers. When I attempted to run the installer I received the following inscrutable warning:

Windows cannot access the specific device, path, or file. You may not have the appropriate permissions to access the item.

Thanks, Windows, that’s helpful. I had no idea what was happening, especially since I was logged in as an administrator with full control of the file.

As usual, the web was my friend in solving the problem. Windows 2003 Server has a security feature where executables copied from remote systems are blocked for execution unless allowed by an administrator. The feature appears to have been present for a while (since SP2?), but I don’t recall running into it before. As mentioned above, the file in my case was copied from my workstation over SMB; I don’t know if other transfer methods are also affected. I don’t have a problem with the feature, but I do have a problem with how it’s implemented. An indication in the pop-up of why the file could not be accessed would have resulted in a resolution of seconds not … er … minutes.

The resolution is simple enough, go into the properties for the file and you’ll see at the bottom a security warning and a button that allows you to unblock the file.

Click the “Unblock” button and you can execute the file as you normally would.

References:

Careful with Google Analytics filters

I made a change to the filters attached to our profiles in Google Analytics (GA) so that we would capture only relevant traffic. Namely, I was attempting to avoid capturing traffic to our development servers. Unfortunately, instead of a domain-based filter I created one that resulted in GA capturing zero traffic. Fail.

The problem stems from my attempt to use a predefined filter to block out the unwanted traffic. I tried to set up a “include only traffic from the domains that include” filter, thinking this was a filter on the server’s domain. What this filter actually checks against is the visitor’s domain. I guess a little more attention to the “from/to” part of the filter would have made this apparent, but Google doesn’t provide much documentation (or even better, examples) on their filters.

After a few days I noticed the sudden lack of data and realized my error. Turns out what I really wanted was a custom filter with the following settings:

  • filter type: include
  • fiter field: hostname
  • filter pattern: regex for the domain (e.g. www.project2061.org
  • case sensitive: no

Even after looking at this again, it’s still not clear to me why these two are different. The descriptive language is almost identical. Google needs to do some work here.

Of course, there are also other solutions to this problem, listed below:

  • Use an include to pull in the GA code and only populate the file on the servers where tracking should occur. The only significant problem here would be forgetting to set up the include.
  • Create advanced segments in GA that filters out traffic to anything other than the production domains. This would have the benefit of being able to track both production and development server use in the same report. But if there are any significant difference between the two sites then this wouldn’t be all that helpful.

References:

suhosin to [internal web app]: you talk too much

Following up on my earlier post, I’ve had to make some further configuration adjustments to avoid suhosin-related restrictions in one of our custom web applications. This particular application has a function that generates a summary of data from student assessments. The summaries can be generated based on groupings of packets and items. Depending on the filtering parameters selected there can be a fairly large number of packets and items. Not all of the packets necessarily contain the items of interest, but it’s always easier to select all if you want an overall summary of item performance.

I recently noticed the following alert in the system log:

ALERT – configured POST variable limit exceeded – dropped variable ‘included_packet_ids[]’ (attacker USER_IP_ADDRESS, file REPORT_FILTERING_PAGE)

One of the reasons I use POST variables on this page is to avoid the relatively small data size limit of GET. Suhosin adds additional limits, including in the number of times you can reference an individual variable.  Our limit was set at 1000, meaning there were over 1000 packets selected. This points to a need to adjust how the filter “selects all” … but for now I’ve adjusted the suhosin limit upward by modifying the suhosin.post.max_vars setting.

References:

suhosin to WordPress: go on a diet

We were seeing a lot of suhosin alerts in the system messages log of the type:

ALERT – script tried to increase memory_limit to 268435456 bytes which is above the allowed value (attacker SERVER_IP_ADDRESS, file WP_MAIN_ADMIN_PAGE, line 96)

The source of the issue is WordPress. The application is trying to raise the memory limit and suhosin won’t let it. Apparently WordPress will try to set a 256MB memory limit before executing certain functions. The necessity of adjusting this setting seems questionable to me, but I also understand that it’s often better to play it safe when developing software for public consumption.

I don’t particularly like applications attempting to specify their own resource usage in a web environment. In my mind applications should specify a required/recommended memory limit in the system requirements and stay away from adjusting this setting behind-the-scenes. Tell me during setup if the current setting may result in non-optimal performance or even a halt in script execution. That’s not how it’s done here, but really no harm is done in the long run beyond the annoyance of suhosin throwing errors at the system logs.

There are two easy fixes to the problem:

  1. Set the PHP memory limit to 256MB
  2. Modify the suhosin.memory_limit parameter to 256MB

In our particular situation it’s just as easy to set the PHP memory limit. There’s always a risk of overloading the physical resources, but this site receives little enough traffic that I’m not concerned about the right confluence of request occurring to cause a crash.

References:

ALERT – script tried to increase memory_limit to 268435456 bytes which is above the allowed value

Keeping the OpenSUSE messages log tidy

Our linux server, an OpenSUSE 11 box, uses syslog-ng to capture log messages. By default syslog-ng is set up to report statistics every hour. Our servers, however, aren’t intended to be used as logging servers so the message handling statistics aren’t very useful. Plus, the statistics messages end up flooding the system log, making it difficult to parse the log for more relevant messages. To rectify the situation I disabled the stats messages by editing /etc/syslog-ng/syslog-ng.conf so that the global options read:

options { long_hostnames(off); sync(0); perm(0640); stats(0); };

The relevant option is stats(). The numerical value is based on seconds and so the default is 3600 (or once an hour). By setting the option to 0 no statistical message should be logged.

AbleCommerce Tax Zone Modification

One of the features of AbleCommerce is the ability to charge taxes based on address. The feature is nice, but has one major flaw … the tax rate has to managed manually. We actually have only a few states where we are required to charge taxes, but one of those is California which is horribly convoluted. The problem boils down to, essentially, the need to track the tax rate for each zip code individually. You can’t even really use blocks of zip codes because a locality’s zip codes may not be consecutive.

AbleCommerce uses the concept of zones for assigning tax rates. For our purposes a zone can be applied based on state or a combination of state and a list of zip codes. Because of California’s tax rules and AbleCommerce’s tax rule management functionality we have to create a separate zone for each tax rate. The zip code list is just a comma-separated string of zip codes. Maintaining the information for the >1000 zip codes is difficult to do in this format.

I created a simple PHP-based page that keeps track of each zip code and its associated tax rate. While not perfect, this goes a long way towards simplifying maintenance of this information. A link to this page can be located in the AbleCommerce administrative section under Configure->Regions->Zones.

AbleCommerce has one other issue that has to be addressed. The default AbleCommerce installation limits a zone’s zip code filter to 255 characters (or about 40 zip codes) in the database and on the web form. Due to the situation with California taxes we have some zones that have significantly more than 40 zip codes and would require the creation of over 70 zones. To get around this problem I removed the web form limit and modified the database field ac_shipZones@PostalCodeFilter to be varchar(8000).

Rewriting URLs on IIS6

One of the nice things about CakePHP is that it attempts to make a site more friendly to the average web site visitor by creating easier-to-remember URLs. There are a few techniques that CakePHP uses, but for the sake of this conversation we’ll focus on one way in particular: apache’s mod_rewrite functionality. Using mod_rewrite, URLs that would normally include the controller file (index.php) and a querystring can be rewritten as a simple file path.

Microsoft doesn’t include mod_rewrite-style functionality in IIS by default, though it has created an extension for IIS7. Since we are currently using IIS6 the extension isn’t an option for us. Fortunately there are a few other options available, including an open-source project called Ionics Isapi Rewrite Filter (IIRF). IIRF is an ISAPI filter that is very similar to mod_rewrite in terms of functionality. After some testing I’ve found this filter works almost perfectly for enabling CakePHP’s friendly URLs.

Getting Started

Installation is painless and requires only a few steps. The following is based on the IIRF 2.0.1.15 release which does not include an installer:

  1. Extract the files from the IIRF archive to a folder on the server.
  2. Open the properties of the IIS root “Web Sites” folder or the specific site that needs IIRF.
  3. In the “ISAPI Filters” tab create a new entry for IIRF; name the new filter (e.g. “IIRF”); specify the IIRF DLL located with the extracted IIRF files at binIIRF.dll.
  4. Ensure that the IIS user has read/execute access to the IIRF DLL and read/write access to any directories that will be used for logging.
  5. Restart IIRF.

IIRF should now be installed, but I’ve found that sometimes the filter won’t appear to be active in the “ISAPI Filters” tab until it has been used the first time. To enable IIRF all you have to do is create a file called IIRF.ini and place it in the web site root folder or in the root of a virtual directory. This file contains any local configuration directives (such as log directory) and URL rewriting rules.

The obligatory gotcha

I mentioned before that the filter works almost perfectly. The only situation where I’ve had problems up to now is if the URL contains one or more space characters. The IIRF log indicates that a URL with a space is being parsed correctly and returning a valid URL. And yet the web server reports a 404 error.

I’ve only done testing when the final URL references a physical file, but based on a conversation in the support forum I suspect this problem also affects parameters in the querystring. I haven’t yet had a chance to fully investigate the issue, so I don’t have a work-around yet when the issue affects physical files. There does, however, appear to be a work-around for spaces in the querystring.

For now I have decided to ensure that any URLs parsed by IIRF that will point to a physical file/directory does not contain spaces.

References

CakePHP usage:

Space-in-URL problems:

Moving from Microsoft Access to MySQL

We recently decided to make a public release of an old web-based application coded in ASP (classic, using VBScript) and using Microsoft Access. In order to make this application public we need to make a few modifications, not the least of which is moving from Microsoft Access to MySQL. Using Microsoft Access on the back-end would significantly hamper the ability of the application to support concurrent users, among other issues.

The majority of the coding modifications have yet to be made, but the database switch has already occurred. In the process of moving from MS Access to MySQL I discovered a few settings that would be helpful should this action need to be performed for other applications. These settings should enable similar applications to be moved with minimal modification to the programming.

First, let’s review some settings related to the MySQL ODBC driver. The settings are relevant to all versions of the driver, but the name of the setting may be different on different versions (I’m using 5.1.6). Here are the options which should be selected:

  • Return matched rows instead of affected rows
  • Treat BIGINT columns as INT columns
  • Enable safe options

The following information relates more generally to changes that may have to be made in the code:

  • MySQL doesn’t really support server-side cursors so the ODBC drivers fakes it. This is, mostly, fine except that some properties of the Recordset object are not available (namely RecordCount). In order to get full cursor support you should change the location from the server to the client (adUseClient or the literal value 3).
  • ASP doesn’t understand non-signed integers. This causes problems when performing operations using these values unless you manually type the value in your script, e.g. scriptvar = CInt(objrs("dbcol")). The other solution is to make all integers signed. Otherwise you will see the error: Variable uses an Automation type not supported in VBScript.
  • Related to the above is the usage of values from the database in comparison functions. VBScript variables are typed (e.g. integer, string, boolean, etc.). Though you can’t specify the type during variable instantiation (with Dim) VBScript does pay attention to type when performing comparisons. When two variables of different types are compared you will get a “Type mismatch” error. The resolution is to Ctype your variables if you run into this type of error.
  • Finally, check your SQL statements for any VBA function calls. These will either have to be modified into MySQL-compatible function calls or removed from the SQL code altogether.

There are a number of issues that may be encountered when attempting to convert an ASP-based application from MS Access to MySQL. The issues addressed here are only those relevant to this particular application. Other applications may require additional or different solutions and settings.

Resources:

Subversion and Third-Party Code

Often in the course of developing a project it is desirable to use code from a  third party. The main benefit, of course, is being able to add functionality without developing that functionality from scratch. Some third-party projects we have used in the past include FCKEditor (now CKEditor), the Yahoo! User Interface library, and, more recently, the CakePHP framework.

When our code is maintained in a subversion repository there are two options for including third-party code: externals and vendor branches. Continue reading Subversion and Third-Party Code