Skip to content

Authentication and Authorization

Wouter van Kleunen edited this page Apr 5, 2022 · 9 revisions

Introduction to mqtt_cpp broker security

The mqtt_cpp broker allows access control to limit publish and subscribe to specific topics. Access control is configured through the auth file. The auth file can be specified through the command line of the broker or in the configuration ini file of the broker:

# Default configuration for MQTT-CPP Broker
verbose=1
certificate=server.crt.pem
private_key=server.key.pem
auth_file=auth.json

# Specify the file to use for verification
verify_file=cacert.pem

# Use common name (CN) as username field for login
verify_field=CN

The auth file is in JSON format and specified both authentication (which users are allowed to connect to the broker) and authorization (which topics each user is allowed to publish / subscribe). Finally, for convenience, users can be joined in groups and authorization can be specified on a group basis.

Special care should be taken when handling sessions, this is explained in section ClientId and session handling.

A complete example of the auth file is given here, different sections are explained below.

{
    # Configure username/login
    "authentication": [
        {
            # Authenticates user by password using sha256 hash and specified salt for password sha256(salt + password)
            "name": "u1",
            "method": "sha256",
            "salt": "salt",
            "digest": "38ea2e5e88fcd692fe177c6cada15e9b2db6e70bee0a0d6678c8d3b2a9aae2ad"
        }
        ,
        {
            # Authenticates user by client certificate
            "name": "u2",
            "method": "client_cert"
        }
        ,
        {
            # Handles all users that login without username / password
            "name": "anonymous",
            "method": "anonymous"
        }
        ,
        {
            # Handles all users that are not authenticated (non-existing user, invalid password)
            "name": "unauthenticated",
            "method": "unauthenticated"
        }
    ],

    # Combine users into groups
    "groups": [
        {
            # Users can be combined into groups, group name starts with @
            "name": "@g1",
            "members": ["u1", "u2", "anonymous", "unauthenticated"]
        }
    ],

    # Give access to topics
    "authorization": [
        {
            # Specified users and groups are denied to publish on this topic
            "topic": "#",
            "deny": { "pub": ["@g1"] }
        },
        {
            # Specified users and groups are denied to subscribe on this topic"
            "topic": "#",
            "deny": { "sub": ["@g1"] }
        },
        {
            # Specified users and groups are allowed to subscribe and publish on this topic"
            "topic": "sub/#",
            "allow": {
                "sub": ["@g1"],
                "pub": ["@g1"]
            }
        },
        {
            # Specified users and groups are denied to subscribe and publish on this topic
            "topic": "sub/topic1",
            "deny": {
                "sub": ["u1", "anonymous"],
                "pub": ["u1", "anonymous"]
            }
        },
        {
            # Allow any user to publish and subscribe to this topic
            "topic": "sub/any_user",
            "allow": {
                "sub": ["@any"],
                "pub": ["@any"]
            }
        }
    ]    
} 

Authentication of users

When a user connects to the broker, it should authorize itself. For all methods, the username of the user is stored in the 'name' field. The name of the user can be any combination of characters, but can not start with the '@' symbol (this is reserved for groups).

Based on different methods, the user can be allowed access to the broker:

  • method=sha256 Using the sha256 method, the username/password the client supplies is checked against a hash stored in the auth file.

An example configuration for using sha256 looks as follows:

{
    # Authenticates user by password using sha256 hash and specified salt for password sha256(salt + password)
    "name": "u1",
    "method": "sha256",
    "salt": "salt",
    "digest": "38ea2e5e88fcd692fe177c6cada15e9b2db6e70bee0a0d6678c8d3b2a9aae2ad"
}

A hash is calculated using the specified salt password field. This can be calculated using the openssl cli tool:

echo -n "saltmypassword" | openssl sha256 
(stdin)= 38ea2e5e88fcd692fe177c6cada15e9b2db6e70bee0a0d6678c8d3b2a9aae2ad

In this example the salt=salt, and password=mypassword, resulting in the hash=38ea2e5e88fcd692fe177c6cada15e9b2db6e70bee0a0d6678c8d3b2a9aae2ad

  • method=client_cert With this method, the clients certificate is validated. If the client supplies a valid certificate, the specified field in the broker config (verify_field) of the certificate is used as username and should match the "name" field in the authentication entry. Default this is the common name (CN). The certificate of the client is verified using the verify_file.

An example of a user validated using certificate:

{
    # Authenticates user by client certificate
    "name": "u2",
    "method": "client_cert"    
}
  • method=anonymous In case the client specifies no username/password while connecting, this method allows specifying the anonymous user.

Only a single anonymous entry in the auth file can be specified:

{
    # Handles all users that login without username / password
    "name": "anonymous",
    "method": "anonymous"
}
  • method=plain_password A plain password method is added for testing purposes, it allows validation against a password stored in plain text in the auth file. Note that is not recommended for production use! In case the auth file is leaked, passwords are breached!

Nonetheless, this method can be used as follows:

{
    # For debugging purposes a plain text can be used
    "name": "username",
    "method": "plain_password",
    "password": "secret"
}
  • method=unauthenticated Finally, to catch users not being able to login (wrong username, wrong password), a catch-all method for unauthenticated users is added.

This allows unauthenticated users to get access to certain topics as well:

{
    # Handles all users that are not authenticated (non-existing user, invalid password)
    "name": "unauthenticated",
    "method": "unauthenticated"
}

Groups

Users can be gived pub/sub access to specific topics. For convenience users can be combined into groups. A group name always starts with the '@' symbol and can be configured as follows:

"groups": [
    {
        # Users can be combined into groups, group name starts with @
        "name": "@g1",
        "members": ["u1", "u2", "anonymous", "unauthenticated"]
    }
]

An internally reserved group name is the @any user group, which matches any user logged in.

Authorization

Using the authorization rules, users and groups can be given access to publish and subscribe to topics. Topics can be specified using topic filters, and may include '+' and '#' to match multiple topics. By default a user is not allowed to publish or subscribe any topic.

Publish/subscribe access is given to users by using authorization rules. Authorization rules are applied in the order that they are given.

Authorization of publish

To give user access to publish to a certain topic, use an entry with type allow:

{
    # Specified users and groups are allowed to publish on this topic
    "topic": "#",
    "allow": { "pub": ["@g1"] }
}

To deny access to a topic, use an entry with type deny:

{
    # Specified users and groups are denied to subscribe and publish on this topic
    "topic": "sub/topic1",
    "deny": { "pub": ["u1", "anonymous"] }
}

In this example, users from group @g1 are allowed to publish any topic. Users u1 and anonymous, however, are not allowed to publish to "sub/topic1".

Authorization of subscribe

To allow users to subscribe to a specific topic, again entries with deny/allow can be specified: To give user access to subscribe to a certain topic, use an entry with type allow:

{
    # Specified users and groups are not allowed to subscribe on this topic
    "topic": "#",
    "deny": {"pub": ["@g1"] }
}
,
{
    # Specified users and groups are allowed to subscribe on this topic and topics below
    "topic": "subtopic_allow/#",
    "allow": { "pub": ["@g1"] }
}
,
{
    # Specified users and groups are not allowed to subscribe on this topic and topics below
    "topic": "subtopic_deny/#",
    "deny": { "pub": ["@g1"] }
}

In this example, users are able to subscribe to topic 'subtopic_allow/#' or other topics below. They will receive messages published to these topics. They are not allowed to subscribe to 'subtopic_deny/#' or topics below. They will not receive messages published to this topic.

important They are however allowed to subscribe to topic '#'. But they will only receive messages published to 'subtopic_allow/#' and not 'subtopic_deny/#'.

ClientId and session handling

ClientIds are used by MQTT to allow continuing sessions for a client when it reconnects. A connecting client specifies an unique clientid, or when no clientid is specified, the broker generates an unique clientid.

For security reasons, internally, the clientid of the user is combined with the username of the user. If the client specifies the clientid '574920e2-8598-11ec-a8a3-0242ac120002' and username 'user', internally the mqtt cpp broker uses the combination (user, 574920e2-8598-11ec-a8a3-0242ac120002) as the session identifier. If a different user connects to the broker with the same clientid, a new unique session is generated for this user.

Different users will not see each others sessions.

important Special care should be taken for the anonymous and unauthenticated users. These users are able to take over other users sessions, which can impose a security risk or denial of service (other session will be disconnected).