Security Hardening

Introduction

This section describes the security hardening features available with RESTHeart and some best practices to protect against common attacks.

Important
No system can guarantee 100% security. Security must be achieved through a set of security hardening measures implemented on the whole system stack and enforced by strong and efficient security policies.

Checklist

Best practice

Priority

Impact

Update the admin user password

Mandatory

Action required

Disable the root role

Suggested

Configuration required

Disable the auto-creation of the admin user

Suggested

Configuration required

Use HTTPS

Mandatory

Action required

Secure connection to MongoDB

Mandatory

Action required

Make sure password are stored in an encrypted form

Mandatory

No action required if the default authenticator is used

Enforce strong passwords

Mandatory

Configuration required

Enable bruteForceAttackGuard

Suggested

Configuration required

Enable originVetoer

Suggested

Configuration required

Define and test the ACL

Mandatory

Action required

Use only aggregations and forbid the filter query parameter

Suggested

Action required

Blacklist the $where MongoDB operator

Mandatory

No action required from 6.3.0, configuration required otherwise

Define role-specific GraphQL applications

Mandatory

Action required

Update the admin user password

At first startup, RESTHeart initializes the user admin, with the default password secret. This user can execute any request.

Warning
YOU MUST UPDATE THE DEFAULT PASSWORD! The role admin can execute any request as it is set as the root role in the mongoAclAuthorizer configuration.

To update it, run the following command:

$ curl -u admin:secret -X PATCH localhost:8080/users/admin -H "Content-Type: application/json" -d '{ "password": "my-strong-password" }'

Refer to User Management for more information on how to create new users, roles and permissions.

Note
mongoAclAuthorizer is the default ACL authorizer from RESTHeart starting v6.2. For previous releases, the fileAclAuthorizer was used. In this case the user password must be updated in the file etc/users.yml.

Disable the auto-creation of the admin user

At startup time, the mongoRealmAuthenticator checks if the user admin exists. If it does not exist, it automatically creates it. This means that whenever the user admin is deleted, it will be eventually recreated with the default password secret.

To avoid this dangerous behavior, you should disable the auto-creation of the admin user:

authenticators:
  mongoRealmAuthenticator:
    # other options omitted
    # clients with root-role can execute any request
    create-user: false # <---- disable auto-creation of the admin user
    create-user-document: '{"_id": "admin", "password": "secret", "roles": ["admin"]}'

Disable the root role

The mongoAclAuthorizer by default defines the root role.

authorizers:
  mongoAclAuthorizer:
    # other options omitted
    # clients with root-role can execute any request
    root-role: admin
Warning
this role is granted all permission, including reading and deleting any MongoDb resource.

When you have setup your ACL, the root role can be disabled as follow:

authorizers:
  mongoAclAuthorizer:
    # other options omitted
    # root role disabled!
    root-role: null

Use HTTPS

Warning
IT IS PARAMOUNT TO SECURE RESTHEART WITH TLS.

This can be achieved either by a reverse proxy (such as Nginx, AWS API gateway, etc) or by configuring RESTHeart to only use the https-listener.

To configure RESTHeart to use TLS, follow the instructions at Configure TLS.

Secure connection to MongoDB

To configure RESTHeart to connect to MongoDB over TLS, follow the instructions at Secure connection to MongoDB.

It is also very important to restrict the access to the MongoDB database to only the resources that are required by RESTHeart; for this refer to Restrict permissions of MongoDb User.

Make sure password are stored in an encrypted form

Passwords must be stored in an encrypted form.

The default mongoRealmAuthenticator is recommended for production use and it does store the password in an encrypted form with the default configuration.

mongoRealmAuthenticator:
    # omitting other options
    bcrypt-hashed-password: true
    bcrypt-complexity: 12

The fileRealmAuthenticator is for testing and development purposes only and not suggested for production use, since it does not support password hashing.

Important
If you use a custom Authenticator you must make sure that the passwords are stored in an encrypted form.

Enforce strong passwords

If users can update their password, it is important enforcing that user-defined passwords are strong enough and avoid a user to chose a weak password like 123456.

The default mongoRealmAuthenticator is recommended for production use and it allows to refuse to update user documents with weak passwords.

mongoRealmAuthenticator:
    # omitting other options
    enforce-minimum-password-strenght: false
    # Integer from 0 to 4
    # 0 Weak        (guesses < 3^10)
    # 1 Fair        (guesses < 6^10)
    # 2 Good        (guesses < 8^10)
    # 3 Strong      (guesses < 10^10)
    # 4 Very strong (guesses >= 10^10)
    minimum-password-strength: 3
Note
enforce-minimum-password-strenght is available from RESTHeart v6.3.0.

bruteForceAttackGuard

bruteForceAttackGuard defends from brute force password cracking attacks by returning 429 Too Many Requests when more than max-failed-attempts wrong requests are received in last 10 seconds from the same ip.

Note
bruteForceAttackGuard is available from RESTHeart v6.3.0.
Important
if RESTHeart is behind a revers proxy, this must set the header X-Forwarded-For with the client IP. In this case set the option trust-x-forwarded-for: true
plugins-args:
    # defends from brute force password cracking attacks
    # by returning `429 Too Many Requests` when more than
    # `max-failed-attempts` wrong requests
    # are received in last 10 seconds from the same ip
    bruteForceAttackGuard:
      enabled: false
      # if true, the source ip is obtained from X-Forwarded-For header
      # this requires that header beeing set by the proxy, dangerous otherwise
      trust-x-forwarded-for: false
      # max number of failed attempts in 10 seconds sliding window
      # before returning 429 Too Many Requests
      max-failed-attempts: 5

originVetoer

originVetoer protects from CSRF attacks by forbidding requests whose Origin header is not whitelisted

Note
this is disable by default and musts be activated by adding the following configuration with the correct whitelist to your restheart.yml file:
authorizers:
# originVetoer protects from CSRF attacks by forbidding requests whose Origin header is not whitelisted
  originVetoer:
      enabled: true
      whitelist:
        - https://restheart.org
        - https://restheart.com

Define and test the ACL

The mongoAclAuthorizer allows to define a very fine grained, role based ACL.

The permissions set must allow to execute just the required requests, blacklisting unused query parameters, projecting the response to hide sensitive data, merging the request body with sensitive properties at the server-side, filtering writes and reads.

The following permission document is an example of a very fine grained ACL:

{
    "_id": "userCanCreateDocumentsInOwnCollection",
    "description": [
        "**** DESCRIPTION PROPERTY IS NOT REQUIRED, HERE ONLY FOR DOCUMENTATION PURPOSES",
        "allow role 'user' to create documents under /{userid}",
        "the request content must contain 'title' and 'content' <- bson-request-contains(title, content)",
        "the request content cannot contain any property other than 'title' and 'content' <- bson-request-whitelist(title, content)",
        "no qparams can be specified <- qparams-whitelist()",
        "the property 'author' and 'status' are added to the request at server-side <- mergeRequest",
        "the property 'log' with some request values is added to the request at server-side <- mergeRequest"
    ],
    "roles": ["user"],
    "priority": 100,
    "predicate": "method(POST) and path-template('/{userid}') and equals(@user._id, ${userid}) and bson-request-whitelist(title, content) and bson-request-contains(title, content) and qparams-whitelist()",
    "mongo": {
      "mergeRequest": {
        "author": "@user._id",
        "status": "draft",
        "log": "@request"
      }
    }
  }

Refer to Format of permission for more information.

Note
When the permission language cannot be used and you need more control, you can define a custom Vetoer or an Request Interceptor that can enforce additional checking logic.

Use only aggregations and forbid the filter query parameter

The filter query parameter for the Mongo REST API allows clients to execute any MongoDB query.

This is very convenient at development time, however when you are ready to deploy your application, you should blacklist the filter query parameter in your ACL and rely on Aggregations to expose, well defined and secured queries.

Blacklist the $where MongoDB operator

If you cannot disable the filter query parameter, you can blacklist unused operators, using the filterOperatorsBlacklist plugin.

The $where MongoDB query operator is dangerous and should not be used in any case.

Note
filterOperatorsBlacklist is enabled by default and blacklists $where only starting from RESTHeart v6.3.0. For previous versions, it should be enabled by adding the following configuration to your restheart.yml file:
plugins-args:
    # a global blacklist for mongodb operators in filter query parameter
    filterOperatorsBlacklist:
      blacklist: [ "$where" ]
      enabled: true

Define role-specific GraphQL applications

Note
The GraphQL API is read-only, so you should only pay attention to avoid exposing sensitive information to users. This very important due to the nature of GraphQL that allows the client to request data in any format allowed by the GraphQL schema.

In order secure the GraphQL API, several GraphQL applications should be defined with different read logic and bound to different URIs. In this way, different roles can be granted access to different subsets of the GraphQL apps thus protecting the information.

Warning
Protecting the GraphQL API requires the application definitions to be defined with the correct filtering options. Always test your APIs!