Search code examples
node.jssecuritygoogle-app-enginestrapi

How to securely set database password in Strapi?


The Strapi framework (as far as I understand) requires the database password to be provided at launch. Usually, the password is specified in the database.js file, like this:

module.exports = ({ env }) => ({
  defaultConnection: 'default',
  connections: {
    default: {
      connector: 'bookshelf',
      settings: {
        client: 'postgres',
        host: '/cloudsql/myDatabaseInstanceName',
        database: 'databaseName',
        username: 'databaseUsername',
        password: 'databasePassword',
      },
    },
  },
});

This of course is not very secure, as the database.js file is usually committed to the repo.

Therefore, some people inject the password into the database.js file, instead storing it as an environment variable:

module.exports = ({ env }) => ({
  defaultConnection: 'default',
  connections: {
    default: {
      connector: 'bookshelf',
      settings: {
        client: 'postgres',
        host: `/cloudsql/${env('INSTANCE_CONNECTION_NAME')}`,
        database: env('DATABASE_NAME'),
        username: env('DATABASE_USERNAME'),
        password: env('DATABASE_PASSWORD'),
      },
    },
  },
});

However, this is also not very secure. In many runtime environments (including Google App Engine, which I am using) the environment passwords can be viewed, in plaintext, by any project user.

Ideally, I would like to store the database password in a secret vault (I'm using Google Secret Manager), and somehow provide the password from the vault to the database.js file at launch. But I don't understand how to implement that? Is it even possible to access a secret vault from database.js? Or, how else might I securly inject my database password into Strapi?

Thanks!


Solution

  • Solution for Strapi V4 - After trying so many solutions I finally found one. We have to do it in index.js > "async register" function. I tried in bootstrap.js as well but it didnt work.

    //index.js
        ....
        ....
        async register({ strapi }) {
        try {
          console.log("Fetching database secrets....");
          const secretData = await getSecret();
          console.log("Database secrets fetched successfully !!!");
          strapi.config.set("database.connection.connection.host", secretData.host);
          strapi.config.set("database.connection.connection.database", secretData.dbname);
          strapi.config.set("database.connection.connection.user", secretData.username);
          strapi.config.set("database.connection.connection.password", secretData.password);
          strapi.config.set("database.connection.connection.port", secretData.port);
    
        } catch (err) {
          console.log("error is secret fetch call.", err)
        }
    
      },
     ....
     ....
    

    And simply is database.js try to connect with other default configurations.

    //database.js
    module.exports = ({ env }) => {
      return {
        connection: {
          client: 'postgres',
          connection: {
            //postgres connection is set in index.js, no need to set it here again.
            schema: env('DATABASE_SCHEMA', 'public'), // Not required
    
            ssl: {
              rejectUnauthorized: env.bool('DATABASE_SSL_SELF', false), // For self-signed certificates
            },
            useNullAsDefault: true,
          }
        }
      }
    };