Apache config and .htaccess - security

From Helpful
Jump to: navigation, search
Related to web development, hosting, and such: (See also the webdev category)
jQuery: Introduction, some basics, examples · plugin notes · unsorted

Server stuff:

Dynamic server stuff:

These are primarily notes
It won't be complete in any sense.
It exists to contain fragments of useful information.

HTTPS (HTTP over TLS/SSL)

See also SSL and HTTPS notes


Note that [mod_ssl] is not the same as Apache-SSL. The two are alternatives, and some configuration is different.


Some background

  • HTTPS is generic HTTP over generic SSL/TLS
  • SSL/TLS needs to handshake, and needs to fetch a certificate, and this happens before HTTP enters the picture
  • that certificate is sent by the Apache SSL plugin. When you serve more than one HTTPS-enabled website you may have a chicken-and-egg problem, in that apache must know which certificate to send, but since HTTP has not entered the picture yet, it can't rely on the HTTP Host header. Your options:
    • serve only one HTTPS website
    • set things up so that you have one certificate be valid for all HTTPS-served websites
    • specify a certificate per IP address+port combo
    • Use SNI (Server Name Indication), an extension of SSL/TLS, functionally very similar to the Host: header in HTTP requests. SNI is essentially required for when you have distict named virtual hosts that use HTTPS and share IP+port
      • MSIE on XP didn't support SNI. These days you can assume it works.


More pragmatically, you will generally want both:

Listen 80
NameVirtualHost *:80

and (probably under an IfModule condition referring to mod_gnutls.c or mod_ssl.c):

Listen 443
NameVirtualHost *:443


You'll then typically want a VirtualHost entry for both these ports, quite possibly with identical contents (except for SSLEngine, SSLCertificateFile, SSLCertificateKeyFile), to support both secure and regular HTTP. (it's typically most flexible to tell your app to redirect back and forth as needed -- though arguably it's easier for the HTTP site to just redirect you to HTTPS)

Assuming you want to run a SSL host on the SSL port, you may want something vaguely like:

<VirtualHost 192.168.0.1:80>
  ServerName www.example.com
  DocumentRoot /var/www/
</VirtualHost>

<VirtualHost 192.168.0.1:443>
  ServerName www.example.com
  DocumentRoot /var/www/

  SSLEngine on
  SSLCertificateFile      /path/to/relevant/servercrt.pem
  SSLCertificateChainFile /path/to/relevant/serverchain.pem
  SSLCertificateKeyFile   /path/to/relevant/serverkey.pem
</VirtualHost>

(Note: Apache will also need to Listen 443 or it won't have bound to the port that a browser looks for when it sees https://)

Notes:

  • You may want to put SSLRequireSSL in your <Directory> sections. This means the SSL module forbids plain HTTP access that can happen if you misconfigured SSL details.


On redirects between HTTP and HTTPS

AllowOverride

AllowOverride controls which directives will (not) be picked up from .htaccess files.


It knows about five categories, that Apache directives are sorted into:

  • FileInfo: document and handler related things, including SetHandler, ErrorDocument, DefaultType, input and output filters, mod_mime directives.
  • Options: refers mainly to the Options directive.
  • AuthConfig: user authentication directives, including Require, AuthName, AuthType, and AuthUserFile.
  • Limit: host limiting directives, particularly Allow, Deny, and Order.
  • Indexes: Apache-generated directory index details like DirectoryIndex, FancyIndexing, ReadmeName, AddIconByEncoding, AddIconByType, DefaultIcon, IndexIgnore, and IndexOptions.
  • You can also specify All and None


From an administrative view, it is cleaner to disable all overrides server-wide, then allow things that users argue are necessary.

If you trust your users enough, the admin can be relieved of continual tweaking for users if they are allowed to to do most things via .htaccess. You can try to balance these.

See also When (not) to use .htaccess files.


Disabling everything is overly paranoid, in that AuthConfig and Limit are not hugely abusable (users can only misconfigure their own access control). (And users may assume that their password-protected directories they protected elsewhere and copied to your server will be safe here too)


The vaguely-abusable options are FileInfo (handlers, filters) and Options (symlinks). Consider that the (now-common) scripting can do many of these things anyway, and you probably have it installed (think PHP). (There may be some difference in as what user they can do it, though.)

Options

Controls features per directory.

If used in .htaccess, it has to be allowed via AllowOverride.


See also:

Access restriction

TODO: clarify 2.2-versus-2.4-style in all of the below


Host/net restrictions

You might be tempted to think of these as ACLs (order matters, terminate once matched) but that would lead to wrong behaviour.


Instead, apache uses what the last applicable rule said, running though a set of allow and a set of deny rules. If any rule applies, the allowed-or-not is updated. It will run though one or the order set first, depending on the Order specified.

Usually, either allow or deny is 'all', meaning that you are effectively using order to control the default policy - whitelisting or blacklisting. (you can also do a conditionless allow or deny by haing one of the lists be empty)


Whitelisting takes the form of "deny everything, then allow specific cases":

Order deny,allow
deny from all
#allow only these two university networks
allow from 130.89.
allow from 129.125.

(Accidentally using Order allow,deny would deny everything)


Blacklisting is "allow everything, but deny specific cases".

Order allow,deny
allow from all
#block these two university networks:
deny from 130.89.
deny from 129.125.

(Accidentally using Order deny,allow would allow everthing)


Per method

You can do per-{HTTPmethod}x{net/host} combinations.

This is probably only useful when you know exactly why you need to block only a few methods instead of all access. Examples where it makes sense include WebDAV and Subversion access, which use methods like PUT, DELETE, OPTIONS, PROPFIND, COPY, MOVE, LOCK, UNLOCK and a few others.


In the following example, I want to block access to all hosts except some home and university computers. More exactly put:

  • Allow GET, POST and HEAD for hosts I work from, deny for all others
  • Deny all other methods from everywhere (optional paranoia against possible abuse of other methods)
<Limit GET POST HEAD>             #Actually redundant; GET implies HEAD
   order deny,allow
   allow from 129.125.                  #subnet, IP:   rug.nl network
   allow from .scarfboy.com             #domain, name: could include my server and workstation
   allow from workstation.scarfboy.com  #host, name:   doesn't exist, but you get the idea
   allow from 3.2.1.2                   #host, IP:    (also doesn't exist)
   deny from all                      #...and deny everyone else
 </Limit>
 <LimitExcept GET POST HEAD>   
   Order deny,allow           # Since there are no allow rules
   Deny from all              #   this means 'block all'
 </LimitExcept>

LimitExcept is a relatively little used addition that limits everything except mentioned methods. It can be usefully combined with Limit to have rules cover all HTTP methods without being able to accidentally forget one.

It allows you to be more explicit about all the cases under consideration, rather than leave them up to defaults and implications.

Downtime notice trick

One simple trick to do a site-wide "this site is being upgraded" notice is to do the following inside a (virtual) host:

# deny from everyone, except from yourself (whatever IP/net)
order deny,allow
deny from all
allow from 192.0.2.199
 
# custom error document with a useful message
ErrorDocument 403 /updating.html
 
<Files updating.html>
 allow from all
</Files>

The allowed nets/hosts get the site as usual. Everyone else gets the contents of /updating.html, on every URL. (...because it causes a 403 Forbidden error, which with the ErrorDocument causes a redirect to that /updating.html page)


The above is for bother and security. If you care about crawler reaction you probably want to use 503 Service unavailable instead, because it's treated as temporary by most.

It requires more work, because you can't just piggyback on basic authorization as the above did.


Authentication

You can make apache do HTTP authentication, supported by most any browser. The simplest method is the built-in Apache authentication handling:

AuthType      Basic                  
AuthName      "Seeekrit area"

AuthType sets the type, and the fact authentication needs to happen. AuthName is displayed by the browser when it asks for the login.


AuthTypes

This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me)
Using
AuthType Basic
or
Aut`hType Digest
causes apache to add its own auth handler to requests.

They are two rather different methods of handling password verification. Digest is more secure than Basic but not supported by quite everything. Even so, Digest should be your default choice unless you have specific reason to use Basic.


In both cases you'll also want a Require, for example:

Require valid-user

or

Require user specificusername


Basic

Basic authentication means the client sends the password itself (Base64'd) in every request that the password applies to (by host+realm(verify)).

This makes not only a plain-text setup, but also sent unnecessarily much (even a login token would be a vast improvement). It's easy to sniff the password from traffic, particularly in these WiFi days. This also means you should never use a good password on Basic auth.

Apache's Basic auth handler requires we specify:

AuthType Basic
AuthUserFile  /path/to/.htpasswd
AuthGroupFile /dev/null              # in this case, no user-group map is used


Digest

Digest authentication is an alternative to Basic that uses a nonce and sends a hash, which means the password itself is never sent but also that cryptanalysis (of sniffed traffic) is non-trivial. (Web servers store a hash of the password -- or should, anyway. If someone stole that file, they can still do offline brute-forcing based on the stored hashes, though.)

It is intended to be a halfway decent alternative to the insecure Basic auth. All major browsers have supported it for a while. Not all minimal clients and libraries do, though.

Apache's digest auth handler requires:

AuthType       Digest
AuthDigestFile /path/to/.htdigest

In apache 2.2 it was decided that the directive wasn't necessary and you should use the following (you'll see a "Invalid command 'AuthDigestFile'" if you don't)

AuthType     Digest
AuthUserFile /path/to/.htdigest

The realm name is part of Digest authentication (which matters when adding entries, and in that changing the realm name means you need to set new entries).


If you get the log error Access to /path failed: verification of user id '<null>' not configured, this probably means the digest module isn't loaded (on linux you usually need to look to conf.d to add the define that httpd.conf uses as the condition to load it).

.htpasswd / .htdigest

The .htpasswd files (storing the hashes) can be called anything, but is conventionally called .htpasswd or similar, partly because default apache static file serving is configured to not serve files with names that start with .ht.


The .htpasswd/.htdigest files can sit anywhere as on your filesystem, as long as they are readable by the user apache runs as. You could keep a central file for all virtual hosts (say, in /var/www), keep one per host, per directory, or whatnot. (You probably ought to keep it out of any DocumentRoots, though apache's own file static serving is by default configured not to ever serve it.)


If you use one central file, note:

  • for basic auth you probably want to use Require user and/or Require group, otherwise all users you have are allowed into all protected areas


Using the htpasswd and htdigest utilities

htpasswd is used to create and alter the password file used Basic AuthType:

htpasswd htpasswdfile username

The first time you need to use -c, meaning 'create the file I'm mentioning'.

The executable may be called
htpasswd2
when you use Apache 2. It does the same thing, the numbering just made it easier to install apache 1.3 and 2.0 side by side.



htdigest is much like htpasswd, except that you create a hash per usernames-realm combination, so:

htdigest [-c] passwdfile realm username

You'll often want to use quotes around the realm to avoid spaces being bothersome, e.g.:

htdigest .htdigest 'Remote access' username


Authorization

Requirements

apache 2.2

The above deals with authenication, and we still need to do authorisation.


Which means you require something. Options include:

Require valid-user               # any user listed in the htpasswd file
Require user  user1 user2 user3  # one of these specific users listed here
Require group group1 group2      # anyone in one of these groups

Group files consist of lines like:

mygroup: bob joe anne


Note: by the time you want groups, you may want a feature like hooking into a (remote) login server or into system accounts. You could start managing the user and group files, but it is often more convenient to hook into some established login system using apache modules like mod_pam, mod_radius or mod_auth_ldap. Note, however, that this may open such a backend to be more easily brute-forced/DoS, so be careful.


You can selectively apply this to specific HTTP methods by putting the details into <Limit> sections. It's generally easier to have it blanket unless until you are exposing an API of sorts.


apache 2.4
This article/section is a stub — probably a pile of half-sorted notes, is not well-checked so may have incorrect bits. (Feel free to ignore, fix, or tell me)

In apache 2.4, the above is deprecated in favour of mod_authz_host (if you don't want to convert just yet, look at mod_access_compat)


Differences from 2.2:

  • The Order Allow, Deny stuff is gone (verify)
  • Multiple requirements (of distinct types, you can e.g. provide various nets to ip) should be nested in either RequireAll or RequireAny (AND and OR, basically)
When you don't use this but do use multiple Require statements, they work as if they were nested in RequireAny (OR behaviour). You may want to nest them just to make it more intuitive.
  • (Replaces the earlier Satisfy any / Satisfy All stuff)


  • The Require directive has more types.
In 2.2 you mainly had valid-user, user, group, env
In 2.4 you have local, ip, host, http-method, expr
you can also negate things
Sometimes RequireNone is handier.


Satisfy: host and/or user

When you combine host-level (Allow/Deny) and user-level (Require) restrictions, the default behaviour is:

Satisfy all

...which means that both need to be satisfied - authentication and coming from the specified network.


If, instead, you want behaviour like passwordless from home, required from anywhere else, then you can use:

Satisfy any      # the default is all

...in combination with require and an Order deny,allow type setup.

See also