Categories
.htaccess Tutorials & Tips

.htaccess: Restricting access to all *.php files except index.php

<Files *.php>
    Order Deny,Allow
    Deny from all
    #Allow from 127.0.0.1
</Files>

<Files index.php>
    Order Allow,Deny
    Allow from all
</Files>

The 2 <Files> directives must be in the exact same order as above.

Categories
.htaccess Tutorials & Tips

Get rid of trailing slash off URL requests with .htaccess

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)\/(\?.*)?$ $1$2 [R=301,L]

Yeah, that’s it. So:

  1. /path/dir/ is redirected to /path/dir
  2. /path/dir/?a=1&b=2 is redirected to /path/dir?a=1&b=2
Categories
.htaccess Tutorials & Tips Free PHP Classes & Library Information Security PHP Tips & Tutorials

PHP Class for Handling .htpasswd and .htgroup (Member Login & User Management)

Apache is a marvelous web server that offers .htpasswd and .htgroup for controlling restricted access to your website. By help of .htaccess, they work as a member login & user management system that is so simple and easy to deploy. You can even define user groups / roles with it.

Basically,

  • .htpasswd defines pairs of username & password that are user accounts.
  • .htgroup defines groups / roles of user accounts that can be access-controlled as a whole.
  • .htaccess then applies .htpasswd and .htgroup to the current directory, and specifies which groups in .htgroup has access to the current directory.

For example, we have

/home/myuser/.htpasswd

user1:{SHA}kGPaD671VNU0OU5lqLiN/h6Q6ac=
user2:{SHA}npMqPEX3kPQTo+x/+ZckHDrIcQI=
user3:{SHA}q1Fh2LTUjjkncp11m0M9WUH5Zrw=

/home/myuser/.htgroup

admin: user2
editor: user1 user3
writer: user3

/home/myuser/public_html/example.com/member/.htaccess

AuthName "Members Area"
AuthType Basic
AuthUserFile /home/myuser/.htpasswd
AuthGroupFile /home/myuser/.htgroup
<Limit GET POST>
require group admin
require group writer
</Limit>

What they do is only let users in the admin and writer group, that is user2 and user3, to access example.com/member. When someone tries to access example.com/member, Apache would prompt him or her for user name and password, and he or she must be either user2 or user3 to access it – they must enter the correct password set out in .htpasswd for user2 or user3.

user1 isn’t allowed to access example.com/member even if the password is correct. You get the idea.

You can place .htaccess anywhere in your website, and it will control access to the directory it’s in by the defined rules (which groups / roles are allowed to access). Just make sure it is pointing to the right .htpasswd and .htgroup by AuthUserFile and AuthGroupFile.

And you can have multiple .htaccess in different directories of your website, using the same .htpasswd and .htgroup.

This is so simple yet so very handy in creating & managing different users and user roles (.htpasswd, .htgroup) and giving them permissions (.htaccess) in accessing different website assets.

PHP Class

Now that you are familiar with the basic authentication and native user management system in Apache, you can use this two simple PHP classes to automate tasks such as user creation, user deletion, adding user to group, and removing user from group.

class Htpasswd

class Htpasswd {
	
	private $file = '';
	private $salt = 'AynlJ2H.74VEfI^BZElc-Vb6G0ezE9a55-Wj';
	
	private function write($pairs = array()) {
		$str = '';
		foreach ($pairs as $username => $password) {
			$str .= "$username:{SHA}$password\n";
		}
		file_put_contents($this -> file, $str);
	}
	
	private function read() {
		$pairs = array();
		$fh = fopen($this -> file, 'r');
		while (!feof($fh)) {
			$pair_str = str_replace("\n", '', fgets($fh));
			$pair_array = explode(':{SHA}', $pair_str);
			if (count($pair_array) == 2) {
				$pairs[$pair_array[0]] = $pair_array[1];
			}
		}
		return $pairs;
	}
	
	private function getHash($clear_password = '') {
		if (!empty($clear_password)) {
			return base64_encode(sha1($clear_password, true));
		} else {
			return false;
		}
	}
	
	public function __construct($file) {
		if (file_exists($file)) {
			$this -> file = $file;
		} else {
			die($file." doesn't exist.");
			return false;
		}
	}

	public function getUsers() {
		return $this -> read();
	}
	
	public function addUser($username = '', $clear_password = '') {
		if (!empty($username) && !empty($clear_password)) {
			$all = $this -> read();
			if (!array_key_exists($username, $all)) {
				$all[$username] = $this -> getHash($clear_password);
				$this -> write($all);
			}
		} else {
			return false;
		}
	}
	
	public function deleteUser($username = '') {
		$all = $this -> read();
		if (array_key_exists($username, $all)) {
			unset($all[$username]);
			$this -> write($all);
		} else {
			return false;
		}
	}
	
	public function doesUserExist($username = '') {
		$all = $this -> read();
		if (array_key_exists($username, $all)) {
			return true;
		} else {
			return false;
		}
	}
	
	public function getClearPassword($username) {
		return strtolower(substr(sha1($username.$this -> salt), 4, 12));
	}
	
}

class Htgroup

class Htgroup {
	
	private $file = '';
	
	private function write($groups = array()) {
		$str = '';
		foreach ($groups as $group => $users) {
			$users_str = '';
			foreach ($users as $user) {
				if (!empty($users_str)) {
					$users_str .= ' ';
				}
				$users_str .= $user;
			}
			$str .= "$group: $users_str\n";
		}
		file_put_contents($this -> file, $str);
	}
	
	private function read() {
		$groups = array();
		$groups_str = file($this -> file, FILE_IGNORE_NEW_LINES);
		foreach ($groups_str as $group_str) {
			if (!empty($group_str)) {
				$group_str_array = explode(': ', $group_str);
				if (count($group_str_array) == 2) {
					$users_array = explode(' ', $group_str_array[1]);
					$groups[$group_str_array[0]] = $users_array;
				}
			}
		}
		return $groups;
	}
	
	public function __construct($file) {
		if (file_exists($file)) {
			$this -> file = $file;
		} else {
			die($file." doesn't exist.");
			return false;
		}
	}

	public function getGroups() {
		return $this -> read();
	}
	
	public function addUserToGroup($username = '', $group = '') {
		if (!empty($username) && !empty($group)) {
			$all = $this -> read();
			if (isset($all[$group])) {
				if (!in_array($username, $all[$group])) {
					$all[$group][] = $username;
				}
			} else {
				$all[$group][] = $username;
			}
			$this -> write($all);
		} else {
			return false;
		}
	}
	
	public function deleteUserFromGroup($username = '', $group = '') {
		$all = $this -> read();
		if (array_key_exists($group, $all)) {
			$user_index = array_search($username, $all[$group]);
			if ($user_index !== false) {
				unset($all[$group][$user_index]);
				if (count($all[$group]) == 0) {
					unset($all[$group]);
				}
				$this -> write($all);
			}
		} else {
			return false;
		}
	}

	public function getGroupsByUser($username = '') {
		$all = $this -> read();
		$user_groups = array();
		foreach ($all as $group => $users) {
			if (in_array($username, $users)) {
				$user_groups[] = $group;
			}
		}
		return $user_groups;
	}

}

Usage

First, you should use your own $salt. Change the value of $salt in the Htpasswd class to something else for your own application.

To instantiate the classes:

$passwdHandler = new Htpasswd('/home/myuser/.htpasswd');
$groupHandler = new Htgroup('/home/myuser/.htgroup');

To create and delete a user:

// Add a user with name 'user1' and password 'I prefer to use passphrase rather than password.' if it doesn't exist in .htpasswd.
$passwdHandler -> addUser('user1', 'I prefer to use passphrase rather than password.');
// Delete the user 'user1' if it exists in .htpasswd.
$passwdHandler -> deleteUser('user1');

To check if a particular user exists in .htpasswd:

// Check if user 'user1' exists in .htpasswd.
if ($passwdHandler -> doesUserExist('user1')) {
	// User 'user1' exists.
}

To get the clear text password for a particular user (Apache stores passwords in .htpasswd as encoded strings):

// Get the clear password for user 'user1'.
echo $passwdHandler -> getClearPassword('user1');

To add a user to a group:

// Add user 'user1' to group 'admin' in .htgroup. Group will be automatically created if it doesn't exist.
$groupHandler -> addUserToGroup('user1', 'admin');

To delete a user from a group (user still exists in .htpasswd, just not associated with the group any more):

// Delete user 'user1' from group 'admin' in .htgroup. Group will be automatically removed if it doesn't contain any users.
$groupHandler -> deleteUserFromGroup('user1', 'admin');

To get a list of groups a particular user is assigned to:

/* Get an array of groups that 'user1' is a member of. */
$user_groups = $groupHandler -> getGroupsByUser('user1');

Conclusion

This ain’t concluded. It’s just an END notice. Feel free to let me know your thoughts and how my classes work for you.

Categories
.htaccess Tutorials & Tips Apache Web Server Tutorials & Tips Content / SEO Tips & Tutorials Web Applications & Online Software

<IfModule></IfModule> in .htaccess

I was debugging about some mod_rewrite errors caused by the .htaccess file on one of my sites and couldn’t solve the problem myself so I opted for a help thread on the SitePoint.com forum. Turned out it’s not the problem of my mod_rewrite rules in .htaccess but some incorrect file permissions that were causing the trouble.

Dklynn was very nice to help me in this regard and offered a gold advice that I thought I should share with you.

You Should NOT Use <IfModule></IfModule>. Why?

He pointed out that I should get rid of the <IfModule> conditional from the .htaccess file. It’s useless and a complete waste of server resources by all means. No excuses.

Considering the fact that all HTTP requests (including those to trivial assets such as .css, .js, .gif, .jpg, etc., thus each web page download would trigger about 10 or even more HTTP requests) to the Apache web server are handled by the rules in your .htaccess file, I think I should get rid of all such <IfModule> conditionals from all .htaccess files on all my sites, or the server would be doing useless check of “If Module xxx Is Enabled” every time a HTTP request is received…..

With a site receiving 1000 page visits per day, your server would have to do 10,000 such useless checks at the cost server resources and performance.

Many famous 3rd party scripts such as WordPress has <IfModule></IfModule> in the .htaccess file by default. This is to prevent potential errors should the module was not installed on the client’s production site / server. You should try and remove it. If the mod_rewrite and SEO friendly URLs are working, you can remove it without any problems.

This is pure gold advice offered by Dklynn. Check out his site about Apache mod_rewrite.

Categories
.htaccess Tutorials & Tips Information Security

Use .htaccess to allow access only from a single HTTP referrer

Sometimes you want the user to access something (a web page or a downloadable file) only by clicking a link on your own website instead of being able to directly access it by typing in the URL address in the browser address bar. This is achievable by a few lines in .htaccess.

RewriteEngine On
RewriteCond %{HTTP_REFERER} !(www.)?example.com/download-page.php
RewriteRule .* - [F]

Write down the above lines in the .htaccess of the directory that you want users to access only by clicking links on http://www.example.com/download-page.php or http://example.com/download-page.php. Direct access to download stuff from the directory or from any other HTTP referrer will fail.

While this may not be bullet proof as referral information can be faked from the client side, it is a simple solution that should suffice in most cases. For example, this can be used to prevent hot linking from other websites that link directly to something on your website, reducing traffic stealing.

Categories
.htaccess Tutorials & Tips Information Security PHP Tips & Tutorials

Turn off and disable magic_quotes_gpc in .htaccess

It’s not only insecure but it inconveniently commands the use of PHP function stripslashes() every time you pull something from the database or when you get something from the client side. While most of the hosts out there are using factory settings of PHP that turn off magic_quotes_gpc by default, there are a few that don’t.

The value of magic_quotes_gpc cannot be set with the ini_set() function after PHP 4.2.3, some hosts enable custom php.ini in your home directory which you can use to set magic_quotes_gpc to 0 (zero) or false. Otherwise, you’d have to resort to .htaccess to set the PHP configuration values for your local directories.

To turn off magic_quotes and magic_quotes_gpc off in .htaccess, simply put these lines in the .htaccess file of your site / directory wherein you want magic_quotes or magic_quotes_gpc disabled:

php_value magic_quotes 0
php_flag magic_quotes off
php_value magic_quotes_gpc 0
php_flag magic_quotes_gpc off
Categories
.htaccess Tutorials & Tips PHP Tips & Tutorials

PHP: Run .CSS files as PHP

Most of time we need to run some other mime types as PHP in Apache web server, such as run PHP code in HTML files, we want to Apache to parse that particular type of file so that PHP code in them are recognized and executed. Though from the external viewers’ point of view, such as Google bot or human visitors, nothing’s different.

To run .css files, the cascading style sheets as PHP or enable the PHP code in .css files so as to harness the power of a programming language in rendering and supplying the stylesheet file, just add a simple line in the .htaccess file of the particular directory where you are hosting those .css files containing PHP code:

AddType application/x-httpd-php .css

That’s all. And in case you want to make the entire web document directory to run .css files as PHP once and for all, better don’t do it because Apache would demand unnecessary extra resources to parse all .css files, containing or not containing the PHP code.

Update: A better approach is to rely on mod_rewrite the Apache URL rewriting module to rewrite any incoming requests for .css files to corresponding .php files.

RewriteEngine On
RewriteRule ^screen\.css$ screen.css.php [NC]
Categories
.htaccess Tutorials & Tips Apache Web Server Tutorials & Tips

Set Expiration or Expiring Time by mod_expires.c on Apache via .htaccess to Reduce Web Page Loading Time

When your site’s ready in design and majority of common media resources won’t change for quite some time, say, half a year, for example, the images and flashes. It makes sense to set the expiration of them much longer than the default to prevent the client browsers downloading them every time a visitor drops by, thus conveniently decreasing the page loading time and increasing user experience.

So how do we do that? On Apache web server, examine, customize and put the following section of .htaccess directives in the document root of your site:

<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault "access plus 3 days"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType text/css "access plus 1 month"
ExpiresByType text/javascript "access plus 1 week"
ExpiresByType application/x-javascript "access plus 1 week"
</IfModule>

In case you don’t know what a .htaccess file is, just create a text file named .htaccess (yes, just a dot with the elongated extension), put the above code in, save it and upload the file to the root directory of your domain.

Of course, that said, you will need mod_expires module explicitly enabled as it’s not a core feature but an extension.

Categories
.htaccess Tutorials & Tips

.htaccess: Deny From All – Prohibit, Forbid or Restrict Directory Access

Restricting directory access might be one of the most frequently used .htaccess techniques out there. As a site grows, there always are some areas that you don’t want visitors to look at such as merchandise warehouse where you store digital products for sale.

You want a programmed server-side script to serve the download after confirming payment instead of risking the users downloading them directly from the directory without paying you.

To deny all requests for the restricted directory or folder, prepare a .htaccess text file in that directory and put the following directive in it:

deny from all

That’s it.

Allow and enable access from certain IP

Say you have a permanent IP and you want to administer the site via /admin and protect the directory from the rest of the world once ‘n’ for all, then you will want the following .htaccess directives:

order deny, allow
deny from all
allow from 12.34.56.78

Wherein 12.34.56.78 is your IP.

Or if you have an IP range for an entire country, you can allow visits to your site from that particular country only with this technique.

Or if you are operating the site from LAN you can allow only LAN IP to access certain directories such as /admin:

order deny, allow
deny from all
allow from 192.168.0
Disallow and deny access from certain IP

You get the idea. To allow all visits except from a few identified spam bots, just reverse the deny and allow order like this:

order allow, deny
# 98.76.54.32 is a bad bot here
deny from 98.76.54.32
allow from all

Another blocking method via robots.txt.

Categories
.htaccess Tutorials & Tips

.htaccess: Directory Listing – Enable Web Directory Browsing & Indexing

One of the best things I love Apache web server is that it instantly enables you to share files and resources via plain web directory index listing without having to spend time making any fancy web pages to serve them.

However, there are times when you need to hide things out. To disable web directory listing, you need just a simple directive in .htaccess of that directory. Insert this line to disable apache files listing for this directory and all directories or folders underneath it:

Options -Indexes

However, you may need to specifically enable file indexing for some directory under the parent directory. Just override the above .htaccess directive by creating another .htaccess in the child directory and write:

Options +Indexes
Enable fancy indexing

Fancy indexing is a bit more sophisticated than plain indexing in that it explicitly presents file types and image icons (icons are small images that boost user experience by conveying the idea very intuitively, such as these flag icons standing for countries) for easy discrimination. You can enable fancy indexing for a directory listing by adding an additional directive:

Options +Indexes
# adding fancy directory indexing
IndexOptions +FancyIndexing

# is the comment symbol for .htaccess directives.

Exclude certain files from indexing

There might be confidential files you want to exclude from the indexing and stop them from being shown to visitors. The directive below helps you achieve this task:

IndexIgnore *.jpg *.gif readme.txt

Very straightforward and self-explaining, just modify it for your own situations.