One place for hosting & domains

      February 2020

      How To Use Database Migrations and Seeders to Abstract Database Setup in Laravel


      Migrations and seeders are powerful database utilities provided by the Laravel PHP framework to allow developers to quickly bootstrap, destroy and recreate an application’s database. These utilities help to minimize database inconsistency problems that can arise with multiple developers working on the same application: new contributors need only to run a couple artisan commands to set the database up on a fresh install.

      In this guide, we’ll create migrations and seeders to populate a Laravel demo application’s database with sample data. At the end, you will be able to destroy and recreate your database tables as many times as you want, using only artisan commands.

      Prerequisites

      In order to follow this guide, you’ll need:

      Note: In this guide, we’ll use a containerized development environment managed by Docker Compose to run the application, but you may also opt to run the application on a LEMP server. To set this up, you can follow our guide on How to Install and Configure Laravel with LEMP on Ubuntu 18.04.

      Step 1 — Obtaining the Demo Application

      To get started, we’ll fetch the demo Laravel application from its GitHub repository. We’re interested in the tutorial-02 branch, which includes a Docker Compose setup to run the application on containers. In this example, we’ll download the application to our home folder, but you can use any directory of your choice:

      • cd ~
      • curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-2.0.1.zip -o travellist.zip

      Because we downloaded the application code as a .zip file, we’ll need the unzip command to unpack it. If you haven’t done so recently, update your machine’s local package index:

      Then install the unzip package:

      Following that, unzip the contents of the application:

      Then rename the unpacked directory to travellist-demo for easier access:

      • mv travellist-laravel-demo-tutorial-2.0.1 travellist-demo

      In the next step, we’ll create a .env configuration file to set up the application.

      Step 2 — Setting Up the Application’s .env File

      In Laravel, a .env file is used to set up environment-dependent configurations, such as credentials and any information that might vary between deployments. This file is not included in revision control.

      Warning: The environment configuration file contains sensitive information about your server, including database credentials and security keys. For that reason, you should never share this file publicly.

      The values contained in the .env file will take precedence over the values set in regular configuration files located in the config directory. Each installation on a new environment requires a tailored environment file to define things such as database connection settings, debug options, and the application URL, among other items that may vary depending on which environment the application is running.

      Navigate to the travellist-demo directory:

      We’ll now create a new .env file to customize the configuration options for the development environment we’re setting up. Laravel comes with an example.env file that we can copy to create our own:

      Open this file using nano or your text editor of choice:

      This is how your .env file looks like now:

      .env

      APP_NAME=Travellist
      APP_ENV=dev
      APP_KEY=
      APP_DEBUG=true
      APP_URL=http://localhost:8000 
      
      LOG_CHANNEL=stack
      
      DB_CONNECTION=mysql
      DB_HOST=db
      DB_PORT=3306
      DB_DATABASE=travellist
      DB_USERNAME=travellist_user
      DB_PASSWORD=password

      The current .env file from the travellist demo application contains settings to use the containerized environment we’ve created with Docker Compose in the last part of this series. You don’t need to change any of these values, but you are free to modify the DB_DATABASE, DB_USERNAME and DB_PASSWORD if you wish, since these are pulled by our docker-compose.yml file automatically to set up the development database. Just make sure the DB_HOST variable remains unchanged, since it references the name of our database service within the Docker Compose environment.

      If you make any changes to the file, make sure to save and close it by pressing CTRL + X, Y, then ENTER.

      Note: If you have opted to run the application on a LEMP server, you’ll need to change the highlighted values to reflect your own database settings, including the DB_HOST variable.

      Step 3 — Installing Application Dependencies with Composer

      We’ll now use Composer, PHP’s dependency management tool, to install the application’s dependencies and make sure we’re able to execute artisan commands.

      Bring up your Docker Compose environment with the following command.
      This will build the travellist image for the app service and pull in the additional Docker images required by the nginx and db services, in order to create the application environment:

      Output

      Creating network "travellist-demo_travellist" with driver "bridge" Building app Step 1/11 : FROM php:7.4-fpm ---> fa37bd6db22a Step 2/11 : ARG user ---> Running in 9259bb2ac034 … Creating travellist-app ... done Creating travellist-nginx ... done Creating travellist-db ... done

      This operation might take a few minutes to complete. Once the process is finished, we can run Composer to install the application’s dependencies.

      To execute composer and other commands in the app service container, we’ll use docker-compose exec. The exec command allows us to execute any command of our choice on containers managed by Docker Compose. It uses the following syntax: docker-compose exec service_name command.

      Note: In case you have opted to use a LEMP server to run the demo application, you should ignore the docker-compose exec app portion of the commands listed throughout this guide. For example, instead of running the following command as it’s written, you would just run:

      To execute composer install in the app container, run:

      • docker-compose exec app composer install

      Output

      Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 85 installs, 0 updates, 0 removals - Installing doctrine/inflector (1.3.1): Downloading (100%) - Installing doctrine/lexer (1.2.0): Downloading (100%) - Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%) …

      When Composer is finished installing the application’s dependencies, you’ll be able to execute artisan commands. To test that the application is able to connect to the database, run the following command which will clean up any pre-existing tables:

      • docker-compose exec app php artisan db:wipe

      This command will drop any pre-existing tables on the configured database. If it ran successfully and the application is able to connect to the database, you’ll see output like this:

      Output

      Dropped all tables successfully.

      Now that you have installed the application dependencies with Composer, you can use the artisan tool to create migrations and seeders.

      Step 4 — Creating Database Migrations

      The artisan command line tool that ships with Laravel contains a series of helper commands that can be used to manage the application and bootstrap new classes. To generate a new migration class, we can use the make:migration command as follows:

      • docker-compose exec app php artisan make:migration create_places_table

      Laravel infers the operation to be executed (create), the name of the table (places), and whether this migration will create a new table or not, based on the descriptive name provided to the make:migration command.

      You’ll see output similar to this:

      Output

      Created Migration: 2020_02_03_143622_create_places_table

      This will generate a new file in the application’s database/migrations directory. The timestamp included in the auto-generated file is used by Laravel to determine in which order migrations should be executed.

      Use your text editor of choice to open the generated migration file. Remember to replace the highlighted value with your own migration file name:

      • nano database/migrations/2020_02_03_143622_create_places_table.php

      The generated migration file contains a class called CreatePlacesTable:

      database/migrations/2020_02_03_143622_create_places_table.php

      <?php
      
      use IlluminateDatabaseMigrationsMigration;
      use IlluminateDatabaseSchemaBlueprint;
      use IlluminateSupportFacadesSchema;
      
      class CreatePlacesTable extends Migration
      {
          /**
           * Run the migrations.
           *
           * @return void
           */
          public function up()
          {
              Schema::create('places', function (Blueprint $table) {
                  $table->bigIncrements('id');
                  $table->timestamps();
              });
          }
      
          /**
           * Reverse the migrations.
           *
           * @return void
           */
          public function down()
          {
              Schema::dropIfExists('places');
          }
      }
      
      

      This class has two methods: up and down. Both methods contain bootstrap code that you can extend to customize what happens when that migration is executed and also what happens when it is rolled back.

      We’ll modify the up method so that the places table reflects the structure we’re already using in the current application’s version:

      • id: primary key field.
      • name: name of the place.
      • visited: whether or not this place was already visited.

      The Laravel schema builder exposes methods for creating, updating and deleting tables in a database. The Blueprint class defines the table’s structure and it provides several methods to abstract the definition of each table field.

      The auto-generated code sets up a primary id field called id. The timestamps method creates two datetime fields that are automatically updated by the underlying database classes when data is inserted or updated within that table. In addition to these, we’ll need to include a name and a visited field.

      Our name field will be of type string, and our visited field will be set with the boolean type. We’ll also set a default value of 0 for the visited field, so that if no value is passed, it means the place was not visited yet. This is how the up method will look like now:

      database/migrations/2020_02_03_143622_create_places_table.php

      …
          public function up()
          {
              Schema::create('places', function (Blueprint $table) {
                  $table->bigIncrements('id');
                  $table->string('name', 100);
                  $table->boolean('visited')->default(0);
                  $table->timestamps();
              });
          }
      …
      

      Note: You can find the full list of available column types in the Laravel documentation.

      After including the two highlighted lines on your own migration script, save and close the file.

      Your migration is now ready to be executed via artisan migrate. However, that would only create an empty table; we also need to be able to insert sample data for development and testing. In the next step, we’ll see how to do that using database seeders.

      Step 5 — Creating Database Seeders

      A seeder is a special class used to generate and insert sample data (seeds) in a database. This is an important feature in development environments, since it allows you to recreate the application with a fresh database, using sample values that you’d otherwise have to manually insert each time the database is recreated.

      We’ll now use the artisan command to generate a new seeder class for our places table called PlacesTableSeeder:

      • docker-compose exec app php artisan make:seeder PlacesTableSeeder

      The command will create a new file called PlacesTableSeeder.php inside the database/seeds directory. Open that file using your text editor of choice:

      • nano database/seeds/PlacesTableSeeder.php

      This is what the auto-generated PlacesTableSeeder.php file looks like:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      
      class PlacesTableSeeder extends Seeder
      {
          /**
           * Run the database seeds.
           *
           * @return void
           */
          public function run()
          {
              //
          }
      }
      
      

      Our new seeder class contains an empty method named run. This method will be called when the db:seed Artisan command is executed.

      We need to edit the run method in order to include instructions to insert sample data in the database. We’ll use the Laravel query builder to streamline this process.

      The Laravel query builder offers a fluent interface for database operations such as inserting, updating, deleting, and retrieving data. It also introduces safeguards against SQL injection attacks. The query builder is exposed by the DB facade - a static proxy to underlying database classes in the service container.

      To get started, we’ll create a static class variable to hold all the sample places we want to insert into the database as an array. This will allow us to use a foreach loop to iterate through all values, inserting each one in the database using the query builder.

      We’ll call this variable $places:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      
      class PlacesTableSeeder extends Seeder
      {
          static $places = [
              'Berlin',
              'Budapest',
              'Cincinnati',
              'Denver',
              'Helsinki',
              'Lisbon',
              'Moscow',
              'Nairobi',
              'Oslo',
              'Rio',
              'Tokyo'
          ];

      Next, we’ll need to include a use statement at the top of our PlacesTableSeeder class to facilitate referencing the DB facade throughout the code:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      use IlluminateSupportFacadesDB;
      
      class PlacesTableSeeder extends Seeder
      …
      

      We can now iterate through the $places array values using a foreach loop, and insert each one in our places table using the query builder:

      database/seeds/PlacesTableSeeder.php

      …
          public function run()
          {
              foreach (self::$places as $place) {
                  DB::table('places')->insert([
                      'name' => $place,
                      'visited' => rand(0,1) == 1
                  ]);
              }
          }
      
      

      The foreach loop iterates through each value of the $places static array. At each iteration, we use the DB facade to insert a new row at the places table. We set the name field to the name of the place we just obtained from the $places array, and we set the visited field to a random value of either 0 or 1.

      This is what the full PlacesTableSeeder class will look like after all the updates:

      database/seeds/PlacesTableSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      use IlluminateSupportFacadesDB;
      
      class PlacesTableSeeder extends Seeder
      {
          static $places = [
              'Berlin',
              'Budapest',
              'Cincinnati',
              'Denver',
              'Helsinki',
              'Lisbon',
              'Moscow',
              'Nairobi',
              'Oslo',
              'Rio',
              'Tokyo'
          ];
      
          /**
           * Run the database seeds.
           *
           * @return void
           */
          public function run()
          {
              foreach (self::$places as $place) {
                  DB::table('places')->insert([
                      'name' => $place,
                      'visited' => rand(0,1) == 1
                  ]);
              }
          }
      }
      

      Save and close the file when you’re done making these changes.

      Seeder classes aren’t automatically loaded in the application. We need to edit the main DatabaseSeeder class to include a call to the seeder we’ve just created.

      Open the database/seeds/DatabaseSeeder.php file using nano or your favorite editor:

      • nano database/seeds/DatabaseSeeder.php

      The DatabaseSeeder class looks like any other seeder: it extends from the Seeder class and has a run method. We’ll update this method to include a call to PlacesTableSeeder.

      Update the current run method inside your DatabaseSeeder class by deleting the commented-out line and replacing it with the following highlighted code:

      database/seeds/DatabaseSeeder.php

      …
          public function run()
          {
              $this->call(PlacesTableSeeder::class);
          }
      ...
      

      This is how the full DatabaseSeeder class will look like after the update:

      database/seeds/DatabaseSeeder.php

      <?php
      
      use IlluminateDatabaseSeeder;
      
      class DatabaseSeeder extends Seeder
      {
          /**
           * Seed the application's database.
           *
           * @return void
           */
          public function run()
          {
              $this->call(PlacesTableSeeder::class);
          }
      }
      
      
      

      Save and close the file when you’re done updating its content.

      We have now finished setting up both a migration and a seeder for our places table. In the next step, we’ll see how to execute them.

      Step 6 — Running Database Migrations and Seeders

      Before proceeding, we need to make sure your application is up and running. We’ll set up the application encryption key and then access the application from a browser to test the web server.

      To generate the encryption key required by Laravel, you can use the artisan key:generate command:

      • docker-compose exec app php artisan key:generate

      Once the key has been generated, you’ll be able to access the application by pointing your browser to your server hostname or IP address on port 8000:

      http://server_host_or_ip:8000
      

      You’ll see a page like this:

      MySQL error

      That means the application is able to connect to the database, but it couldn’t find a table called places. We’ll create the places table now, using the following migrate artisan command:

      • docker-compose exec app php artisan migrate

      You’ll get output similar to this:

      Output

      Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (0.06 seconds) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (0.06 seconds) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (0.03 seconds) Migrating: 2020_02_10_144134_create_places_table Migrated: 2020_02_10_144134_create_places_table (0.03 seconds)

      You’ll notice that a few other migrations were executed along with the create_places_table migration we’ve set up. These migrations are auto generated when Laravel is installed. Although we won’t be using these additional tables now, they will be needed in the future when we expand the application to have registered users and scheduled jobs. For now, you can just leave them as is.

      At this point our table is still empty. We need to run the db:seed command to seed the database with our sample places:

      • docker-compose exec app php artisan db:seed

      This will run our seeder and insert the sample values we defined within our PlacesTableSeeder class. You’ll see output similar to this:

      Output

      Seeding: PlacesTableSeeder Seeded: PlacesTableSeeder (0.06 seconds) Database seeding completed successfully.

      Now, reload the application page on your browser. You’ll see a page similar to this:

      Demo Laravel Application

      Whenever you need to start from scratch, you can drop all your database tables with:

      • docker-compose exec app php artisan db:wipe

      Output

      Dropped all tables successfully.

      To run the app migrations and seed the tables in a single command, you can use:

      • docker-compose exec app php artisan migrate --seed

      Output

      Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table (0.06 seconds) Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table (0.07 seconds) Migrating: 2019_08_19_000000_create_failed_jobs_table Migrated: 2019_08_19_000000_create_failed_jobs_table (0.03 seconds) Migrating: 2020_02_10_144134_create_places_table Migrated: 2020_02_10_144134_create_places_table (0.03 seconds) Seeding: PlacesTableSeeder Seeded: PlacesTableSeeder (0.06 seconds) Database seeding completed successfully.

      If you want to roll back a migration, you can run:

      • docker-compose exec app php artisan migrate:rollback

      This will trigger the down method for each migration class inside the migrations folder. Typically, it will remove all the tables that were created through migration classes, leaving alone any other tables that might have been manually created. You’ll see output like this:

      Output

      Rolling back: 2020_02_10_144134_create_places_table Rolled back: 2020_02_10_144134_create_places_table (0.02 seconds) Rolling back: 2019_08_19_000000_create_failed_jobs_table Rolled back: 2019_08_19_000000_create_failed_jobs_table (0.02 seconds) Rolling back: 2014_10_12_100000_create_password_resets_table Rolled back: 2014_10_12_100000_create_password_resets_table (0.02 seconds) Rolling back: 2014_10_12_000000_create_users_table Rolled back: 2014_10_12_000000_create_users_table (0.02 seconds)

      The rollback command is especially useful when you’re making changes to application models and a db:wipe command can’t be used - for instance, if multiple systems depend on the same database.

      Conclusion

      In this guide, we’ve seen how to use database migrations and seeders to facilitate setting up development and testing databases for a Laravel 6 application.

      As a next step, you might want to check the Laravel documentation for more details on how to use the query builder, and how to use Eloquent models to abstract your application’s database schema even further.



      Source link

      Understanding Map and Set Objects in JavaScript


      The author selected the Open Internet/Free Speech Fund to receive a donation as part of the Write for DOnations program.

      In JavaScript, developers often spend a lot of time deciding the correct data structure to use. This is because choosing the correct data structure can make it easier to manipulate that data later on, saving time and making code easier to comprehend. The two predominant data structures for storing collections of data are Objects and Arrays (a type of object). Developers use Objects to store key/value pairs and Arrays to store indexed lists. However, to give developers more flexibility, the ECMAScript 2015 specification introduced two new types of iterable objects: Maps, which are ordered collections of key/value pairs, and Sets, which are collections of unique values.

      In this article, you will go over the Map and Set objects, what makes them similar or different to Objects and Arrays, the properties and methods available to them, and examples of some practical uses.

      Maps

      A Map is a collection of key/value pairs that can use any data type as a key and can maintain the order of its entries. Maps have elements of both Objects (a unique key/value pair collection) and Arrays (an ordered collection), but are more similar to Objects conceptually. This is because, although the size and order of entries is preserved like an Array, the entries themselves are key/value pairs like Objects.

      Maps can be initialized with the new Map() syntax:

      const map = new Map()
      

      This gives us an empty Map:

      Output

      Map(0) {}

      Adding Values to a Map

      You can add values to a map with the set() method. The first argument will be the key, and the second argument will be the value.

      The following adds three key/value pairs to map:

      map.set('firstName', 'Luke')
      map.set('lastName', 'Skywalker')
      map.set('occupation', 'Jedi Knight')
      

      Here we begin to see how Maps have elements of both Objects and Arrays. Like an Array, we have a zero-indexed collection, and we can also see how many items are in the Map by default. Maps use the => syntax to signify key/value pairs as key => value:

      Output

      Map(3) 0: {"firstName" => "Luke"} 1: {"lastName" => "Skywalker"} 2: {"occupation" => "Jedi Knight"}

      This example looks similar to a regular object with string-based keys, but we can use any data type as a key with Maps.

      In addition to manually setting values on a Map, we can also initialize a Map with values already. We do this using an Array of Arrays containing two elements that are each key/value pairs, which looks like this:

      [ [ 'key1', 'value1'], ['key2', 'value2'] ]
      

      Using the following syntax, we can recreate the same Map:

      const map = new Map([
        ['firstName', 'Luke'],
        ['lastName', 'Skywalker'],
        ['occupation', 'Jedi Knight'],
      ])
      

      Note: This example uses trailing commas, also referred to as dangling commas. This is a JavaScript formatting practice in which the final item in a series when declaring a collection of data has a comma at the end. Though this formatting choice can be used for cleaner diffs and easier code manipulation, whether to use it or not is a matter of preference. For more information on trailing commas, see this Trailing Comma article from the MDN web docs.

      Incidentally, this syntax is the same as the result of calling Object.entries() on an Object. This provides a ready-made way to convert an Object to a Map, as shown in the following code block:

      const luke = {
        firstName: 'Luke',
        lastName: 'Skywalker',
        occupation: 'Jedi Knight',
      }
      
      const map = new Map(Object.entries(luke))
      

      Alternatively, you can turn a Map back into an Object or an Array with a single line of code.

      The following converts a Map to an Object:

      const obj = Object.fromEntries(map)
      

      This will result in the following value of obj:

      Output

      {firstName: "Luke", lastName: "Skywalker", occupation: "Jedi Knight"}

      Now, let’s convert a Map to an Array:

      const arr = Array.from(map)
      

      This will result in the following Array for arr:

      Output

      [ ['firstName', 'Luke'], ['lastName', 'Skywalker'], ['occupation', 'Jedi Knight'] ]

      Map Keys

      Maps accept any data type as a key, and do not allow duplicate key values. We can demonstrate this by creating a map and using non-string values as keys, as well as setting two values to the same key.

      First, let’s initialize a map with non-string keys:

      const map = new Map()
      
      map.set('1', 'String one')
      map.set(1, 'This will be overwritten')
      map.set(1, 'Number one')
      map.set(true, 'A Boolean')
      

      This example will override the first key of 1 with the subsequent one, and it will treat '1' the string and 1 the number as unique keys:

      Output

      0: {"1" => "String one"} 1: {1 => "Number one"} 2: {true => "A Boolean"}

      Although it is a common belief that a regular JavaScript Object can already handle Numbers, booleans, and other primitive data types as keys, this is actually not the case, because Objects change all keys to strings.

      As an example, initialize an object with a numerical key and compare the value for a numerical 1 key and a stringified "1" key:

      // Initialize an object with a numerical key
      const obj = { 1: 'One' }
      
      // The key is actually a string
      obj[1] === obj['1']  // true
      

      This is why if you attempt to use an Object as a key, it will print out the string object Object instead.

      As an example, create an Object and then use it as the key of another Object:

      // Create an object
      const objAsKey = { foo: 'bar' }
      
      // Use this object as the key of another object
      const obj = {
        [objAsKey]: 'What will happen?'
      } 
      

      This will yield the following:

      Output

      {[object Object]: "What will happen?"}

      This is not the case with Map. Try creating an Object and setting it as the key of a Map:

      // Create an object
      const objAsKey = { foo: 'bar' }
      
      const map = new Map()
      
      // Set this object as the key of a Map
      map.set(objAsKey, 'What will happen?')
      

      The key of the Map element is now the object we created.

      Output

      key: {foo: "bar"} value: "What will happen?"

      There is one important thing to note about using an Object or Array as a key: the Map is using the reference to the Object to compare equality, not the literal value of the Object. In JavaScript {} === {} returns false, because the two Objects are not the same two Objects, despite having the same (empty) value.

      That means that adding two unique Objects with the same value will create a Map with two entries:

      // Add two unique but similar objects as keys to a Map
      map.set({}, 'One')
      map.set({}, 'Two')
      

      This will yield the following:

      Output

      Map(2) {{…} => "One", {…} => "Two"}

      But using the same Object reference twice will create a Map with one entry.

      // Add the same exact object twice as keys to a Map
      const obj = {}
      
      map.set(obj, 'One')
      map.set(obj, 'Two')
      

      Which will result in the following:

      Output

      Map(1) {{…} => "Two"}

      The second set() is updating the same exact key as the first, so we end up with a Map that only has one value.

      Getting and Deleting Items from a Map

      One of the disadvantages of working with Objects is that it can be difficult to enumerate them, or work with all the keys or values. The Map structure, by contrast, has a lot of built-in properties that make working with their elements more direct.

      We can initialize a new Map to demonstrate the following methods and properties: delete(), has(), get(), and size.

      // Initialize a new Map
      const map = new Map([
        ['animal', 'otter'],
        ['shape', 'triangle'],
        ['city', 'New York'],
        ['country', 'Bulgaria'],
      ])
      

      Use the has() method to check for the existence of an item in a map. has() will return a Boolean.

      // Check if a key exists in a Map
      map.has('shark') // false
      map.has('country') // true
      

      Use the get() method to retrieve a value by key.

      // Get an item from a Map
      map.get('animal') // "otter"
      

      One particular benefit Maps have over Objects is that you can find the size of a Map at any time, like you can with an Array. You can get the count of items in a Map with the size property. This involves fewer steps than converting an Object to an Array to find the length.

      // Get the count of items in a Map
      map.size // 4
      

      Use the delete() method to remove an item from a Map by key. The method will return a Boolean—true if an item existed and was deleted, and false if it did not match any item.

      // Delete an item from a Map by key
      map.delete('city') // true
      

      This will result in the following Map:

      Output

      Map(3) {"animal" => "otter", "shape" => "triangle", "country" => "Bulgaria"}

      Finally, a Map can be cleared of all values with map.clear().

      // Empty a Map
      map.clear()
      

      This will yield:

      Output

      Map(0) {}

      Keys, Values, and Entries for Maps

      Objects can retrieve keys, values, and entries by using the properties of the Object constructor. Maps, on the other hand, have prototype methods that allow us to get the keys, values, and entries of the Map instance directly.

      The keys(), values(), and entries() methods all return a MapIterator, which is similar to an Array in that you can use for...of to loop through the values.

      Here is another example of a Map, which we can use to demonstrate these methods:

      const map = new Map([
        [1970, 'bell bottoms'],
        [1980, 'leg warmers'],
        [1990, 'flannel'],
      ])
      

      The keys() method returns the keys:

      map.keys()
      

      Output

      MapIterator {1970, 1980, 1990}

      The values() method returns the values:

      map.values()
      

      Output

      MapIterator {"bell bottoms", "leg warmers", "flannel"}

      The entries() method returns an array of key/value pairs:

      map.entries()
      

      Output

      MapIterator {1970 => "bell bottoms", 1980 => "leg warmers", 1990 => "flannel"}

      Iteration with Map

      Map has a built-in forEach method, similar to an Array, for built-in iteration. However, there is a bit of a difference in what they iterate over. The callback of a Map’s forEach iterates through the value, key, and map itself, while the Array version iterates through the item, index, and array itself.

      // Map 
      Map.prototype.forEach((value, key, map) = () => {})
      
      // Array
      Array.prototype.forEach((item, index, array) = () => {})
      

      This is a big advantage for Maps over Objects, as Objects need to be converted with keys(), values(), or entries(), and there is not a simple way to retrieve the properties of an Object without converting it.

      To demonstrate this, let’s iterate through our Map and log the key/value pairs to the console:

      // Log the keys and values of the Map with forEach
      map.forEach((value, key) => {
        console.log(`${key}: ${value}`)
      })
      

      This will give:

      Output

      1970: bell bottoms 1980: leg warmers 1990: flannel

      Since a for...of loop iterates over iterables like Map and Array, we can get the exact same result by destructuring the array of Map items:

      // Destructure the key and value out of the Map item
      for (const [key, value] of map) {
        // Log the keys and values of the Map with for...of
        console.log(`${key}: ${value}`)
      }
      

      Map Properties and Methods

      The following table shows a list of Map properties and methods for quick reference:

      Properties/Methods Description Returns
      set(key, value) Appends a key/value pair to a Map Map Object
      delete(key) Removes a key/value pair from a Map by key Boolean
      get(key) Returns a value by key value
      has(key) Checks for the presence of an element in a Map by key Boolean
      clear() Removes all items from a Map N/A
      keys() Returns all keys in a Map MapIterator object
      values() Returns all values in a Map MapIterator object
      entries() Returns all keys and values in a Map as [key, value] MapIterator object
      forEach() Iterates through the Map in insertion order N/A
      size Returns the number of items in a Map Number

      When to Use Map

      Summing up, Maps are similar to Objects in that they hold key/value pairs, but Maps have several advantages over objects:

      • Size – Maps have a size property, whereas Objects do not have a built-in way to retrieve their size.
      • Iteration – Maps are directly iterable, whereas Objects are not.
      • Flexibility – Maps can have any data type (primitive or Object) as the key to a value, while Objects can only have strings.
      • Ordered – Maps retain their insertion order, whereas objects do not have a guaranteed order.

      Due to these factors, Maps are a powerful data structure to consider. However, Objects haves some important advantages as well:

      • JSON – Objects work flawlessly with JSON.parse() and JSON.stringify(), two essential functions for working with JSON, a common data format that many REST APIs deal with.
      • Working with a single element – Working with a known value in an Object, you can access it directly with the key without the need to use a method, such as Map’s get().

      This list will help you decide if a Map or Object is the right data structure for your use case.

      Set

      A Set is a collection of unique values. Unlike a Map, a Set is conceptually more similar to an Array than an Object, since it is a list of values and not key/value pairs. However, Set is not a replacement for Arrays, but rather a supplement for providing additional support for working with duplicated data.

      You can initialize Sets with the new Set() syntax.

      const set = new Set()
      

      This gives us an empty Set:

      Output

      Set(0) {}

      Items can be added to a Set with the add() method. (This is not to be confused with the set() method available to Map, although they are similar.)

      // Add items to a Set
      set.add('Beethoven')
      set.add('Mozart')
      set.add('Chopin')
      

      Since Sets can only contain unique values, any attempt to add a value that already exists will be ignored.

      set.add('Chopin') // Set will still contain 3 unique values
      

      Note: The same equality comparison that applies to Map keys applies to Set items. Two objects that have the same value but do not share the same reference will not be considered equal.

      You can also initialize Sets with an Array of values. If there are duplicate values in the array, they will be removed from the Set.

      // Initialize a Set from an Array
      const set = new Set(['Beethoven', 'Mozart', 'Chopin', 'Chopin'])
      

      Output

      Set(3) {"Beethoven", "Mozart", "Chopin"}

      Conversely, a Set can be converted into an Array with one line of code:

      const arr = [...set]
      

      Output

      (3) ["Beethoven", "Mozart", "Chopin"]

      Set has many of the same methods and properties as Map, including delete(), has(), clear(), and size.

      // Delete an item
      set.delete('Beethoven') // true
      
      // Check for the existence of an item
      set.has('Beethoven') // false
      
      // Clear a Set
      set.clear()
      
      // Check the size of a Set
      set.size // 0
      

      Note that Set does not have a way to access a value by a key or index, like Map.get(key) or arr[index].

      Keys, Values, and Entries for Sets

      Map and Set both have keys(), values(), and entries() methods that return an Iterator. However, while each one of these methods have a distinct purpose in Map, Sets do not have keys, and therefore keys are an alias for values. This means that keys() and values() will both return the same Iterator, and entries() will return the value twice. It makes the most sense to only use values() with Set, as the other two methods exist for consistency and cross-compatibility with Map.

      const set = new Set([1, 2, 3])
      // Get the values of a set
      set.values()
      

      Output

      SetIterator {1, 2, 3}

      Iteration with Set

      Like Map, Set has a built-in forEach() method. Since Sets don’t have keys, the first and second parameter of the forEach() callback return the same value, so there is no use case for it outside of compatibility with Map. The parameters of forEach() are (value, key, set).

      Both forEach() and for...of can be used on Set. First, let’s look at forEach() iteration:

      const set = new Set(['hi', 'hello', 'good day'])
      
      // Iterate a Set with forEach
      set.forEach((value) => console.log(value))
      

      Then we can write the for...of version:

      // Iterate a Set with for...of
      for (const value of set) {  
          console.log(value);
      }
      

      Both of these strategies will yield the following:

      Output

      hi hello good day

      Set Properties and Methods

      The following table shows a list of Set properties and methods for quick reference:

      Properties/Methods Description Returns
      add(value) Appends a new item to a Set Set Object
      delete(value) Removes the specified item from a Set Boolean
      has() Checks for the presence of an item in a Set Boolean
      clear() Removes all items from a Set N/A
      keys() Returns all values in a Set (same as values()) SetIterator object
      values() Returns all values in a Set (same as keys()) SetIterator object
      entries() Returns all values in a Set as [value, value] SetIterator object
      forEach() Iterates through the Set in insertion order N/A
      size Returns the number of items in a Set Number

      When to Use Set

      Set is a useful addition to your JavaScript toolkit, particularly for working with duplicate values in data.

      In a single line, we can create a new Array without duplicate values from an Array that has duplicate values.

      const uniqueArray = [ ...new Set([1, 1, 2, 2, 2, 3])] // (3) [1, 2, 3]
      

      This will give:

      Output

      (3) [1, 2, 3]

      Set can be used for finding the union, intersection, and difference between two sets of data. However, Arrays have a significant advantage over Sets for additional manipulation of the data due to the sort(), map(), filter(), and reduce() methods, as well as direct compatibility with JSON methods.

      Conclusion

      In this article, you learned that a Map is a collection of ordered key/value pairs, and that a Set is a collection of unique values. Both of these data structures add additional capabilities to JavaScript and simplify common tasks such as finding the length of a key/value pair collection and removing duplicate items from a data set, respectively. On the other hand, Objects and Arrays have been traditionally used for data storage and manipulation in JavaScript, and have direct compatibility with JSON, which continues to make them the most essential data structures, especially for working with REST APIs. Maps and Sets are primarily useful as supporting data structures for Objects and Arrays.

      If you would like to learn more about JavaScript, check out the homepage for our How To Code in JavaScript series, or browse our How to Code in Node.js series for articles on back-end development.



      Source link

      Recommended Steps To Harden Apache HTTP on FreeBSD 12.0


      The author selected the Free and Open Source Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Although the default installation of an Apache HTTP server is already safe to use, its configuration can be substantially improved with a few modifications. You can complement already present security mechanisms, for example, by setting protections around cookies and headers, so connections can’t be tampered with at the user’s client level. By doing this you can dramatically reduce the possibilities of several attack methods, like Cross-Site Scripting attacks (also known as XSS). You can also prevent other types of attacks, such as Cross-Site Request Forgery, or session hijacking, as well as Denial of Service attacks.

      In this tutorial you’ll implement some recommended steps to reduce how much information on your server is exposed. You will verify the directory listings and disable indexing to check the access to resources. You’ll also change the default value of the timeout directive to help mitigate Denial of Service type of attacks. Furthermore you’ll disable the TRACE method so sessions can’t be reversed and hijacked. Finally you’ll secure headers and cookies.

      Most of the configuration settings will be applied to the Apache HTTP main configuration file found at /usr/local/etc/apache24/httpd.conf.

      Prerequisites

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

      With the prerequisites in place you have a FreeBSD system with a stack on top able to serve web content using anything written in PHP, such as major CMS software. Furthermore, you’ve encrypted safe connections through Let’s Encrypt.

      Reducing Server Information

      The operating system banner is a method used by computers, servers, and devices of all kinds to present themselves into networks. Malicious actors can use this information to gain exploits into the relevant systems. In this section you’ll reduce the amount of information published by this banner.

      Sets of directives control how this information is displayed. For this purpose the ServerTokens directive is important; by default it displays all details about the operating system and compiled modules to the client that’s connecting to it.

      You’ll use a tool for network scanning to check what information is currently revealed prior to applying any changes. To install nmap run the following command:

      To get your server’s IP address, you can run the following command:

      • ifconfig vtnet0 | awk '/inet / {print $2}'

      You can check the web server response by using the following command:

      • nmap -sV -p 80 your-server-ip

      You invoke nmap to make a scan (hence the -s flag), to display the version (the -V flag) on port 80 (the -p flag) on the given IP or domain.

      You’ll receive information about your web server, similar to the following:

      Output

      Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-22 00:30 CET Nmap scan report for 206.189.123.232 Host is up (0.054s latency). PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.41 ((FreeBSD) OpenSSL/1.1.1d-freebsd Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 7.59 seconds

      This output shows that information such as the operating system, the Apache HTTP version, and OpenSSL are visible. This can be useful for attackers to gain information about the server and choose the right tools to exploit, for example, a vulnerability in the software running on the server.

      You’ll place the ServerTokens directive in the main configuration file since it doesn’t come configured by default. The lack of this configuration makes Apache HTTP display the full information about the server as the documentation states. To limit the information that is revealed about your server and configuration, you’ll place the ServerTokens directive inside the main configuration file.

      You’ll place this directive following the ServerName entry in the configuration file. Run the following command to find the directive

      • grep -n 'ServerName' /usr/local/etc/apache24/httpd.conf

      You’ll find the line number that you can then search with vi:

      Output

      226 #ServerName www.example.com:80

      Run the following command:

      • sudo vi +226 /usr/local/etc/apache24/httpd.conf

      Add the following highlighted line:

      /usr/local/etc/apache24/httpd.conf

      . . .
      #ServerName www.example.com:80
      ServerTokens Prod
      

      Save and exit the file with :wq and ENTER.

      Setting the ServerTokens directive to Prod will make it only display that this is an Apache web server.

      For this to take effect restart the Apache HTTP server:

      To test the changes, run the following command:

      • nmap -sV -p 80 your-server-ip

      You’ll see similar output to the following with more minimal information on your Apache web server:

      Output

      Starting Nmap 7.80 ( https://nmap.org ) at 2020-01-22 00:58 CET Nmap scan report for WPressBSD (206.189.123.232) Host is up (0.056s latency). PORT STATE SERVICE VERSION 80/tcp open http Apache httpd Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 7.59 seconds

      You’ve seen what information the server was announcing prior to the change and you’ve now reduced this to the minimum. With this you’re providing fewer clues about your server to an external actor. In the next step you’ll manage the directory listings for your web server.

      Managing Directory Listings

      In this step you’ll ensure the directory listing is correctly configured, so the right parts of the system are publicly available as intended, while the remainder are protected.

      Note: When an argument is declared it is active, but the + can visually reinforce it is in fact enabled. When a minus sign - is placed the argument is denied, for example, Options -Indexes.

      Arguments with + and/or - can not be mixed, it is considered bad syntax in Apache HTTP and it may be rejected at the start up.

      Adding the statement Options -Indexes will set the content inside the data path /usr/local/www/apache24/data to not index (read listed) automatically if an .html file doesn’t exist, and not show if a URL maps this directory. This will also apply when using virtual host configurations such as the one used for the prerequisite tutorial for the Let’s Encrypt certificate.

      You will set the Options directive with the -Indexes argument and with the +FollowSymLinks directive, which will allow symbolic links to be followed. You’ll use the + symbol in order to comply with Apache’s HTTP conventions.

      Run the following command to find the line to edit in the configuration file:

      • grep -n 'Options Indexes FollowSymLinks' /usr/local/etc/apache24/httpd.conf

      You’ll see output similar to the following:

      Output

      263 : Options Indexes FollowSymLinks

      Run this command to directly access the line for editing:

      • sudo vi +263 /usr/local/etc/apache24/httpd.conf

      Now edit the line as per the configuration:

      /usr/local/etc/apache24/httpd.conf

      . . .
      #
      Options -Indexes +FollowSymLinks
      
      #
      . . .
      

      Save and exit the file with :wq and ENTER.

      Restart Apache HTTP to implement these changes:

      At your domain in the browser, you’ll see a forbidden access message, also known as the 403 error. This is due to the changes you’ve applied. Placing -Indexes into the Options directive has disabled the auto-index capability of Apache HTTP and therefore there’s no index.html file inside the data path.

      You can solve this by placing an index.html file inside the VirtualHost you enabled in the prerequisite tutorial for the Let’s Encrypt certificate. You’ll use the default block within Apache HTTP and place it in the same folder as the DocumentRootthat you declared in the virtual host.

      /usr/local/etc/apache24/extra/httpd-vhosts.conf

      <VirtualHost *:80>
          ServerAdmin your_email@your_domain.com
          DocumentRoot "/usr/local/www/apache24/data/your_domain.com"
          ServerName your_domain.com
          ServerAlias www.your_domain.com
          ErrorLog "/var/log/your_domain.com-error_log"
          CustomLog "/var/log/your_domain.com-access_log" common
      </VirtualHost>
      

      Use the following command to do this:

      • sudo cp /usr/local/www/apache24/data/index.html /usr/local/www/apache24/data/your_domain.com/index.html

      Now you’ll see an It works! message when visiting your domain.

      In this section you’ve placed restrictions to the Indexes directive to not automatically enlist and display content other than what you intend. Now if there is not an index.html file inside the data path Apache HTTP will not automatically create an index of contents. In the next step you’ll move beyond obscuring information and customize different directives.

      Reducing the Timeout Directive Value

      The Timeout directive sets the limit of time Apache HTTP will wait for new input/output before failing the connection request. This failure can occur due to different circumstances such as packets not arriving to the server or data not being confirmed as received by the client.

      By default the timeout is set to 60 seconds. In environments where the internet service is slow this default value may be sensible, but one minute is quite a long time particularly if the server is covering a target of users with faster internet service. Furthermore the time during which the server is not closing the connection can be abused to perform Denial of Service attacks (DoS). If a flood of these malicious connections occurs the server will stumble and possibly become saturated and irresponsive.

      To change the value you’ll find the Timeout entries in the httpd-default.conf file:

      • grep -n 'Timeout' /usr/local/etc/apache24/extra/httpd-default.conf

      You’ll see similar output to:

      Output

      8 # Timeout: The number of seconds before receives and sends time out. 10 Timeout 60 26 # KeepAliveTimeout: Number of seconds to wait for the next request from the 29 KeepAliveTimeout 5 89 RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500

      In the output line 10 sets the Timeout directive value. To directly access this line run the following command:

      • sudo vi +10 /usr/local/etc/apache24/extra/httpd-default.conf

      You’ll change it to 30 seconds, for example, like the following:

      /usr/local/etc/apache24/extra/httpd-default.conf

      #
      # Timeout: The number of seconds before receives and sends time out.
      #
      Timeout 30
      

      Save and exit the file with :wq and ENTER.

      The value of the Timeout directive has to balance a time range large enough for those events to allow a legitimate and successful connection to happen, but short enough to prevent undesired connection attempts.

      Note: Denial of Service attacks can drain the server’s resources quite effectively. A complementary and very capable counter measure is using a threaded MPM to get the best performance out of how Apache HTTP handles connections and processes. In this tutorial How To Configure Apache HTTP with MPM Event and PHP-FPM on FreeBSD 12.0 there are steps on enabling this capability.

      For this change to take effect restart the Apache HTTP server:

      You’ve changed the default value of the Timeout directive in order to partially mitigate DoS attacks.

      Disabling the TRACE method

      The Hypertext Transport Protocol was developed following a client-server model and as such, the protocol has request methods to retrieve or place information from/to the server. The server needs to understand these sets of methods and the interaction between them. In this step you’ll configure the minimum necessary methods.

      TheTRACE method, which was considered harmless, was leveraged to perform Cross Site Tracing attacks. These types of attacks allow malicious actors to steal user sessions through that method. The method was designed for debugging purposes by the server returning the same request originally sent by the client. Because the cookie from the browser’s session is sent to the server it will be sent back again. However, this could potentially be intercepted by a malicious actor, who can then redirect a browser’s connection to a site of their control and not to the original server.

      Because of the possibility of the misuse of the TRACE method it is recommended to only use it for debugging and not in production. In this section you’ll disable this method.

      Edit the httpd.conf file with the following command and then press G to reach the end of the file:

      • sudo vi /usr/local/etc/apache24/httpd.conf

      Add the following entry path at the end of the file:

      /usr/local/etc/apache24/httpd.conf

      . . .
      TraceEnable off
      

      A good practice is to only specify the methods you’ll use in your Apache HTTP web server. This will help limit potential entry points for malicious actors.

      LimitExcept can be useful for this purpose since it will not allow any other methods than those declared in it. For example a configuration can be established like this one:

      /usr/local/etc/apache24/httpd.conf

      DocumentRoot "/usr/local/www/apache24/data"
      <Directory "/usr/local/www/apache24/data">
          Options -Indexes +FollowSymLinks -Includes
          AllowOverride none
           <LimitExcept GET POST HEAD>
             deny from all
          </LimitExcept>
          Require all granted
      </Directory>
      

      As declared within the LimitExcept directive only the GET, POST, and HEAD methods are allowed in the configuration.

      • The GET method is part of the HTTP protocol and it is used to retrieve data.
      • The POST method is also part of the HTTP protocol and is used to send data to the server.
      • The HEAD method is similar to GET, however this has no response body.

      You’ll use the following command and place the LimitExcept block inside the file:

      • sudo vi +272 /usr/local/etc/apache24/httpd.conf

      To set this configuration you’ll place the following block into the DocumentRoot directive entry where the content will be read from, more specifically inside the Directory entry:

      /usr/local/etc/apache24/httpd.conf

      . . .
      <LimitExcept GET POST HEAD>
         deny from all
      </LimitExcept>
      . . .
      

      To apply the changes restart Apache HTTP:

      The newer directive AllowedMethods provides similar functionality, although its status is still experimental.

      You’ve seen what HTTP methods are, their use, and the protection they offer from malicious activity leveraging the TRACE method as well as how to declare what methods to use. Next you’ll work with further protections dedicated to HTTP headers and cookies.

      Securing Headers and Cookies

      In this step you’ll set specific directives to protect the sessions that the client machines will open when visiting your Apache HTTP web server. This way your server will not load unwanted content, encryption will not be downgraded, and you’ll avoid content sniffing.

      Headers are components of the requests methods. There are headers to adjust authentication, communication between server and client, caching, content negotiation, and so on.

      Cookies are bits of information sent by the server to the browser. These bits allow the server to recognize the client browser from one computer to another. They also allow servers to recognize user sessions. For example, they can track a shopping cart of a logged-in user, payment information, history, and so on. Cookies are used and retained in the client’s web browser since HTTP is a stateless protocol, meaning once the connection closes the server does not remember the request sent by one client, or another one.

      It is important to protect headers as well as cookies because they provide communication between the web browser client and the web server.

      The headers module comes activated by default. To check if it’s loaded you’ll use the following command:

      • sudo apachectl -M | grep 'headers'

      You’ll see the following output:

      Output

      headers_module (shared)

      If you don’t see any output, check if the module is activated inside Apache’s httpd.conf file:

      • grep -n 'mod_headers' /usr/local/etc/apache24/httpd.conf

      As output you’ll see an uncommented line referring to the specific module for headers:

      /usr/local/etc/apache24/httpd.conf

      . . .
      122  LoadModule headers_module libexec/apache24/mod_headers.so
      . . .
      

      Remove the hashtag at the beginning of the mod_headers.so line, if present, to activate the directive.

      By making use of the following Apache HTTP directives you’ll protect headers and cookies from malicious activity to reduce the risk for clients and servers.

      Now you’ll set the header’s protection. You’ll place all these header values in one block. You can choose to apply these values as you wish, but all are recommended.

      Edit the httpd.conf file with the following command and then press G to reach the end of the file:

      • sudo vi /usr/local/etc/apache24/httpd.conf

      Place the following block at the end of the file:

      /usr/local/etc/apache24/httpd.conf

      . . .
      <IfModule mod_headers.c>
        # Add security and privacy related headers
        Header set Content-Security-Policy "default-src 'self'; upgrade-insecure-requests;"
        Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
        Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure"
        Header set X-Content-Type-Options "nosniff"
        Header set X-XSS-Protection "1; mode=block"
        Header set Referrer-Policy "strict-origin"
        Header set X-Frame-Options: "deny"
        SetEnv modHeadersAvailable true
      </IfModule>
      
      • Header set Strict-Transport-Security "max-age=31536000; includeSubDomains": HTTP Strict Transport Security (HTSTS) is a mechanism for web servers and clients (mainly browsers) to establish communications using only HTTPS. By implementing this you’re avoiding man-in-the-middle attacks, where a third party in between the communication could potentially access the bits, but also tamper with them.

      • Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure": The HttpOnly and Secure flags on headers help prevent cross-site scripting attacks, also known as XSS. Cookies can be misused by attackers to pose as legitimate visitors presenting themselves as someone else (identity theft), or be tampered.

      • Header set Referrer-Policy "strict-origin": The Referrer-Policy header sets what information is included as the referrer information in the header field.

      • Header set Content-Security-Policy "default-src 'self'; upgrade-insecure-requests;": The Content-Security-Policy header (CSP) will completely prevent loading content not specified in the parameters, which is helpful to prevent cross-site scripting (XSS) attacks. There are many possible parameters to configure the policy for this header. The bottom line is configuring it to load content from the same site and upgrade any content with an HTTP origin.

      • Header set X-XSS-Protection "1; mode=block": This supports older browsers that do not cope with Content-Security-Policy headers. The ‘X-XSS-Protection’ header provides protection against Cross-Site Scripting attacks. You do not need to set this header unless you need to support old browser versions, which is rare.

      • Header set X-Frame-Options: "deny": This prevents clickjacking attacks. The 'X-Frame-Options’ header tells a browser if a page can be rendered in a <frame>, <iframe>, <embed>, or <object>. This way content from other sites cannot be embedded into others, preventing clickjacking attacks. Here you’re denying all frame render so the web page can’t be embedded anywhere else, not even inside the same web site. You can adapt this to your needs, if, for example, you must authorize rendering some pages because they are advertisements or collaborations with specific websites.

      • Header set X-Content-Type-Options "nosniff": The 'X-Content-Type-Options’ header controls MIME types so they’re not changed and followed. MIME types are file format standards; they work for text, audio, video, image, and so on. This header blocks malicious actors from content sniffing those files and trying to alter the file types.

      Now restart Apache for the changes to take effect:

      To check the security levels of your configuration settings, visit the security headers website. Having followed the steps in this tutorial, your domain will score an A grade.

      Note: If you make your headers check by visiting https://securityheaders.com/ and get an F grade it could be because there is no index.html inside the DocumentRoot of your site as instructed at the end of Step 2. If checking your headers you get a different grade than an A or an F, check each Header set line looking for any misspelling that may have caused the downgrade.

      In this step you have worked with up to seven settings to improve the security of your headers and cookies. These will help prevent cross-site scripting, clickjacking, and other types of attacks.

      Conclusion

      In this tutorial you’ve addressed several security aspects, from information disclosure, to protecting sessions, through setting alternative configuration settings for important functionality.

      For further resources on hardening Apache, here are some other references:

      For extra tools to protect Apache HTTP:



      Source link