Blocking Site Visitors by User Agent

One of my sites was receiving a lot of hits with a user agent of Mozilla/4.0 (compatible; ICS), within a very short timeframe (requests within the same second), from a huge variety of IP addresses.

A quick look around showed ‘compatible; ICS’ is probably not a person or search engine.

I checked several IP addresses that used that user agent at: Project HoneyPot, and most of them were listed as potential sources of dictionary attacks and spam senders.

A common reaction to this is to block the requests by user agent, and that’s what I did, using:

RewriteCond %{HTTP_USER_AGENT} (compatible;\ ICS)
RewriteRule ^ - [F]

These must go before any other RewriteRules.

To test the site and ensure it still runs properly, I used Bots vs. Browsers, which allows you to request pages using a specific user agent string. Be sure to check the page with user agents that include the string you want to block, and those which should be allowed.

eZ publish - Creative Commons Licensed Image - Class

Using Creative Commons licensed images allows you to include some beautiful photos and illustrations on your site.

It’s really important to add the appropriate credits when you’re using the images, to comply with the license, but more importantly, to give credit to the creator. To properly credit the work, review this.

To make it easier to credit the images in eZ publish, I created a class and supporting templates. The content class package export (the link above) includes the class definition. The templates I used are below:

design/site/override/templates/full/cc_image.tpl

{* CC_Image - Full view *}
{let sort_order=$node.parent.sort_array[0][1]
     sort_column=$node.parent.sort_array[0][0]
     sort_column_value=cond( $sort_column|eq( 'published' ), $node.object.published,
                             $sort_column|eq( 'modified' ), $node.object.modified,
                             $sort_column|eq( 'name' ), $node.object.name,
                             $sort_column|eq( 'priority' ), $node.priority,
                             $sort_column|eq( 'modified_subnode' ), $node.modified_subnode,
                             false() )
     previous_image=fetch_alias( subtree, hash( parent_node_id, $node.parent_node_id,
                                                class_filter_type, include,
                                                class_filter_array, array( 'cc_image' ),
                                                limit, 1,
                                                attribute_filter, array( and, array( $sort_column, $sort_order|choose( '>', '<' ), $sort_column_value ) ),
                                                sort_by, array( array( $sort_column, $sort_order|not ), array( 'node_id', $sort_order|not ) ) ) )
     next_image=fetch_alias( subtree, hash( parent_node_id, $node.parent_node_id,
                                            class_filter_type, include,
                                            class_filter_array, array( 'cc_image' ),
                                            limit, 1,
                                            attribute_filter, array( and, array( $sort_column, $sort_order|choose( '<', '>' ), $sort_column_value ) ),
                                            sort_by, array( array( $sort_column, $sort_order ), array( 'node_id', $sort_order ) ) ) )}

<div class="content-view-full">
    <div class="class-image">

        <h1>{$node.name|wash}</h1>

        {if is_unset( $versionview_mode )}
        <div class="content-navigator">
            {if $previous_image}
                <div class="content-navigator-previous">
                    <div class="content-navigator-arrow">&laquo;&nbsp;</div><a href={$previous_image[0].url_alias|ezurl} title="{$previous_image[0].name|wash}">{'Previous image'|i18n( 'design/base' )}</a>
                </div>
            {else}
                <div class="content-navigator-previous-disabled">
                    <div class="content-navigator-arrow">&laquo;&nbsp;</div>{'Previous image'|i18n( 'design/base' )}
                </div>
            {/if}

            {if $previous_image}
                <div class="content-navigator-separator">|</div>
            {else}
                <div class="content-navigator-separator-disabled">|</div>
            {/if}

            {let forum=$node.parent}
                <div class="content-navigator-forum-link"><a href={$forum.url_alias|ezurl}>{$forum.name|wash}</a></div>
            {/let}

            {if $next_image}
                <div class="content-navigator-separator">|</div>
            {else}
                <div class="content-navigator-separator-disabled">|</div>
            {/if}

            {if $next_image}
                <div class="content-navigator-next">
                    <a href={$next_image[0].url_alias|ezurl} title="{$next_image[0].name|wash}">{'Next image'|i18n( 'design/base' )}</a><div class="content-navigator-arrow">&nbsp;&raquo;</div>
                </div>
            {else}
                <div class="content-navigator-next-disabled">
                    {'Next image'|i18n( 'design/base' )}<div class="content-navigator-arrow">&nbsp;&raquo;</div>
                </div>
            {/if}
        </div>
        {/if}

        <div class="attribute-image">
            <p>{attribute_view_gui attribute=$node.data_map.image image_class=large}</p>
        </div>

        <div class="attribute-caption">
            {attribute_view_gui attribute=$node.data_map.caption}
        </div>

        <div class="attribute-attribution">
            {'Image credit'|i18n( 'design/base' )}&nbsp;&raquo;&nbsp;{attribute_view_gui attribute=$node.data_map.attribution}
        </div>

        <div class="attribute-creative-commons-license">
                {'Creative Commons License'|i18n( 'design/base' )}:&nbsp;{attribute_view_gui attribute=$node.data_map.creative_commons_license}
        </div>

    </div>
</div>
{/let}

design/site/override/templates/line/cc_image.tpl

{* CC_Image - Line view *}

<div class="content-view-line">
    <div class="class-image">

    <div class="content-image">
        <p style="float:left;width:125px">{attribute_view_gui attribute=$node.data_map.image image_class=small href=$node.url_alias|ezurl()}&nbsp;</p><h2>{$node.name}</h2>
        {attribute_view_gui attribute=$node.data_map.caption}
    </div>

    </div>
</div>

design/site/override/templates/galleryslide/cc_image.tpl

{* CC_Image - Gallery slide view *}

<div class="content-view-galleryslide">
    <div class="class-image">

    <h1>{$parent_name|wash()}: {$node.name|wash()}</h1>

    <div class="attribute-image">
        <p>{attribute_view_gui attribute=$node.data_map.image image_class=large}</p>
    </div>

    <div class="attribute-caption">
        {attribute_view_gui attribute=$node.data_map.caption}
    </div>

    </div>
</div>


design/site/override/templates/embed/cc_image.tpl

<div class="content-view-embeddedmedia">
<div class="class-image">

<div class="attribute-image">
<p>
{if or( is_set( $link_parameters.href ),$object.data_map.attribution.has_content )}
    {attribute_view_gui attribute=$object.data_map.image image_class=first_set( $object_parameters.size,ezini( 'ImageSettings', 'DefaultEmbedAlias', 'content.ini' ), '' ) href=first_set( $link_parameters.href,$object.main_node.url )|ezurl target=$link_parameters.target link_class=first_set( $link_parameters.class, '' ) link_id=first_set( $link_parameters['xhtml:id'], '' ) link_title=first_set( $link_parameters['xhtml:title'], $object.data_map.attribution.content.data_text, '' )}
{else}
  {if is_set($object_parameters.size)}
    {attribute_view_gui attribute=$object.data_map.image image_class=$object_parameters.size}
  {else}
    {attribute_view_gui attribute=$object.data_map.image image_class=ezini( 'ImageSettings', 'DefaultEmbedAlias', 'content.ini' )}
  {/if}
{/if}
</p>
</div>

{if $object.data_map.caption.has_content}
{if is_set($object.data_map.image.content[$object_parameters.size].width)}
<div class="attribute-caption" style="width: {$object.data_map.image.content[$object_parameters.size].width}px">
{else}
<div class="attribute-caption">
{/if}
    {attribute_view_gui attribute=$object.data_map.caption}
</div>
{/if}
</div>
</div>

Template override settings. I used the default image class for some cases.

settings/siteaccess/site/override.ini.append.php


[cc_image_full]
Source=node/view/full.tpl
MatchFile=full/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_galleryslide]
Source=node/view/galleryslide.tpl
MatchFile=galleryslide/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed]
Source=content/view/embed.tpl
MatchFile=embed/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed-inline]
Source=content/view/embed-inline.tpl
MatchFile=embed-inline/image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed_node]
Source=node/view/embed.tpl
MatchFile=embed/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_embed-inline_node]
Source=node/view/embed-inline.tpl
MatchFile=embed-inline/image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_line]
Source=node/view/line.tpl
MatchFile=line/cc_image.tpl
Subdir=templates
Match[class_identifier]=cc_image

[cc_image_galleryline]
Source=node/view/galleryline.tpl
MatchFile=galleryline/image.tpl
Subdir=templates
Match[class_identifier]=cc_image

I tested this with a custom site design, but it was based on one of the eZ site styles, and very few of the templates were modified. It should integrate well with most designs, but customization is ‘eZ’ enough. :)

I used the standard, default eZ publish image class as the base for the templates.

Login Access Limits

After reviewing the log files for this blog, I noticed many attempts to log into it, and send bogus contact form data.

This is my blog, registration and comments are disabled. To all those who would post helpful comments and legitimate information, I’m sorry.

I access the blog administration from a very limited set of IP addresses, so, instead of wasting my time blocking access from IPs that shouldn’t be logging in, I decided to block all accesses to the administration interface, except my IP address.

This is done using server configuration directives. Refer to the appropriate documentation on blocking access.

After making the changes, be sure to test the effect. The link above is for a nice proxy service that will allow you to visit your pages with a different IP address. The pages should display fine for all navigation through the blog, except things like logging in, and perhaps the contact form. Check anything that’s important to you.

This works if you have a site, blog, or system where the authorized users are from a limited set of IP addresses. It can’t be used to protect against ‘bots and spammers on a forum or contact form. In those cases, I recommend BotScout.

For all those who have been trying to login, please go away.

Great New Web Resource

CoderZone.org launched recently.

It’s great new resource for web people, from ‘n00bs’ to ‘w00ts’. What makes it special:

  • A great team of moderators. These guys are experienced and know the web.
  • A library of code snippets, little bits of code that will save you a tremendous amount of time. You can contribute code, too.
  • XHTML/HTML & CSS sandboxes so you can test out ideas quickly.
  • An SQL sandbox for testing queries.
  • It’s free.
  • A very cool design.
  • No ads, the forum is there to help people, not distract you with ads you aren’t going to click on anyway.

bash ImageMagick Image Slicer

This is a bash script which accepts an image file, determines the width and height, and creates five vertical segments. It can be used to create a ‘puzzle’, tiles, CAPTCHA sequences and other interesting visual effects.

#!/bin/bash
if [ "$#" -le 2 ]; then
       echo "usage: $0 <base directory> <image file> <segments> [<imagemagick directory>]"
else
BASE_DIR="$1"
ORIGINAL_IMAGE="$2"
SEGMENTS="$3"
if [ "$#" -ge 4 ]; then
        IMAGEMAGICK_DIR="$4"
else
        IMAGEMAGICK_DIR=''
fi
RESIZED_IMAGE="image.jpg"
IMAGE_WIDTH=300
"$IMAGEMAGICK_DIR"convert "$BASE_DIR$ORIGINAL_IMAGE" -resize "$IMAGE_WIDTH"x +repage "$BASE_DIR$RESIZED_IMAGE"
IMAGE_DIMENSIONS=`"$IMAGEMAGICK_DIR"identify "$BASE_DIR$RESIZED_IMAGE" | cut -f 3 -d ' '`
SEGMENT_WIDTH=$((`echo "$IMAGE_DIMENSIONS" | cut -f 1 -d 'x'`/$SEGMENTS))
SEGMENT_HEIGHT=`echo "$IMAGE_DIMENSIONS" | cut -f 2 -d 'x'`
echo '<?php'
echo define\(\'SEGMENTS\',"$SEGMENTS"\)\;
echo define\(\'IMAGE_WIDTH\',"$IMAGE_WIDTH"\)\;
echo define\(\'IMAGE_HEIGHT\',"$SEGMENT_HEIGHT"\)\;
echo define\(\'SEGMENT_WIDTH\',"$SEGMENT_WIDTH"\)\;
echo define\(\'SEGMENT_HEIGHT\',"$SEGMENT_HEIGHT"\)\;
for A in `seq 0 $(($SEGMENTS-1))`
do
       SEGMENT_OFFSET=$(($SEGMENT_WIDTH * $A))
       "$IMAGEMAGICK_DIR"convert "$BASE_DIR$RESIZED_IMAGE" -crop "$SEGMENT_WIDTH"x"$SEGMENT_HEIGHT+$SEGMENT_OFFSET+0" "$BASE_DIR"segments/"$A".jpg
done
"$IMAGEMAGICK_DIR"convert "$BASE_DIR$RESIZED_IMAGE" -colorspace gray -level 0,80%,4.0 "$BASE_DIR"target.jpg
fi

The last convert command creates a a faded version of the image.

Further customization could include segmentation on a different axis, different image file formats, etc.