One place for hosting & domains

      security

      How To Harden the Security of Your Production Django Project


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Developing a Django application can be a quick and clean experience, because its approach is flexible and scalable. Django also offers a variety of security-oriented settings that can help you seamlessly prepare your project for production. However, when it comes to production deployment, there are several ways to further secure your project. Restructuring your project by breaking up your settings will allow you to easily set up different configurations based on the environment. Leveraging dotenv for hiding environment variables or confidential settings will ensure you don’t release any details about your project that may compromise it.

      While implementing these different strategies and features might seem time-consuming at first, developing a practical workflow will allow you to deploy releases of your project without compromising on security, or your productivity.

      In this tutorial, you will leverage a security-oriented workflow for your Django development by implementing and configuring environment-based settings, dotenv, and Django’s built-in security settings. These features all complement each other and will result in a version of your Django project that is ready for different approaches you may take to deployment.

      Prerequisites

      Before you begin this guide you’ll need the following:

      Note: If you’re using an existing Django project, you may have different requirements. This tutorial suggests a particular project structure, however, you can also use each of the sections of this tutorial individually as needed.

      Step 1 — Restructuring Django’s Settings

      In this first step, you’ll start by rearranging your settings.py file into environment-specific configurations. This is a good practice when you need to move a project between different environments, for example, development and production. This arrangement will mean less reconfiguration for different environments; instead, you’ll use an environment variable to switch between configurations, which we’ll discuss later in the tutorial.

      Create a new directory called settings in your project’s sub-directory:

      • mkdir testsite/testsite/settings

      (As per the prerequisites we’re using testsite, but you can substitute your project’s name in here.)

      This directory will replace your current settings.py configuration file; all of your environment-based settings will be in separate files contained in this folder.

      In your new settings folder, create three Python files:

      • cd testsite/testsite/settings
      • touch base.py development.py production.py

      The development.py file will contain settings you’ll normally use during development. And production.py will contain settings for use on a production server. You should keep these separate because the production configuration will use settings that will not work in a development environment; for example, forcing the use of HTTPS, adding headers, and using a production database.

      The base.py settings file will contain settings that development.py and production.py will inherit from. This is to reduce redundancy and to help keep your code cleaner. These Python files will be replacing settings.py, so you’ll now remove settings.py to avoid confusing Django.

      While still in your settings directory, rename settings.py to base.py with the following command:

      • mv ../settings.py base.py

      You’ve just completed the outline of your new environment-based settings directory. Your project won’t understand your new configuration yet, so next, you’ll fix this.

      Step 2 — Using python-dotenv

      Currently Django will not recognize your new settings directory or its internal files. So, before you continue working with your environment-based settings, you need to make Django work with python-dotenv. This is a dependency that loads environment variables from a .env file. This means that Django will look inside a .env file in your project’s root directory to determine which settings configuration it will use.

      Go to your project’s root directory:

      Install python-dotenv:

      • pip install python-dotenv

      Now you need to configure Django to use dotenv. You’ll edit two files to do this: manage.py, for development, and wsgi.py, for production.

      Let’s start by editing manage.py:

      Add the following code:

      testsite/manage.py

      import os
      import sys
      import dotenv
      
      def main():
          os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings.development')
      
          if os.getenv('DJANGO_SETTINGS_MODULE'):
          os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
      
          try:
              from django.core.management import execute_from_command_line
          except ImportError as exc:
              raise ImportError(
                  "Couldn't import Django. Are you sure it's installed and "
                  "available on your PYTHONPATH environment variable? Did you "
                  "forget to activate a virtual environment?"
              ) from exc
          execute_from_command_line(sys.argv)
      
      
      if __name__ == '__main__':
          main()
      
      dotenv.load_dotenv(
          os.path.join(os.path.dirname(__file__), '.env')
      )
      

      Save and close manage.py and then open wsgi.py for editing:

      Add the following highlighted lines:

      testsite/testsite/wsgi.py

      
      import os
      import dotenv
      
      from django.core.wsgi import get_wsgi_application
      
      dotenv.load_dotenv(
          os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
      )
      
      os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'testsite.settings.development')
      
      if os.getenv('DJANGO_SETTINGS_MODULE'):
       os.environ['DJANGO_SETTINGS_MODULE'] = os.getenv('DJANGO_SETTINGS_MODULE')
      
      application = get_wsgi_application()
      

      The code you’ve added to both of these files does two things. First, whenever Django runs—manage.py for running development, wsgi.py for production—you’re telling it to look for your .env file. If the file exists, you instruct Django to use the settings file that .env recommends, otherwise you use the development configuration by default.

      Save and close the file.

      Finally, let’s create a .env in your project’s root directory:

      Now add in the following line to set the environment to development:

      testsite/.env

      DJANGO_SETTINGS_MODULE="testsite.settings.development"
      

      Note: Add .env to your .gitignore file so it is never included in your commits; you’ll use this file to contain data such as passwords and API keys that you do not want visible publicly. Every environment your project is running on will have its own .env with settings for that specific environment.

      It is recommended to create a .env.example to include in your project so you can easily create a new .env wherever you need one.

      So, by default Django will use testsite.settings.development, but if you change DJANGO_SETTINGS_MODULE to testsite.settings.production for example, it will start using your production configuration. Next, you’ll populate your development.py and production.py settings configurations.

      Step 3 — Creating Development and Production Settings

      Next you’ll open your base.py and add the configuration you want to modify for each environment in the separate development.py and production.py files. The production.py will need to use your production database credentials, so ensure you have those available.

      Note: It is up to you to determine which settings you need to configure, based on environment. This tutorial will only cover a common example for production and development settings (that is, security settings and separate database configurations).

      In this tutorial we’re using the Django project from the prerequisite tutorial as the example project. We’ll move settings from from base.py to development.py. Begin by opening development.py:

      • nano testsite/settings/development.py

      First, you will import from base.py—this file inherits settings from base.py. Then you’ll transfer the settings you want to modify for the development environment:

      testsite/testsite/settings/development.py

      from .base import *
      
      DEBUG = True
      
      DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.sqlite3',
              'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
          }
      }
      

      In this case, the settings specific to development are: DEBUG, you need this True in development, but not in production; and DATABASES, a local database instead of a production database. You’re using an SQLite database here for development.

      Note: For security purposes, Django’s DEBUG output will never display any settings that may contain the strings: API, KEY, PASS, SECRET, SIGNATURE, or TOKEN.

      This is to ensure secrets will not be revealed, if you accidentally deploy a project to production with DEBUG still enabled.

      With that being said, never deploy a project publicly with DEBUG enabled. It will only ever put the security of your project at risk.

      Next, let’s add to production.py:

      • nano testsite/settings/production.py

      Production will be similar to development.py, but with a different database configuration and DEBUG set to False:

      testsite/testsite/settings/production.py

      from .base import *
      
      DEBUG = False
      
      ALLOWED_HOSTS = []
      
      DATABASES = {
          'default': {
              'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.sqlite3'),
              'NAME': os.environ.get('SQL_DATABASE', os.path.join(BASE_DIR, 'db.sqlite3')),
              'USER': os.environ.get('SQL_USER', 'user'),
              'PASSWORD': os.environ.get('SQL_PASSWORD', 'password'),
              'HOST': os.environ.get('SQL_HOST', 'localhost'),
              'PORT': os.environ.get('SQL_PORT', ''),
          }
      }
      

      For the example database configuration given, you can use dotenv to configure each of the given credentials, with defaults included. Assuming you’ve already set up a database for the production version of your project, please use your configuration instead of the example provided.

      You have now configured your project to use different settings based on DJANGO_SETTINGS_MODULE in .env. Given the example settings you’ve used, when you set your project to use production settings, DEBUG becomes False, ALLOWED_HOSTS is defined, and you start using a different database that you’ve (already) configured on your server.

      Step 4 — Working with Django’s Security Settings

      Django includes security settings ready for you to add to your project. In this step, you’ll add security settings to your project that are considered essential for any production project. These settings are intended for use when your project is available to the public. It’s not recommended to use any of these settings in your development environment; hence, in this step you’re limiting these settings to the production.py configuration.

      For the most part these settings are going to enforce the use of HTTPS for various web features, such as session cookies, CSRF cookies, upgrading HTTP to HTTPS, and so on. Therefore, if you haven’t already set up your server with a domain pointing to it, hold off on this section for now. If you need to set up your server ready for deployment, check out the Conclusion for suggested articles on this.

      First open production.py:

      In your file, add the highlighted settings that work for your project, according to the explanations following the code:

      testsite/testsite/settings/production.py

      from .base import *
      
      DEBUG = False
      
      ALLOWED_HOSTS = ['your_domain', 'www.your_domain']
      
      DATABASES = {
          'default': {
              'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.sqlite3'),
              'NAME': os.environ.get('SQL_DATABASE', os.path.join(BASE_DIR, 'db.sqlite3')),
              'USER': os.environ.get('SQL_USER', 'user'),
              'PASSWORD': os.environ.get('SQL_PASSWORD', 'password'),
              'HOST': os.environ.get('SQL_HOST', 'localhost'),
              'PORT': os.environ.get('SQL_PORT', ''),
          }
      }
      
      SECURE_SSL_REDIRECT = True
      
      SESSION_COOKIE_SECURE = True
      
      CSRF_COOKIE_SECURE = True
      
      SECURE_BROWSER_XSS_FILTER = True
      
      • SECURE_SSL_REDIRECT redirects all HTTP requests to HTTPS (unless exempt). This means your project will always try to use an encrypted connection. You will need to have SSL configured on your server for this to work. Note that if you have Nginx or Apache configured to do this already, this setting will be redundant.
      • SESSION_COOKIE_SECURE tells the browser that cookies can only be handled over HTTPS. This means cookies your project produces for activities, such as logins, will only work over an encrypted connection.
      • CSRF_COOKIE_SECURE is the same as SESSION_COOKIE_SECURE but applies to your CSRF token. CSRF tokens protect against Cross-Site Request Forgery. Django CSRF protection does this by ensuring any forms submitted (for logins, signups, and so on) to your project were created by your project and not a third party.
      • SECURE_BROWSER_XSS_FILTER sets the X-XSS-Protection: 1; mode=block header on all responses that do not already have it. This ensures third parties cannot inject scripts into your project. For example, if a user stores a script in your database using a public field, when that script is retrieved and displayed to other users it will not run.

      If you would like to read more about the different security settings available within Django, check out their documentation.

      Warning: Django’s documentation states you shouldn’t rely completely on SECURE_BROWSER_XSS_FILTER. Never forget to validate and sanitize input.

      Additional Settings

      The following settings are for supporting HTTP Strict Transport Security (HSTS)—this means that your entire site must use SSL at all times.

      • SECURE_HSTS_SECONDS is the amount of time in seconds HSTS is set for. If you set this for an hour (in seconds), every time you visit a web page on your website, it tells your browser that for the next hour HTTPS is the only way you can visit the site. If during that hour you visit an insecure part of your website, the browser will show an error and the insecure page will be inaccessible.
      • SECURE_HSTS_PRELOAD only works if SECURE_HSTS_SECONDS is set. This header instructs the browser to preload your site. This means that your website will be added to a hardcoded list, which is implemented in popular browsers, like Firefox and Chrome. This requires that your website is always encrypted. It is important to be careful with this header. If at anytime you decide to not use encryption for your project, it can take weeks to be manually removed from the HSTS Preload list.
      • SECURE_HSTS_INCLUDE_SUBDOMAINS applies the HSTS header to all subdomains. Enabling this header, means that both your_domain and unsecure.your_domain will require encryption even if unsecure.your_domain is not related to this Django project.

      Warning: Incorrectly configuring these additional settings can break your site for a significant amount of time.

      Please read the Django documentation on HSTS before implementing these settings.

      It is necessary to consider how these settings will work with your own Django project; overall the settings discussed here are a good foundation for most Django projects. Next you’ll review some further usage of dotENV.

      Step 5 — Using python-dotenv for Secrets

      The final part of this tutorial will help you leverage python-dotenv. This will allow you to hide certain information such as your project’s SECRET_KEY or the admin’s login URL. This is a great idea if you intend to publish your code on platforms like GitHub or GitLab since these secrets won’t be published. Instead, whenever you initially set up your project on a local environment or a server, you can create a new .env file and define those secret variables.

      You must hide your SECRET_KEY so you’ll start working on that in this section.

      Open your .env file:

      And add the following line:

      testsite/.env

      DJANGO_SETTINGS_MODULE="django_hardening.settings.development"
      SECRET_KEY="your_secret_key"
      

      And in your base.py:

      • nano testsite/settings/base.py

      Let’s update the SECRET_KEY variable like so:

      testsite/testsite/settings/base.py

      . . .
      SECRET_KEY = os.getenv('SECRET_KEY')
      . . .
      

      Your project will now use the SECRET_KEY located in .env.

      Lastly, you’ll hide your admin URL by adding a long string of random characters to it. This will ensure bots can’t brute force the login fields and strangers can’t try guessing the login.

      Open .env again:

      And add a SECRET_ADMIN_URL variable:

      testsite/.env

      DJANGO_SETTINGS_MODULE="django_hardening.settings.development"
      SECRET_KEY="your_secret_key"
      SECRET_ADMIN_URL="very_secret_url"
      

      Now let’s tell Django to hide your admin URL with SECRET_ADMIN_URL:

      Note: Don’t forget to replace your_secret_key and very_secret_url with your own secret strings. Also don’t forget to replace very_secret_url with your own secret URL.

      If you want to use random strings for these variables, Python provides a fantastic secrets.py library for generating such strings. The examples they give are great ways to create small Python programs for generating secure random strings.

      Edit the admin URL like so:

      testsite/testsite/urls.py

      import os
      from django.contrib import admin
      from django.urls import path
      
      urlpatterns = [
          path(os.getenv('SECRET_ADMIN_URL') + '/admin/', admin.site.urls),
      ]
      

      You can now find the admin login page at very_secret_url/admin/ instead of just /admin/.

      Conclusion

      In this tutorial you have configured your current Django project for easy use with different environments. Your project now leverages python-dotenv for handling secrets and settings. And your production settings now have Django’s built-in security features enabled.

      If you’ve enabled all the recommended security components and re-implemented settings as directed, your project has these key features:

      • SSL/HTTPS for all communications (for example, subdomains, cookies, CSRF).
      • XSS (cross-site scripting) attacks prevention.
      • CSRF (cross-site request forgery) attacks prevention.
      • Concealed project secret key.
      • Concealed admin login URL preventing brute-force attacks.
      • Separate settings for development and production.

      If you are interested in learning more about Django, check out our tutorial series on Django development.

      Also, if you haven’t already put your project into production, here is a tutorial on How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 20.04. You can also check out our Django topic page for further tutorials.

      And, of course, please read over Django’s settings documentation, for further information.



      Source link

      How To Secure Node.js Applications with a Content Security Policy


      The author selected the Free Software Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      When the browser loads a page, it executes a lot of code to render the content. The code could be from the same origin as the root document, or a different origin. By default, the browser does not distinguish between the two and executes any code requested by a page regardless of the source. Attackers use this exploit to maliciously inject scripts to the page, which are then executed because the browser has no way of determining if the content is harmful. These situations are where a Content Security Policy (CSP) can provide protection.

      A CSP is an HTTP header that provides an extra layer of security against code-injection attacks, such as cross-site scripting (XSS), clickjacking, and other similar exploits. It facilitates the creation of an “allowlist” of trusted content and blocks the execution of code from sources not present in the allowlist. It also reports any policy violations to a URL of your choice, so that you can keep abreast of potential security attacks.

      With the CSP header, you can specify approved sources for content on your site that the browser can load. Any code that is not from the approved sources, will be blocked from executing, which makes it considerably more difficult for an attacker to inject content and siphon data.

      In this tutorial, you’ll review the different protections the CSP header offers by implementing one in an example Node.js application. You’ll also collect JSON reports of CSP violations to catch problems and fix exploits quickly.

      Prerequisites

      To follow this tutorial, you will need the following:

      • A recent version of Node.js installed on your machine. Follow the steps in the relevant How To Install Node.js tutorial for your operating system to set up a Node.js development environment.

      You should also use a recent browser version, preferably Chrome, as it has the best support for CSP level 3 directives at the time of writing this article (November 2020). Also, make sure to disable any third-party extensions while testing the CSP implementation so that they don’t interfere with the violation reports rendered in the console.

      Step 1 — Setting Up the Demo Project

      To demonstrate the process of creating a Content Security Policy, we’ll work through the entire process of implementing one for this demo project. It’s a one-page website with a variety of content that approximates a typical website or application. It includes a small Vue.js application, YouTube embeds, and some images sourced from Unsplash. It also uses Google fonts and the Bootstrap framework, which is loaded over a content delivery network (CDN).

      In this step, you’ll set up the demo project on your test server or local machine and view it in your browser.

      First, clone the project to your filesystem using the following command:

      • git clone https://github.com/do-community/csp-demo

      Once your project directory is set up, change into it with the following command:

      Next, install the dependencies specified in the package.json file with the next command. You use the express package to set up the web server, while nodemon helps to automatically restart the node application when it detects file changes in the directory:

      Once the dependencies you’ve installed the dependencies, enter the following command to start the web server on port 5500:

      You can now visit your_server_ip:5500 or localhost:5500 in your browser to view the demo page. You will find the text Hello World!, a YouTube embed, and some images on the page.

      CSP Demo

      In the next section, we’ll implement a CSP policy that covers only the most basic protections. We’ll then build on that in the subsequent sections as we uncover all the legitimate resources that we need to allow on the page.

      Step 2 — Implementing a Basic CSP

      Let’s go ahead and write a CSP policy that restricts fonts, images, scripts, styles, and embeds to those originating from the current host only. The following is the response header that achieves this:

      Content-Security-Policy: default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self';
      

      Here’s an explanation of the policy directives in this header:

      • font-src defines the sources from where fonts can be loaded from.
      • img-src defines the sources from which image loading is permitted.
      • script-src controls any script-loading privileges on a web page.
      • style-src is the directive for allowing stylesheet sources.
      • frame-src defines allowed sources for frame embeds. (It was deprecated in CSP level 2, but reinstated in level 3.)
      • default-src defines a fallback policy for certain directives if they are not explicitly specified in the header. Here is a complete list of the directives that fall back to default-src.

      In this example, all the specified directives are assigned the 'self' keyword in their source list. This indicates that only resources from the current host (including the URL scheme and port number) should be allowed to execute. For example, script-src 'self' allows the execution of scripts from the current host, but it blocks all other script sources.

      Let’s go ahead and add the header to our Node.js project.

      Leave your app running and open a new terminal window to work with your server.js file:

      Next, add the CSP header from the example in an Express middleware layer. This ensures that you’re including the header in every response from the server:

      server.js

      const express = require('express');
      const bodyParser = require('body-parser');
      const path = require('path');
      const app = express();
      
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy',
          "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'"
        );
        next();
      });
      
      app.use(bodyParser.json());
      app.use(express.static(path.join(__dirname)));
      
      app.get('/', (req, res) => {
        res.sendFile(path.join(__dirname + '/index.html'));
      });
      
      const server = app.listen(process.env.PORT || 5500, () => {
        const { port } = server.address();
        console.log(`Server running on PORT ${port}`);
      });
      

      Save the file and reload the project in your browser. You’ll notice that the page is completely broken.

      Our CSP header is working as expected and all the external sources that we included on the page have been blocked from loading because they violate the defined policy. However, this is not an ideal way to test a brand-new policy since it can break a website when violations occur.

      Broken page after adding basic CSP

      This is why the Content-Security-Policy-Report-Only header exists. You can use it instead of Content-Security-Policy to prevent the browser from enforcing the policy, while still reporting the violations that occur—this means that you can refine the policy without putting your site at risk. Once you’re happy with your policy, you can switch back to the enforcing header so that the protections are activated.

      Go ahead and replace the Content-Security-Policy header with Content-Security-Policy-Report-Only in your server.js file:

      Add the following highlighted code:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-src 'self'"
        );
        next();
      });
      . . .
      

      Save the file and reload the page in your browser. The page returns to a working state, but the browser console still reports the CSP violations. Each violation is prefixed with [Report Only] to indicate that the policy is not enforced.

      Report only mode is now active

      In this section, we created the initial implementation of our CSP and set it to report-only mode so that we can refine it without causing the site to break. In the next section, we’ll fix the violations triggered through our initial CSP.

      Step 3 — Fixing Policy Violations

      The policy we implemented in the previous section triggered several violations because we restricted all resources to the origin only—however, we have several third-party assets on the page.

      The two ways to fix CSP violations are: approving the sources in the policy, or removing the code that triggers the violations. Since legitimate resources are triggering all the violations, we’ll concentrate mainly on the former option in this section.

      Open up your browser console. It will display all the current violations of the CSP. Let’s fix each of these issues.

      Allowing the Stylesheets

      The first two violations in the console are from the Google fonts and Bootstrap stylesheets, which you’re loading from https://fonts.googleapis.com and https://cdn.jsdelivr.net respectively. You can allow them both on the page through the style-src directive:

      style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net;
      

      This specifies that CSS files from the origin host, https://fonts.googleapis.com, and https://cdn.jsdelivr.net, should be executed on the page. This policy is quite broad, because it allows any stylesheet from the allowlist domains (not just the ones you’re currently using).

      We can be more specific by using the exact file or directory we’d like to allow instead:

      style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css;
      

      Now, it will only allow the exact, specified stylesheet to execute. It will block all other stylesheets—even if they originate from https://cdn.jsdelivr.net.

      You can update the CSP header like the following with the updated style-src directive. By the time you reload the page, both violations will be resolved:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self';"
        );
        next();
      });
      . . .
      

      Allowing the Image Sources

      The images you’re using on the page are from a single source: https://images.unsplash.com. Let’s allow it through the img-src directive like the following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self'"
        );
        next();
      });
      . . .
      

      Allowing the Youtube Embed

      You can allow valid sources for nested browsing contexts, which use elements such as <iframe>, through the frame-src directive. If this directive is absent, the browser will look for the child-src directive, which subsequently falls back to the default-src directive.

      Our current policy limits frame embeds to the origin host. Let’s add https://www.youtube.com to the allowlist so that the CSP doesn’t block Youtube embeds from loading once we enforce the policy:

      frame-src 'self' https://www.youtube.com;
      

      Note that the www subdomain is significant here. If you have an embed from https://youtube.com, it will be blocked according to this policy unless you also add https://youtube.com to the allowlist:

      frame-src 'self' https://www.youtube.com https://youtube.com;
      

      Here’s the updated CSP header. Change the frame-src directive as following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self'; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      Allowing the Font Files

      Google fonts violations

      The Google fonts stylesheet contain references to several font files from https://fonts.gstatic.com. You will find that these files currently violate the defined font-src policy (currently 'self'), so you need to account for them in your revised policy:

      font-src 'self' https://fonts.gstatic.com;
      

      Update the font-src directive in the CSP header as following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self'; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      Once you reload the page, the console will no longer report violations for the Google fonts files.

      Allowing the Vue.js Script

      A Vue.js script loaded over a CDN is rendering the Hello world! text at the top of the page. We’ll allow its execution on the page through the script-src directive. As mentioned earlier, it’s important to be specific when allowing CDN sources so we don’t open up our site to other possible malicious scripts that are hosted on that domain.

      script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js;
      

      In this example, you’re only allowing the exact script at that URL to execute on the page. If you want to switch between development and production builds, you’ll need to add the other script URL to the allowlist as well, or you can allow all the resources present in the /dist location, to cover both cases at once:

      script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/;
      

      Here’s the updated CSP header with the relevant changes:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      At this point, we’ve successfully allowed all the external files and scripts that our page relies on. But we still have one more CSP violation to resolve due to the presence of an inline script on the page. We’ll explore a few solutions to this problem in the next section.

      Step 4 — Handling Inline Sources

      Although you can approve inline code (such as JavaScript code in a <script> tag) within a CSP using the 'unsafe-inline' keyword, it is not recommended because it greatly increases the risk of a code-injection attack.

      This example policy allows the execution of any inline script on the page, but this is not safe for the aforementioned reasons.

      script-src 'self' 'unsafe-inline' https://unpkg.com/vue@3.0.2/dist/;
      

      The best way to avoid using unsafe-inline is to move the inline code to an external file and reference it that way. This is a better approach for caching, minification, and maintainability, and it also makes the CSP easier to modify in the future.

      However, if you absolutely must use inline code, there are two major ways to add them to your allowlist safely.

      Option 1 — Using a Hash

      This method requires that you calculate a SHA hash that is based on the script itself and then add it to the script-src directive. In recent versions of Chrome, you don’t even need to generate the hash yourself as it’s already included in the CSP violation error in the console:

      [Report Only] Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https://unpkg.com/vue@3.0.2/dist/". Either the 'unsafe-inline' keyword, a hash ('sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='), or a nonce ('nonce-...') is required to enable inline execution.
      

      The highlighted section here is the exact SHA256 hash that you would need to add to the script-src directive to allow the execution of the specific inline script that triggered the violation.

      Copy the hash and add it to your CSP as follows:

      script-src 'self' https://unpkg.com/vue@3.0.2/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM=';
      

      The disadvantage to this approach is that if the contents of the script changes, the generated hash will be different, which will trigger a violation.

      Option 2 — Using a Nonce

      The second way to allow the execution of inline code is by using a nonce. These are random strings that you can use to allow a complete block of code regardless of its content.

      Here’s an example of a nonce value in use:

      script-src 'self' https://unpkg.com/vue@3.0.2/dist/ 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'
      

      The value of the nonce in the CSP must match the nonce attribute on the script:

      <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
        // Some inline code
      </script>
      

      Nonces must be unguessable and dynamically generated each time the page is loaded so that an attacker is unable to use them for the execution of a malicious script. If you decide to implement this option, you can use the crypto package to generate a nonce as following:

      const crypto = require('crypto');
      let nonce = crypto.randomBytes(16).toString('base64');
      

      We will opt for the hash method in this tutorial since it’s more practical for our use case.

      Update the script-src directive in your CSP header to include the SHA256 hash of the sole inline script as shown following:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://unpkg.com/vue@3.0.2/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com;"
        );
        next();
      });
      . . .
      

      This removes the final CSP violation error that the inline script triggers from the console.

      In the next section, we will monitor the effects of our CSP in a production environment.

      Step 5 — Monitoring Violations

      Once you have your CSP in place, it’s necessary to keep an eye on its effect once in use. For example, if you forget to allow a legitimate source in production or when an attacker is trying to exploit an XSS attack vector (which you need to identify and stop immediately).

      Without some form of active reporting in place, there’s no way to know of these events. This is why the report-to directive exists. It specifies a location that the browser should POST a JSON-formatted violation report to, in the event it has to take action based on the CSP.

      To use this directive, you need to add an additional header to specify an endpoint for the Reporting API:

      Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}
      

      Once that is set, specify the group name in the report-to directive as following:

      report-to csp-endpoint;
      

      Here’s the updated portion of the server.js file with the changes. Be sure to replace the <your_server_ip> placeholder in the Report-To header with your actual server IP address:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Report-To',
          '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://<your_server_ip>:5500/__cspreport__"}],"include_subdomains":true}'
        );
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint;"
        );
        next();
      });
      . . .
      

      The report-to directive is intended to replace the now deprecated report-uri directive, but most browsers don’t support it yet (as of November 2020). So, for compatibility with current browsers while also ensuring compatibility with future browser releases support, you should specify both report-uri and report-to in your CSP. If the latter is supported, it will ignore the former:

      server.js

      . . .
      app.use(function (req, res, next) {
        res.setHeader(
          'Report-To',
          '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}'
        );
        res.setHeader(
          'Content-Security-Policy-Report-Only',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;"
        );
        next();
      });
      . . .
      

      The /__cspreport__ route needs to exist on the server as well; add this to your file like the following:

      server.js

      . . .
      app.get('/', (req, res) => {
        res.sendFile(path.join(__dirname + '/index.html'));
      });
      
      app.post('/__cspreport__', (req, res) => {
        console.log(req.body);
      });
      . . .
      

      Some browsers send the Content-Type of the report payload as application/csp-report, while others use application/json. If the report-to directive is supported, the Content-Type should be application/reports+json.

      To account for all the possible Content-Type values, you have to set up some configurations on your express server:

      server.js

      . . .
      app.use(
        bodyParser.json({
          type: ['application/json', 'application/csp-report', 'application/reports+json'],
        })
      );
      . . .
      

      At this point, any CSP violations will be sent to the /__cspreport__ route and subsequently logged to the terminal.

      You can try it out by adding a resource from a source that is not compliant with the current CSP, or modifying the inline script in the index.html file as shown following:

      index.html

      . . .
      <script>
        new Vue({
          el: '#vue',
          render(createElement) {
            return createElement('h1', 'Hello World!');
          },
        });
        console.log("Hello")
      </script>
      . . .
      

      This will trigger a violation because the hash of the script is now different from what you included in the CSP header.

      Here’s a typical violation report from a browser using the report-uri:

      {
        'csp-report': {
          'document-uri': 'http://localhost:5500/',
          referrer: '',
          'violated-directive': 'script-src-elem',
          'effective-directive': 'script-src-elem',
          'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;",
          disposition: 'report',
          'blocked-uri': 'inline',
          'line-number': 58,
          'source-file': 'http://localhost:5500/',
          'status-code': 200,
          'script-sample': ''
        }
      }
      

      The parts to this report are:

      • document-uri: The page the violation occurred on.
      • referrer: The page’s referrer.
      • blocked-uri: The resource that violated the page’s policy (an inline script in this case).
      • line-number: The line number where the inline code begins.
      • violated-directive: The specific directive that was violated. (script-src-elem in this case, which falls back to script-src.)
      • original-policy: The complete policy of the page.

      If the browser supports the report-to directive, the payload should have a similar structure to what is following. Notice how it’s different from the report-uri payload while still carrying the same information:

      [{
          "age": 16796,
          "body": {
              "blocked-uri": "https://vimeo.com",
              "disposition": "enforce",
              "document-uri": "https://localhost:5500/",
              "effective-directive": "frame-src",
              "line-number": 58,
          'original-policy': "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-uri /__cspreport__;",
              "referrer": "",
              "script-sample": "",
              "sourceFile": "https://localhost:5500/",
              "violated-directive": "frame-src"
          },
          "type": "csp",
          "url": "https://localhost:5500/",
          "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
      }]
      

      Note: The report-to directive is supported only in secure contexts, which means that you need to set up your Express server with a valid HTTPS certificate, otherwise you won’t be able to test or use it.

      In this section, we successfully set up CSP monitoring on our server so that we can detect and fix problems quickly. Let’s go ahead and finish this tutorial by enforcing the final policy in the next step.

      Step 6 — Publishing the Final Policy

      Once you’re confident your CSP is set up correctly (ideally after leaving it in production for a few days or weeks in report-only mode), you can enforce it by changing the CSP header from Content-Security-Policy-Report-Only to Content-Security-Policy.

      In addition to reporting the violations, this will stop unauthorized resources from being executed on the page, leading to a safer experience for your visitors.

      Here is the final version of the server.js file:

      server.js

      const express = require('express');
      const bodyParser = require('body-parser');
      const path = require('path');
      const app = express();
      
      app.use(function (req, res, next) {
        res.setHeader(
          'Report-To',
          '{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"http://your_server_ip:5500/__cspreport__"}],"include_subdomains":true}'
        );
        res.setHeader(
          'Content-Security-Policy',
          "default-src 'self'; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.unsplash.com; script-src 'self' https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/ 'sha256-INJfZVfoUd61ITRFLf63g+S/NJAfswGDl15oK0iXgYM='; style-src 'self' https://fonts.googleapis.com https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css; frame-src 'self' https://www.youtube.com https://youtube.com; report-to csp-endpoint; report-uri /__cspreport__;"
        );
        next();
      });
      
      app.use(
        bodyParser.json({
          type: [
            'application/json',
            'application/csp-report',
            'application/reports+json',
          ],
        })
      );
      app.use(express.static(path.join(__dirname)));
      
      app.get('/', (req, res) => {
        res.sendFile(path.join(__dirname + '/index.html'));
      });
      
      app.post('/__cspreport__', (req, res) => {
        console.log(req.body);
      });
      
      const server = app.listen(process.env.PORT || 5500, () => {
        const { port } = server.address();
        console.log(`Server running on PORT ${port}`);
      });
      

      Browser support
      The CSP header is supported in all browsers with the exception of Internet Explorer, which uses the non-standard X-Content-Security-Policy header instead. If you need to support IE, you have to issue the CSP twice in the response headers.

      The latest version of the CSP spec (level 3) also introduced some newer directives that are not well supported at the moment. Examples include the script-src-elem and prefetch-src directives. Make sure to use the appropriate fallbacks when setting up those directives to ensure the protections remain active in browsers that have not caught up yet.

      Conclusion

      In this article, you’ve set up an effective Content Security Policy for a Node.js application and monitored the violations. If you have an older or more complex website, it will require a wider policy setup that covers all the bases. However, setting up a thorough policy is worth the effort as it makes it a lot harder for an attacker to exploit your website to steal user data.

      You can find the final code from this tutorial in this GitHub repository.

      For more security-related articles, check out our Security topic page. If you would like to learn more about working with Node.js, you can read our How To Code in Node.js series.



      Source link

      Utilizing Security Features in SSH


      How to Join

      This Tech Talk is free and open to everyone. Register on Eventbrite here to receive a link to join on Wednesday, November 18, 2020, 11:00 a.m.–12:00 p.m. ET.

      About the Talk

      SSH lets you securely manage remote systems. However, most people don’t make use of the full set of security features SSH has to offer. In this session, we will cover security topics regarding SSH such as encryption algorithms, using ssh-agent to forward our credentials, setting up two factor authentication, and more.

      What You’ll Learn

      • Various security settings for SSH, such as strong encryption algorithms, ssh agents, two factor authentication, and more.

      This Talk is Designed For

      • DevOps engineers
      • System administrators

      Prerequisites

      • Basic knowledge of SSH, such as logging in and creating/using SSH keys

      About the Presenter

      Mason Egger (@masonegger) is currently a Developer Advocate at DigitalOcean, specializing in cloud infrastructure, distributed systems, and Python. Prior to his work at DigitalOcean, he was an SRE (Site Reliability Engineer), helping build and maintain a highly available hybrid multi-cloud PaaS. He is an avid programmer, speaker, educator, and writer/blogger. He is a maintainer of the DigitalOcean Terraform provider and contributes to random open source projects here and there. In his spare time, he enjoys reading, camping, kayaking, and exploring new places.

      To join the live Tech Talk, register here.





      Source link