Skip to main content

Securing a Vox installation

Vox runs inside your own .NET application, in infrastructure you control. Vox is responsible for how it stores credentials and enforces permissions; you are responsible for where secrets come from, who can reach the API and how the installation is operated. This page covers the part that is yours.

Secrets and configuration​

Vox is configured in code, through the options you pass to AddVox. The values themselves should come from the .NET configuration system rather than from literals in the source.

var vox = services.AddVox(options =>
{
options.UseUsers<ApplicationUser>(users =>
{
users.InitialAdministratorEmail = builder.Configuration["Vox:Users:InitialAdministratorEmail"];
users.InitialAdministratorPassword = builder.Configuration["Vox:Users:InitialAdministratorPassword"];
});
});

Supply the values from a source that is not in version control:

  • Production. Environment variables, or a secret store such as Azure Key Vault, AWS Secrets Manager or HashiCorp Vault through its configuration provider. Bound from configuration as above, the environment variable for a nested key uses double underscores: Vox__Users__InitialAdministratorPassword.
  • Development. The .NET user secrets store, dotnet user-secrets set, which keeps values outside the project directory.
  • Never. appsettings.json, or any file that is committed. Vox itself ships no secrets, no default credentials and no connection strings, and an installation should not introduce any.

Connection strings follow the same rule. Give Vox a database user with only the rights it needs: read and write on its own schema, and the rights to apply its migrations during deployment, rather than a server administrator login.

The initial administrator​

InitialAdministratorEmail and InitialAdministratorPassword have no default values. If they are not supplied, no administrator is created, which means a new installation can never be reachable through a credential that someone else could guess.

When you do use them:

  1. Supply the password from your secret store, not from a file.
  2. Sign in and change it immediately.
  3. Remove the setting from the environment once a permanent administrator exists. It is only read when there is no administrator, so leaving it configured serves no purpose and keeps a password in the environment.

Authentication​

Vox supports two modes, selected on VoxUsersOptions.

Local authentication is the default. Users sign in with an email address and a password. The admin UI receives an access token and a refresh token.

External authentication over OpenID Connect delegates sign-in to your identity provider. The admin UI performs the flow and the API validates the provider's access tokens through a bearer scheme that the host registers. Only users that have been added to Vox can sign in, so adding an account to the provider does not by itself grant access to Vox. Roles can be read from a claim on the provider's token instead of from Vox, through RoleSource and RoleClaimType.

Use external authentication where you already run an identity provider. It gives you central account lifecycle, your own multifactor policy and your own session rules, none of which Vox should be deciding for you.

Token lifetimes​

In local authentication the access token is valid for one hour and the refresh token for fourteen days. Both are configurable through AccessTokenLifetime and RefreshTokenLifetime. Shorten the refresh token if your policy requires users to re-authenticate more often; the admin UI renews the access token silently while the refresh token is valid.

How Vox stores credentials​

You do not have to configure any of this, but you may be asked about it.

  • User passwords are hashed with PBKDF2-HMAC-SHA512 at 210,000 iterations, with a 16-byte salt drawn per password from a cryptographically secure random number generator. The iteration count is stored alongside the hash, so it can be raised later without invalidating existing passwords. Verification uses a fixed-time comparison.
  • API client secrets are 256 bits of cryptographically secure random data. Only a hash is stored, and the secret is displayed once at creation and cannot be retrieved afterwards.

API clients​

Other systems authenticate with an API key rather than a user account.

  • Issue a separate API client per integrating system. One secret can then be rotated or revoked without affecting any other integration.
  • Grant each client the roles it actually needs. The permission model is the same as for users, so a client that only reads published entities should not hold write permissions.
  • Record which system holds which client. The secret cannot be read back, so an unlabelled client is one nobody dares revoke.
  • Rotate by creating a new client, moving the integration over, then deleting the old one.

The package feed​

Vox is delivered as NuGet and npm packages from Commerce Mind's package feed. The token you are given is read-only and grants nothing beyond downloading packages. Keep it as a secret in your build system, not in a committed nuget.config, and tell Commerce Mind if it may have been exposed so that it can be replaced.

Keeping current​

Vox follows semantic versioning, and every release has release notes. A release containing a security fix says so, with the severity and the affected earlier versions. Because Vox runs in your environment, you decide when to take a version; subscribe someone to the release notes so that the decision is a decision rather than an oversight.

Where a release includes a database schema migration, the release notes state whether it is reversible. Take a backup before applying one that is not, so that a rollback stays possible.

Reporting a security issue​

Send security issues to security@commercemind.se. Reports are acknowledged within two business days. Please allow a reasonable period for a fix before disclosing publicly.