teaching machines

CS 347: Forcing HTTPS

September 16, 2021 by . Filed under fall-2021, specifications, webdev.

When your browser sends an HTTP request from your local computer to a remote server and the server sends back a response, malicious users on the network could capture or sniff the messages. The headers and bodies of HTTP messages are readable. This is bad. Your messages might contain trade secrets, personal information that could lead to identity theft, or the details of a coup that you are plotting to overthrow a corrupt government.

These days, HTTP messages should always be encrypted using the secure HTTP (or HTTPS) protocol. Even the federal government of the United States of America recommends exclusive use of HTTPS.

When you visit a website served over HTTPS, the browser first communicates with the server to fetch its certificate. A certificate provides two important pieces of information. First, it states that the server is recognized as a legitimate representative of the domain. Second, it provides a public key. The browser sends back a message encrypted by this public key to the server. Only the server can decrypt it, using its private key. Through this initial encrypted message, the server and client agree on another temporary encryption key known only to themselves. Both parties use this key to encrypt messages before sending them and decrypt messages after they’ve been received. Malicious sniffers will be unable to read any of the messages they intercept in the middle of the network.

You are administering a server this semester, and you therefore have the responsibility of encrypting your responses and forcing clients to encrypt their requests. Follow these steps to get your sites served over HTTPS:

  1. Install Certbot on your droplet with this command:
    sudo apt install certbot
    
    Certbot is a utility from Let’s Encrypt, an organization that grants free certificates.
  2. Before Let’s Encrypt will grant you a certificate, it will require you to prove that you have control over the domain whose certificate you are requesting. There are various ways to offer this proof, one of which is to add DNS records. Visit Namecheap in your browser, manage your domain, open the Advanced DNS tab, and find the Add New Record button. Leave the page open.
  3. Back in your terminal, request a certificate that will apply to your domain and all subdomains with this command:
    sudo certbot certonly \
      --manual \
      --preferred-challenges=dns \
      -d YOUR-DOMAIN-NAME \
      -d *.YOUR-DOMAIN-NAME
    
    You will be asked to add a TXT record with a prompt like this:
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Please deploy a DNS TXT record under the name
    _acme-challenge.YOUR-DOMAIN-NAME with the following value:
    
    MOPRS-xQq_yES6SZOsy6wM45wFrBOfslOdRxng5eDp8
    
    Before continuing, verify the record is deployed.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Back in Namecheap, add a new TXT record. Enter _acme-challenge for the host, and the nonsense string for the value. Use your nonsense string, not the one above. Save the record and pause for half a minute before continuing back in the terminal. You may be asked to add additional TXT records. Do not delete the first one as you add the second.
  4. If the request succeeds, the certificate will be stored in /etc/letsencrypt/live. If the request fails, delete the TXT records on Namecheap and try running step 3 again.
  5. Apache must be configured a bit before it can handle encryption. Enable its secure sockets layer (SSL) plugin with this command:
    sudo a2enmod ssl
    
  6. Run sudo YOUR-EDITOR /etc/letsencrypt/options-ssl-apache.conf and add this content:
    SSLEngine on
    
    # Intermediate configuration, tweak to your needs
    SSLProtocol             all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
    SSLCipherSuite          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    SSLHonorCipherOrder     off
    
    SSLOptions +StrictRequire
    
    # Add vhost name to log entries:
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" vhost_combined
    LogFormat "%v %h %l %u %t \"%r\" %>s %b" vhost_common
    
    This file is normally generated by Certbot, but not when adding a certificate manually. Boo.
  7. Modify your Apache configuration. You want any HTTP traffic on port 80 to get redirected to HTTPS on port 443. Additionally, you want the virtual host listening on port 443 to load in the certificate:
    # Redirect all HTTP traffic to HTTPS.
    <VirtualHost *:80>
      ServerName project1.YOUR-DOMAIN-NAME
      Redirect / https://project1.YOUR-DOMAIN-NAME/
    </VirtualHost>
    
    # Secure HTTP typically runs on port 443.
    <VirtualHost *:443>
      ServerAdmin webmaster@localhost
      ServerName project1.YOUR-DOMAIN-NAME
      DocumentRoot /home/YOUR-DROPLET-USERNAME/YOUR-PROJECT1-DIRECTORY
    
      # Allow access to all files.
      <Directory /home/YOUR-DROPLET-USERNAME/YOUR-PROJECT1-DIRECTORY>
        Require all granted
      </Directory>
    
      ErrorLog ${APACHE_LOG_DIR}/error.log
      CustomLog ${APACHE_LOG_DIR}/access.log combined
      SSLCertificateFile /etc/letsencrypt/live/YOUR-DOMAIN-NAME/fullchain.pem
      SSLCertificateKeyFile /etc/letsencrypt/live/YOUR-DOMAIN-NAME/privkey.pem
      Include /etc/letsencrypt/options-ssl-apache.conf
    </VirtualHost>
    
    Replace YOUR-DROPLET-USERNAME, YOUR-DOMAIN-NAME, and YOUR-PROJECT1-DIRECTORY with their actual values.
  8. Restart Apache with this command:
    sudo service apache2 restart
    

When you visit http://project1.YOUR-DOMAIN-NAME, the browser should automically redirect to https://project1.YOUR-DOMAIN-NAME.