Lighttpd Advanced security: Difference between revisions

From Alpine Linux
m (→‎Access Control List: negative comparison explained)
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
For higher security [[Lighttpd]] can be configured to allow https access.
== Access Control List ==


==Generate Certificate and Keys==
This is a way you can define a directory and only allow clients coming from the specified ip's to have access. This might be nice to allow internal LAN clients access to the status pages or employee contact information and deny other clients.
Either generate the public key and certificate and private key using {{Pkg|openssl}}, or by using the ones generated by installing [[Alpine_Configuration_Framework_Design| ACF]]. You don't need to do both, just do one or the other.  The former method, with OpenSSL, is preferred since it gives greater control.


===Generate self-signed certificates with openssl ===
<pre>
To generate certificates, openssl is needed.
#### access control list for hidden_dir (not for use behind proxies) CAUTION REMOVE "#" to work
$HTTP["remoteip"] !~ "127.0.0.1|10.10.10.2|20.10.20.30" {
  $HTTP["url"] =~ "^/hidden_dir/" {
    url.access-deny = ( "" )
  }
}
</pre>


{{Cmd|apk add openssl}}
* <code>remoteip</code> will be compared again single ip only, in that example again 3 ip's. The <nowiki>!~</nowiki> are only used for those ip's that ar allowed, the rest will be denied (negative comparison).
* <code>hidden_dir</code> are the name of the relative path under your root htdocs webserver place, or absolute path where are the files that are served.


Change to the lighttpd configuration directory
<pre>
$HTTP["url"] =~ "^/hidden_dir/" {
    $HTTP["remoteip"] == "33.222.0.0/16" {
    }
    else $HTTP["remoteip"] == "75.209.116.4" {
    }
    else $HTTP["remoteip"] == "79.31.34.79" {
    }
    else $HTTP["remoteip"] != "" {  # (dummy match everything)
        url.access-deny = ( "" )
    }
}
</pre>


{{Cmd|cd /etc/lighttpd}}
* <nowiki>33.222.0.0/16</nowiki> in this case, we first match the denied entry to, then check the ip that access to that entry and for each network range at the last will be the acces deny rule.


With the command below the self-signed certificate and key pair are generated. A 2048 bit key is the minimum recommended at the time of writing, so we use '-newkey rsa:2048' in the command.  Change to suit your needs. Answer all questions.
===== See also =====


{{Cmd|openssl req -newkey rsa:2048 -x509 -keyout server.pem -out server.pem -days 365 -nodes}}
* https://redmine.lighttpd.net/boards/2/topics/1279
* https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModAccess


Adjust the permissions
== stop image hijacking ==


{{Cmd|chmod 400 /etc/lighttpd/server.pem}}
Image hijacking is when someone makes a link to your site to one of your pictures or videos, but displays it on their site as their own content. The reason this is done is to send a browser to your server to use your bandwidth and make the content look like part of the hijacker's site. This is most common as people make links to pictures and add them to a public forum or blog listing. They get to use your picture in their content and not have to use their bandwidth or server to host the file. In order to keep your bandwidth usage low you should block access to images from those clients who are not requesting the connection from your site. Note, this function can be used for any kind on content. Just add the file types to the url.access-deny list. If would like more ideas on lowering bandwidth usage check out our Saving Webserver Bandwidth (Tips).


=== Generate self-signed certificates with acf ===
<pre>
#### stop image hijacking (anti-hotlinking) CAUTION REMOVE "#" to work
# $HTTP["referer"] !~ "^(http://midominio\.org|http://www\.midominio\.org)" {
#    url.access-deny = ( ".jpg", ".jpeg", ".png", ".avi", ".mov" )
# }
</pre>


Install the [[Alpine_Configuration_Framework_Design| ACF]]
== virtual host limits ==


{{Cmd|setup-acf}}
A virtual host is the hostname of your web server. For example this site is called calomel.org. Some bots and scanners will try to access your site using the ip address or no hostname header at all to bypass virtual host limitations. We can block this type of behavior by requiring all clients who want to access our server to reference us by our official host name. This will block anyone who is scanning ip addresses or trying to be mischievous, but allow normal clients like browsers and bots like Google.


Copy the generated certificate to the lighttpd configuration directory.
<pre>
#### virtual host limits CAUTION REMOVE "#" to work
# $HTTP["host"] !~ "^(midominio\.org|www\.midominio\.org)" {
#    url.access-deny = ( "" )
#  }
</pre>


{{Cmd|mv /etc/ssl/mini_httpd/server.pem /etc/lighttpd/server.pem}}
== stop referer spam == 


Adjust the permissions
Referer spam is more of an annoyance than a problem. A web site will connect to your server with the referer field referencing their web site. The idea is that if you publish your web logs or statistics then their hostname will show up on your page. When a search bot like Google comes by it will see the link from your site to theirs and give them more PageRank credit. First, never make your weblogs public. Second, block access to referer spammers with these lines.


{{Cmd|chown root:root /etc/lighttpd/server.pem}}
<pre>
{{Cmd|chmod 400 /etc/lighttpd/server.pem}}
#### stop referer spam CAUTION REMOVE "#" to work
# $HTTP["referer"] =~ "(tarotathome|casinospam)" {
#    url.access-deny = ( "" )
}
</pre>


mini_http is no longer needed.


{{Cmd|/etc/init.d/mini_httpd stop && rc-update del mini_httpd}}
== Perfect Forward Secrecy (PFS) ==


Removing the mini_http package
[https://en.wikipedia.org/wiki/Perfect_forward_secrecy Perfect Forward Secrecy] isn't perfect, but what it does mean is that an adversary who gains the private key of a server does not have the ability to decrypt every encrypted SSL/TLS session.  Without it, an adversary can simply obtain the private key of a server and decrypt and and all SSL/TLS sessions using that key.  This is a major security and privacy concern and so using PFS is probably a good idea long term. It means that every session would have to be decrypted individually, regardless of the state (whether obtained by the adversary or otherwise).


{{Cmd|apk del mini_httpd}}
Ultimately when choosing SSL/TLS ciphers it is the usual chose of security or usability?  Increasing one usually decreases the other.  Nonetheless, an example to prevent the BEAST attack and offer PFS is:


==Configure Lighttpd==
The configuration of lighttpd needs to be modified.
{{Cmd|nano /etc/lighttpd/lighttpd.conf}}
Uncomment this section and adjust the path so 'ssl.pemfile' points to where our cert/key pair is stored. Or copy the example below into your configuration file if you saved it to /etc/lighttpd/server.pem.
<pre>
<pre>
ssl.engine    = "enable"
ssl.cipher-list = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"
ssl.pemfile  = "/etc/lighttpd/server.pem"
</pre>
</pre>


You'll also want to set the server to listen on port 443. Replace this:
== Mitigation of well know-ed attacks ==
server.port = 80
with this:
server.port = 443


Restart lighttpd
=== BEAST attack, CVE-2011-3389 ===
 
{{Cmd|rc-service lighttpd restart}}
 
== Security ==


=== BEAST attack, CVE-2011-3389 ===
To help mitigate the BEAST attack add the following to your configuration:
To help mitigate the BEAST attack add the following to your configuration:


<code>
  #### Mitigate BEAST attack:
  #### Mitigate BEAST attack:
   
   
Line 88: Line 106:
  ssl.disable-client-renegotiation = "enable"
  ssl.disable-client-renegotiation = "enable"
  #
  #
 
</code>
 
=== Perfect Forward Secrecy (PFS) ===
 
[https://en.wikipedia.org/wiki/Perfect_forward_secrecy Perfect Forward Secrecy] isn't perfect, but what it does mean is that an adversary who gains the private key of a server does not have the ability to decrypt every encrypted SSL/TLS session.  Without it, an adversary can simply obtain the private key of a server and decrypt and and all SSL/TLS sessions using that key.  This is a major security and privacy concern and so using PFS is probably a good idea long term. It means that every session would have to be decrypted individually, regardless of the state (whether obtained by the adversary or otherwise).
 
Ultimately when choosing SSL/TLS ciphers it is the usual chose of security or usability?  Increasing one usually decreases the other.  Nonetheless, an example to prevent the BEAST attack and offer PFS is:
 
<pre>
ssl.cipher-list = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"
</pre>


=== POODLE attack (CVE-2014-3566) ===
=== POODLE attack (CVE-2014-3566) ===
Line 107: Line 115:
:!SSLv3
:!SSLv3
</pre>
</pre>


=== FREAK attack (CVE-2015-0204) ===
=== FREAK attack (CVE-2015-0204) ===
Line 127: Line 134:
Also see https://freakattack.com/
Also see https://freakattack.com/


== Other configurations ==
== Https access ==
The following are example configs, they will likely need to be modified to suite your particular setupNonetheless they should provide an indication of how to implement the relevant configuration options.
 
For higher security [[Lighttpd]] can be configured to allow https access.
 
The configuration of lighttpd needs to be modified.
 
{{Cmd|nano /etc/lighttpd/lighttpd.conf}}
 
Uncomment this section and adjust the path so 'ssl.pemfile' points to where our cert/key pair is stored. Or copy the example below into your configuration file if you saved it to /etc/lighttpd/server.pem.
<pre>
ssl.engine    = "enable"
ssl.pemfile  = "/etc/lighttpd/server.pem"
</pre>
 
You'll also want to set the server to listen on port 443. Replace this:
<pre>server.port = 80</pre>
with this:
<pre>server.port = 443</pre>
 
Restart lighttpd
 
{{Cmd|rc-service lighttpd restart}}
 
===Generate Certificate and Keys===
Either generate the public key and certificate and private key using {{Pkg|openssl}}, or by using the ones generated by installing [[Alpine_Configuration_Framework_Design| ACF]].  You don't need to do both, just do one or the otherThe former method, with OpenSSL, is preferred since it gives greater control.
 
====Generate self-signed certificates with openssl ====
 
To generate certificates, openssl is needed.
 
{{Cmd|apk add openssl}}
 
Change to the lighttpd configuration directory
 
{{Cmd|cd /etc/lighttpd}}
 
With the command below the self-signed certificate and key pair are generated. A 2048 bit key is the minimum recommended at the time of writing, so we use '-newkey rsa:2048' in the command.  Change to suit your needs. Answer all questions.
 
{{Cmd|openssl req -newkey rsa:2048 -x509 -keyout server.pem -out server.pem -days 365 -nodes}}
 
Adjust the permissions
 
{{Cmd|chmod 400 /etc/lighttpd/server.pem}}
 
==== Generate self-signed certificates with acf ====
 
Install the [[Alpine_Configuration_Framework_Design| ACF]]
 
{{Cmd|setup-acf}}
 
Copy the generated certificate to the lighttpd configuration directory.
 
{{Cmd|mv /etc/ssl/mini_httpd/server.pem /etc/lighttpd/server.pem}}
 
Adjust the permissions
 
{{Cmd|chown root:root /etc/lighttpd/server.pem}}
{{Cmd|chmod 400 /etc/lighttpd/server.pem}}
 
mini_http is no longer needed.
 
{{Cmd|/etc/init.d/mini_httpd stop && rc-update del mini_httpd}}
 
Removing the mini_http package
 
{{Cmd|apk del mini_httpd}}


=== redirecting HTTP to HTTPS ===
=== redirecting HTTP to HTTPS ===
Any requests to the server via HTTP (TCP port 80 by default) will be redirected to HTTPS (port 443):
Any requests to the server via HTTP (TCP port 80 by default) will be redirected to HTTPS (port 443):


<pre>
<pre>
server.port = 80
server.username = "lighttpd"
server.groupname = "lighttpd"
server.document-root = "/var/www/localhost/htdocs"
server.errorlog = "/var/log/lighttpd/error.log"
dir-listing.activate = "enable"
index-file.names = ( "index.html" )
mimetype.assign = ( ".html" => "text/html", ".txt" => "text/plain", ".jpg" => "image/jpeg", ".png" => "image/png", "" => "application/octet-stream" )
## Ensure mod_redirect is enabled!
## Ensure mod_redirect is enabled!
server.modules              = (
server.modules              = (
Line 153: Line 216:
   }
   }
}
}
$SERVER["socket"] == ":443" {
    ssl.engine                  = "enable"
    ssl.pemfile                = "/etc/lighttpd/certs/www.example.com.pem"
## Make sure the line above points to your SSL cert/key pair!
}
</pre>
=== Serving both HTTP and HTTPS requests ===
Simple, just add in the SSL server port, enable the SSL engine and point to the relevant SSL cert/key pair:
<pre>
server.port = 80
server.username = "lighttpd"
server.groupname = "lighttpd"
server.document-root = "/var/www/localhost/htdocs"
server.errorlog = "/var/log/lighttpd/error.log"
dir-listing.activate = "enable"
index-file.names = ( "index.html" )
mimetype.assign = ( ".html" => "text/html", ".txt" => "text/plain", ".jpg" => "image/jpeg", ".png" => "image/png", "" => "application/octet-stream" )
## Below is HTTPS setup. Make sure to point at relevant cert/key pair for HTTPS to work!
$SERVER["socket"] == ":443" {
    ssl.engine                  = "enable"
    ssl.pemfile                = "/etc/lighttpd/certs/www.example.com.pem"
}
</pre>
</pre>


== More details ==


== More details ==
* [http://redmine.lighttpd.net/wiki/1/Docs:SSL Lighttpd documentation]
* [http://redmine.lighttpd.net/wiki/1/Docs:SSL Lighttpd documentation]


[[Category:Server]]
[[Category:Server]]
[[Category:Security]]
[[Category:Security]]

Revision as of 13:47, 28 August 2019

Access Control List

This is a way you can define a directory and only allow clients coming from the specified ip's to have access. This might be nice to allow internal LAN clients access to the status pages or employee contact information and deny other clients.

 #### access control list for hidden_dir (not for use behind proxies) CAUTION REMOVE "#" to work
$HTTP["remoteip"] !~ "127.0.0.1|10.10.10.2|20.10.20.30" {
   $HTTP["url"] =~ "^/hidden_dir/" {
     url.access-deny = ( "" )
   }
}
  • remoteip will be compared again single ip only, in that example again 3 ip's. The !~ are only used for those ip's that ar allowed, the rest will be denied (negative comparison).
  • hidden_dir are the name of the relative path under your root htdocs webserver place, or absolute path where are the files that are served.
$HTTP["url"] =~ "^/hidden_dir/" {
    $HTTP["remoteip"] == "33.222.0.0/16" {
    }
    else $HTTP["remoteip"] == "75.209.116.4" {
    }
    else $HTTP["remoteip"] == "79.31.34.79" {
    }
    else $HTTP["remoteip"] != "" {  # (dummy match everything)
        url.access-deny = ( "" )
    }
}
  • 33.222.0.0/16 in this case, we first match the denied entry to, then check the ip that access to that entry and for each network range at the last will be the acces deny rule.
See also

stop image hijacking

Image hijacking is when someone makes a link to your site to one of your pictures or videos, but displays it on their site as their own content. The reason this is done is to send a browser to your server to use your bandwidth and make the content look like part of the hijacker's site. This is most common as people make links to pictures and add them to a public forum or blog listing. They get to use your picture in their content and not have to use their bandwidth or server to host the file. In order to keep your bandwidth usage low you should block access to images from those clients who are not requesting the connection from your site. Note, this function can be used for any kind on content. Just add the file types to the url.access-deny list. If would like more ideas on lowering bandwidth usage check out our Saving Webserver Bandwidth (Tips).

 #### stop image hijacking (anti-hotlinking) CAUTION REMOVE "#" to work
 # $HTTP["referer"] !~ "^(http://midominio\.org|http://www\.midominio\.org)" {
 #     url.access-deny = ( ".jpg", ".jpeg", ".png", ".avi", ".mov" )
 # }

virtual host limits

A virtual host is the hostname of your web server. For example this site is called calomel.org. Some bots and scanners will try to access your site using the ip address or no hostname header at all to bypass virtual host limitations. We can block this type of behavior by requiring all clients who want to access our server to reference us by our official host name. This will block anyone who is scanning ip addresses or trying to be mischievous, but allow normal clients like browsers and bots like Google.

 #### virtual host limits CAUTION REMOVE "#" to work
 # $HTTP["host"] !~ "^(midominio\.org|www\.midominio\.org)" {
 #     url.access-deny = ( "" )
 #  }

stop referer spam

Referer spam is more of an annoyance than a problem. A web site will connect to your server with the referer field referencing their web site. The idea is that if you publish your web logs or statistics then their hostname will show up on your page. When a search bot like Google comes by it will see the link from your site to theirs and give them more PageRank credit. First, never make your weblogs public. Second, block access to referer spammers with these lines.

 #### stop referer spam CAUTION REMOVE "#" to work
 # $HTTP["referer"] =~ "(tarotathome|casinospam)" {
 #     url.access-deny = ( "" )
 #  }


Perfect Forward Secrecy (PFS)

Perfect Forward Secrecy isn't perfect, but what it does mean is that an adversary who gains the private key of a server does not have the ability to decrypt every encrypted SSL/TLS session. Without it, an adversary can simply obtain the private key of a server and decrypt and and all SSL/TLS sessions using that key. This is a major security and privacy concern and so using PFS is probably a good idea long term. It means that every session would have to be decrypted individually, regardless of the state (whether obtained by the adversary or otherwise).

Ultimately when choosing SSL/TLS ciphers it is the usual chose of security or usability? Increasing one usually decreases the other. Nonetheless, an example to prevent the BEAST attack and offer PFS is:

ssl.cipher-list = "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"

Mitigation of well know-ed attacks

BEAST attack, CVE-2011-3389

To help mitigate the BEAST attack add the following to your configuration:

#### Mitigate BEAST attack:

# A stricter base cipher suite. For details see:
# http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2011-3389
# or
# http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3389

ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4-SHA:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM"
#
# Make the server prefer the order of the server side cipher suite instead of the client suite.
# This is necessary to mitigate the BEAST attack (unless you disable all non RC4 algorithms).
# This option is enabled by default, but only used if ssl.cipher-list is set.
ssl.honor-cipher-order = "enable"

# Mitigate CVE-2009-3555 by disabling client triggered renegotiation
# This option is enabled by default.
#
ssl.disable-client-renegotiation = "enable"
#

POODLE attack (CVE-2014-3566)

In light of the recent POODLE findings, it's advisable to wherever possible turn off support for SSLv3. This is quite simple, you can just append the following to your cipher list to explicitly disable SSLv3 support:

:!SSLv3

FREAK attack (CVE-2015-0204)

To prevent the so called FREAK attack, keep your SSL library up to date, and do not offer support for export grade ciphers.

There's multiple ways to do this, like turning off export cipher support in the cipher list:

:!EXPORT

Although now might be a good time to review the cipher list in use, and use a stronger, explicit set like the one from the Perfect Forward Secrecy section, or another example:

ssl.cipher-list = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"

Also see https://freakattack.com/

Https access

For higher security Lighttpd can be configured to allow https access.

The configuration of lighttpd needs to be modified.

nano /etc/lighttpd/lighttpd.conf

Uncomment this section and adjust the path so 'ssl.pemfile' points to where our cert/key pair is stored. Or copy the example below into your configuration file if you saved it to /etc/lighttpd/server.pem.

ssl.engine    = "enable"
ssl.pemfile   = "/etc/lighttpd/server.pem"

You'll also want to set the server to listen on port 443. Replace this:

server.port		= 80

with this:

server.port		= 443

Restart lighttpd

rc-service lighttpd restart

Generate Certificate and Keys

Either generate the public key and certificate and private key using openssl, or by using the ones generated by installing ACF. You don't need to do both, just do one or the other. The former method, with OpenSSL, is preferred since it gives greater control.

Generate self-signed certificates with openssl

To generate certificates, openssl is needed.

apk add openssl

Change to the lighttpd configuration directory

cd /etc/lighttpd

With the command below the self-signed certificate and key pair are generated. A 2048 bit key is the minimum recommended at the time of writing, so we use '-newkey rsa:2048' in the command. Change to suit your needs. Answer all questions.

openssl req -newkey rsa:2048 -x509 -keyout server.pem -out server.pem -days 365 -nodes

Adjust the permissions

chmod 400 /etc/lighttpd/server.pem

Generate self-signed certificates with acf

Install the ACF

setup-acf

Copy the generated certificate to the lighttpd configuration directory.

mv /etc/ssl/mini_httpd/server.pem /etc/lighttpd/server.pem

Adjust the permissions

chown root:root /etc/lighttpd/server.pem

chmod 400 /etc/lighttpd/server.pem

mini_http is no longer needed.

/etc/init.d/mini_httpd stop && rc-update del mini_httpd

Removing the mini_http package

apk del mini_httpd

redirecting HTTP to HTTPS

Any requests to the server via HTTP (TCP port 80 by default) will be redirected to HTTPS (port 443):

## Ensure mod_redirect is enabled!
server.modules              = (
                                "mod_redirect",                                    
)

$SERVER["socket"] == ":80" {
  $HTTP["host"] =~ "(.*)" {
    url.redirect = ( "^/(.*)" => "https://%1/$1" )
  }
}

More details