Project

General

Profile

Docs SSL » History » Revision 54

Revision 53 (carpii, 2014-01-26 17:43) → Revision 54/166 (oschonrock, 2014-02-12 13:22)

h1. Secure HTTP 

 bq. Module: core 

 bq. keywords: lighttpd, ssl 

 {{>toc}} 

 h2. Description 

 lighttpd supports _SSLv2_ (disabled from 1.4.21 and 1.5 and onwards) and _SSLv3_ if it is compiled against _openssl_. 

 h2. How to install SSL 

 To use SSL you must have ssl compiled into lighty. You must first have 
 openssl-devel installed and openssl installed as well. On Fedora or 
 Centos you may use yum to install this by running this command:  

 bq.    yum install openssl* 

 And type _yes_ when it asks for a confirmation of what you would like to install. 

 Once installed please download the lighttpd tarball from http://www.lighttpd.net/download and extract it with 

 bq.    tar zxvf lighttpd-1.4.20.tar.gz 

 Enter into the extracted folder and compile with these configuration flags: :: 

 bq.    --with-openssl --with-openssl-libs=/usr/lib 

 Remember to change _ --with-openssl-libs= _ to the folder where your openssl libraries are installed into. 

 Once compiled, run make and make install. If lighty has successfully compiled SSL the command 

 bq.    lighttpd -v 

 Should display (Keep in mind that this new lighty version now has _(ssl)_ after lightys name) 

 bq.    lighttpd-1.4.11 (ssl) - a light and fast webserver 
   Build-Date: Sep    1 2006 19:09:15 

 Remember if you used the RPM packages to install lighty, the init.d scripts will point to the wrong binary of lighty than the one you just compiled. The location of where you compiled lighty should be displayed near the end of make install. Once the location of the binary is found please edit the /etc/init.d/lighttpd script and change what is defined in the lighttpd="/usr/sbin/lighttpd" to your new lighty location. 

 h2. Configuration 

 table{margin-left: 2em}. 
 |_.option |_. description | 
 | ssl.engine | enable/disable ssl engine | 
 | ssl.pemfile | path to the PEM file for SSL support (Should contain both the private key and the certificate)| 
 | ssl.ca-file | path to the CA file for support of chained certificates | 
 | ssl.dh-file | path to the PEM file for Diffie-Hellman key agreement protocol (lighttpd >= 1.4.29 only)| 
 | ssl.ec-curve | defines the set of elliptic-curve-cryptography domain parameters known as a "named curve" (lighttpd >= 1.4.29 only)| 
 | ssl.use-sslv2 | enable/disable use of SSL version 2 (lighttpd < 1.4.21 only, newer version don't support SSLv2)| 
 | ssl.use-sslv3 | enable/disable use of SSL version 3 (lighttpd >= 1.4.29 only)| 
 | ssl.cipher-list | Configure the allowed SSL ciphers | 
 | ssl.honor-cipher-order | enable/disable honoring the order of ciphers set in ssl.cipher-list (set by default when ssl.cipher-list is set)| 
 | ssl.disable-client-renegotiation | enable/disable mitigation of client triggered re-negotiation (see CVE-2009-3555). Important: This setting can only be set globally!| 
 | ssl.verifyclient.activate | enable/disable client verification | 
 | ssl.verifyclient.enforce | enable/disable enforcing client verification |  
 | ssl.verifyclient.depth | certificate depth for client verification | 
 | ssl.verifyclient.exportcert | enable/disable client certificate export to env:SSL_CLIENT_CERT | 
 | ssl.verifyclient.username | client certificate entity to export as env:REMOTE_USER (eg. SSL_CLIENT_S_DN_emailAddress, SSL_CLIENT_S_DN_UID, etc.) | 

 h3. Details 

 To enable SSL you have to provide a valid certificate and have to enable the SSL engine. To make lighttpd SSL-only, simply put the following in your main config (you probably need to set the server port to 443 as well). 

  ssl.engine = "enable" 
  ssl.pemfile = "/path/to/server.pem" 

 To enable SSL in addition to normal HTTP, put the @ssl.engine@ configuration in a @socket@ conditional block: 

  $SERVER["socket"] == ":443" { 
      ssl.engine                    = "enable" 
      ssl.pemfile                   = "/etc/lighttpd/ssl/www.example.org.pem" 
  } 

 This simply enables SSL on port 443. It seems that lighttpd also automatically starts listening on port 443 this way. Like this, all of the other configuration applies to both HTTP and HTTPS. This means that the same pages will be available on both HTTP and HTTPS. If you want to serve different sites, you can change the document root inside the @socket@ conditional: 


  $SERVER["socket"] == ":443" { 
     server.document-root          = "/www/servers/www.example.org/secure/"  
  } 

 When you are using lighttpd 1.4.19 or later, you can also use the @scheme@ conditional to distinguish between secure and normal requests. Note that you can't use the @scheme@ conditional around @ssl.engine@ above, since lighttpd needs to know on what port to enable SSL. 

  $HTTP["scheme"] == "https" { 
     server.document-root          = "/www/servers/www.example.org/secure/"  
  } 

 If you have a .crt and a .key file, cat them together into a single PEM file (the order isn't strictly important): 

   $ cat host.key host.crt > host.pem 

 @ssl.cipher-list@ is a list of ciphers that you want (or don't want) to use when talking SSL. For a list of ciphers and how to include/exclude them, see sections "CIPHER LIST FORMAT" and "CIPHER STRINGS" on "the man-page for ciphers(1ssl)":http://openssl.org/docs/apps/ciphers.html. 

 h3. Permissions 

 Be careful to keep your .pem file private! Lighttpd reads all pemfiles at startup, before dropping privileges. It is therefore best to make the pem file owned by root and readable by root only: 

   $ chown root:root /etc/lighttpd/ssl/example.org.pem 
   $ chmod 400 /etc/lighttpd/ssl/example.org.pem 

 h3. Chained certificates 

 Some certificate authorities use _chained certificates_. This means that your webserver certificate is not signed by the CA root certificate directly, but by an intermediate certificate (which is in turn signed by the root CA). Since browser typically only ship the root certificates and not all intermediate certificates, there is no way for a browser to verify your chained certificate if you don't supply the browser with the intermediate certificate. 

 Using the @ssl.ca-file@ configuration variable, you can tell lighttpd about these intermediate certificates. You can put multiple certificates in a single file, if needed. For example: 

   ssl.ca-file = "/etc/lighttpd/ssl/ca.crt" 

 h3. Troubleshooting 

 * Please note that certificates's files are opened before the server goes chrooted (if it's have to). So these paths are system wide. 
 * On server side, ssldump is your friend: @ssldump -i your_network_interface_goes_here port 443@ 
 * On client side, openssl will help you: @openssl s_client -connect your_server_name_goes_here:443@ 
 * SSL options not working or ignored? Put the options in the same block where you enabled SSL (ssl.engine = "enable"). 

 h3. SSL passwords 

 * If you set a password on your SSL certificate, then each time lighttpd starts you will be requested to enter it manually.    ie: "Enter PEM pass phrase:". To prevent this, remove the password from your private key file. At present there is no configuration option to enable storage of SSL certificate passwords in the lighttpd config file (but it wouldn't make much sense, since having the password stored on disk is not more secure than having no password at all). 

 h3. Diffie-Hellman and Elliptic-Curve Diffie-Hellman parameters 

 Diffie-Hellman and Elliptic-Curve Diffie-Hellman key agreement protocols will be supported in lighttpd 1.4.29. By default, Diffie-Hellman and Elliptic-Curve Diffie-Hellman key agreement protocols use, respectively, the 1024-bit MODP Group with 160-bit prime order subgroup from "RFC 5114":http://tools.ietf.org/rfc/rfc5114.txt and "prime256v1" (also known as "secp256r1") elliptic curve from "RFC 4492":http://tools.ietf.org/rfc/rfc4492.txt. The Elliptic-Curve Diffie-Hellman key agreement protocol is supported in OpenSSL from 0.9.8f version onwards. For maximum interoperability, OpenSSL only supports the "named curves" from "RFC 4492":http://tools.ietf.org/rfc/rfc4492.txt. 

 Using the @ssl.dh-file@ and @ssl.ec-curve@ configuration variables, you can define your own set of Diffie-Hellman domain parameters. For example: 

  ssl.dh-file = "/etc/lighttpd/ssl/dh2048.pem" 
 ssl.ec-curve = "secp384r1" 

 A set of Diffie-Hellman domain parameters can be generated like this (see "OpenSSL documentation":http://www.openssl.org/docs/apps/dhparam.html for help): 

   $ openssl dhparam -out dh2048.pem -outform PEM -2 2048 

 The set of supported elliptic-curves can be obtained like this (see "OpenSSL documentation":http://www.openssl.org/docs/apps/ecparam.html for help): 

   $ openssl ecparam -list_curves 

 h2. SSL on multiple domains 

 A traditional problem with SSL in combination with name based virtual hosting has been that the SSL connection setup happens before the HTTP request. So at the moment lighttpd needs to send its certificate to the client, it does not know yet which domain the client will be requesting. This means it can only supply the default certificate (and use the corresponding key for encryption) and effectively, SSL can only be enabled for that default domain. There are a number of solutions to this problem, with varying levels of support. 

 h3. One IP address per domain 

 This is the classic solution, which requires a separate IP address for each domain. Since Lighttpd knows the ip address that the client connected to before the SSL negotiation, it can simply select the right certificate to use based on the IP address. The most obvious downside to this approach is that it requires multiple addresses, which are not always available and can be expensive. This approach is currently the most reliable, since it works with all clients. 

 To do this in lighttpd, use the @socket@ conditional: 

     $SERVER["socket"] == "10.0.0.1:443" { 
         ssl.pemfile = "/etc/lighttpd/ssl/www.example.org.pem" 
     } 
    
     $SERVER["socket"] == "10.0.0.2:443" { 
         ssl.pemfile = "/etc/lighttpd/ssl/mail.example.org.pem" 
     } 

 h3. UCC / SAN Certificates 

 It is possible to put multiple names in a single certificate. These certificates are usually referred to as UCC (Unified Communications Certificates) certificates, or SAN (Subject Alternative Name) certificates. These certificates use the SAN property to store multiple domain names in a single certificate. This allows lighttpd to always use the same certificate, which is valid for all the domains it serves. 

 The main disadvantage of this approach is that you will have a single certificate with a lot of domain names in it and the certificate needs to change whenever you add a new domain name. When serving sites for different parties, this can be a problem. 

 "According to digicert":http://www.digicert.com/subject-alternative-name-compatibility.htm, SAN certificates are supported by nearly all browsers (except for some mobile browsers) and should be pretty safe to use. 

 Using a SAN certificate requires no special configuration for Lighttpd. 

 h3. Wildcard certificates 

 Certificates support the use of wildcards, which can be useful to support multiple subdomains. For example, one can get a certificate for the *.example.org domain to support all of the subdomains of the example.org domain. 

 This approach of course does not scale well to multiple different domains. Again "according to digicert":http://www.digicert.com/ssl-support/wildcard-compatibility.htm, wildcard certificates should be supported by virtually all clients. 

 h3. Server Name Indication (SNI) 

 An extension to the SSL / TLS protocol that has gained support recently is "Server Name Indication":http://en.wikipedia.org/wiki/Server_Name_Indication.    This addition is simple and elegant: During the SSL negotiation, the client sends the name of the host it wants to contact to the server, which can then use this information to select the correct certificate. 

 This is of course the best and most fitting solution to the problem.    Lighttpd supports SNI since 1.4.24, if openssl 0.9.8f or newer is available. 

 The big downside of this approach is the "limited browser support":http://en.wikipedia.org/wiki/Server_Name_Indication#Support. The newest versions of the major browsers support SNI, but in particular Internet Explorer on Windows XP seems to lack support and support with mobile browsers is unclear. 

 To use SNI with lighttpd, simply put additional @ssl.pemfile@ configuration directives inside @host@ conditionals. It seems a default @ssl.pemfile@ is still required, though. 

     $HTTP["host"] == "www.example.org" { 
         ssl.pemfile = "/etc/lighttpd/www.example.org.pem" 
     } 
    
     $HTTP["host"] == "mail.example.org" { 
         ssl.pemfile = "/etc/lighttpd/mail.example.org.pem" 
     } 

 h2. Other issues 

 h3. Self-Signed Certificates 

 A self-signed SSL certificate can be generated like this: :: 

   $ openssl req -new -x509 \ 
     -keyout server.pem -out server.pem \ 
     -days 365 -nodes 

 h3. Serve ONLY the domain you want under an SSL IP 

 The simple config suggested above, is perhaps too "vague" when serving multiple hosts, one per IP (the traditional and still most common way, until Windows XP dies and nearly all browsers support SNI). Consider this config: 

     $SERVER["socket"] == "10.0.0.1:443" { 
         ssl.pemfile = "/etc/lighttpd/ssl/domain1.com.pem" 
         server.document-root          = "/www/servers/domain1.com"  
     } 

     $SERVER["socket"] == "10.0.0.2:443" { 
         ssl.pemfile = "/etc/lighttpd/ssl/domain2.com.pem" 
         server.document-root          = "/www/servers/domain1.com"  
     } 

 Your DNS config might be: 

 domain1.com    A 10.0.0.1 
 domain2.com    A 10.0.0.2 

 Like this all is fine normally. However if your client has poisoned or stale DNS, or a /etc/hosts entry and makes the following request: 

 https://domain1.com  

 connecting to IP 10.0.0.2 because it has somehow resolved domain1.com to 10.0.0.2 (see above). Then lighty WILL serve the content of domain2 despite recevieving "Host:domain1.com" in the request header! You are kind of protected because your client should give an SSL domain mismatch warning here. However, as was the case in our config, domain1 and domain2 were actually subdomains and covered by the same wildcard cert => no ssl warning! 

 So we were getting CONTENT FROM "WRONG" DOMAIN/DOCROOT just because of stale DNS. 

 For a "tighter" config we would suggest: 

     $SERVER["socket"] == "10.0.0.1:443" { 
         ssl.pemfile = "/etc/lighttpd/ssl/domain1.com.pem" 
         $HTTP["host"] == "domain1.com" { 
           server.document-root          = "/www/servers/domain1.com"  
         } 
     } 

     $SERVER["socket"] == "10.0.0.2:443" { 
         ssl.pemfile = "/etc/lighttpd/ssl/domain2.com.pem" 
         $HTTP["host"] == "domain2.com" { 
           server.document-root          = "/www/servers/domain2.com"  
         } 
     } 

 This ensures that 10.0.0.2:443 will ONLY EVER serve content with a "Host: domain2.com" request header. If domain1.com is requested from 10.0.0.2:443 lighty will now respond with 404, which is better we think. 

 NOTE: the HTTP["host"] conditional should come after ssl engine config (ssl.enable, pemfile, cyphers...) as these are required to establish the connection, and must come before "host" is available (unless you are relying on SNI). Once the connection is up, we can make all subsequent config (docroot, log, rewrite etc) conditional upon "Host" as shown. Tested with lighttpd 1.4.32. 

 


 h3. PCI DSS compliance 

 Matthew Glubb wrote: 

 I should clarify the reason for this work. From September 19th, all major online vendors taking card payments will be required to comply with the Payment Card Industry (PCI) Data Security Standard. Smaller vendors may self-certify but they will be more liable if fraud is committed. 

 Part of this standard is the disabling of SSLv2 and the removal of support for ciphers that have a key length of less than 128 bits. For this reason, I believe that the default SSL configuration for lighttpd should reflect this standard.  


 Since 1.4.12 you can use: :: 

   ssl.use-sslv2 = "disable"     -     (No longer needed as of 1.4.21 and 1.5 this is disabled by default.) 
   ssl.cipher-list = "..." 

 to disable SSLv2 and set a cipher-list. Make sure this settings are in the same block than ssl.engine = "enable", this will not work as global options. 

 cipher-list accepts a string containing the ciphers you would like to accept separated by whitespace.    A list of strings will not work. 

 Matthew also provide a list of possible ciphers: 

 Hope it can be of use. Next time I'll submit a proper svn patch but I was in a hurry! Which might explain my somewhat short list of supported ciphers. I've since done some research and this list of supported ciphers is much more comprehensive. It supports all ciphers >= 128 bit key lengths for SSL v3.0, TLS v1.0, and AES cipher suites from RFC3268, extending TLS v1.0 (these seem to be the ones used by recent browsers, not included in the original list): :: 

     RC4-SHA 
     RC4-MD5 
     ADH-RC4-MD5 
     EDH-RSA-DES-CBC3-SHA 
     EDH-DSS-DES-CBC3-SHA 
     DES-CBC3-SHA 
     ADH-DES-CBC3-SHA 
     DES-CBC3-MD5 
     AES128-SHA 
     AES256-SHA 
     DH-DSS-AES128-SHA 
     DH-DSS-AES256-SHA 
     DH-RSA-AES128-SHA 
     DH-RSA-AES256-SHA 
     DHE-DSS-AES128-SHA 
     DHE-DSS-AES256-SHA 
     DHE-RSA-AES128-SHA 
     DHE-RSA-AES256-SHA  

 [FYI] bb says: the requirements permit use of SSLv2 as long as it isn't the only version available.    for example, having SSLv2, SSLv3, and TLSv1 enabled is PCI compliant.    SSLv2 should still be disabled, however, both because it is weak and because you can expect the PCI requirements to become more stringent in the future.    here is my recommended cipher list, in order, which also happens to be PCI compliant: :: 

     DHE-RSA-AES256-SHA  
     DHE-RSA-AES128-SHA 
     EDH-RSA-DES-CBC3-SHA 
     AES256-SHA 
     AES128-SHA 
     DES-CBC3-SHA 
     DES-CBC3-MD5 
     RC4-SHA 
     RC4-MD5 


 * Note: you are forgetting the CAMELLIA cipher (Firefox 3 default) which going by it's wikipedia page is roughly equivalent to AES.    For a good list do `openssl ciphers HIGH` 

 h3. Important Update: 

 As of September 2013, It is no longer recommended to use RC4 ciphers, and various others such as 3DES should be disabled where possible. 

 See the release notes for Lighttpd 1.4.34 for further info. http://www.lighttpd.net/2014/1/20/1-4-34/ 


 h3. PCI DSS compliance 

 How to enable that the server requests a SSL client certificate? 


 h3. HTTPS detection in PHP 

 Some PHP scripts try to detect HTTPS by checking if $_SERVER['HTTPS'] equals 'on'. To allow that, you can try this: :: 

     server.modules = ( 
         "mod_setenv", 
     ) 
     $SERVER["socket"] == "0.0.0.0:443" { 
         ssl.engine               = "enable" 
         ssl.pemfile              = "/etc/lighttpd/server.pem" 
         ssl.use-sslv2            = "disable" 
         setenv.add-environment = ( 
             "HTTPS" => "on" 
         ) 
     } 

 h2. Links : 

 * [[HowToSimpleSSL]] 
 * [[HowToRedirectHttpToHttps|Redirect HTTP requests to HTTPS]]