FTN8: FutoIn Security Concept
Version: 0.4DV
Date: 2018-05-04
Copyright: 2014-2018 FutoIn Project (http://futoin.org)
Authors: Andrey Galkin

CHANGES

1. Intro

Security concept is required to build a unified authentication and authorization model across security domains and use cases with separate control in open environment like internet.

There is no global trusted party is allowed. Chain of trust must be fully distributed.

1.1. Sub-specifications

2. Concept

2.1. General guidelines

2.1.1. Exposed data best practices

External parties to particular system must never hold internal security-related data even if it is encrypted and/or signed to prevent theoretical falsification attacks in the future.

Notes:

2.1.2. Brute-force prevention

Any authentication and authorization functionality should be accounted for failures per exercised security resource (e.g. user password, session ID, signing key, etc.). System must forcibly disable such resource at reasonable failure count threshold to prevent the attack succeeding.

As such behavior allows easy Denial-of-Service for target resource, System must block attackers earlier than it blocks the target. Also, target identifiers must not be easily guessable (e.g. avoid sequential or derived identifiers).

All thresholds must be specific to time period like day, week, month, quarter, year.

Notes:

2.1.3. Filtering attackers

Each unsuccessful attempt to exercise security operation must be accounted per client source address and other meaningful identifiers. Such accounting should be also aggregated based on network range and/or domain to protect from distributed attacks.

However, such behavior can harm legit users. Therefore, complete blocking should be the last resort. Instead, System should take more light measures like throttling requests per minute and preliminary verification if session is run by real human. Such light measures are mandatory, if System is aware of legit user.

Notes:

2.1.4. Online verification

Online verification must always be enforced even if it complicates processing, imposes scalability issues and/or increases latency. FutoIn Security Concept is strictly against any token-based authentication where not trusted party like Client is part of communication channel between Service and AuthService.

Caching is essential to achieve performance and scalability similar to pure token-based approaches. However, such caching is allowed only if reliable cache invalidation is implemented.

2.2.5. Centralized authentication and authorization

All fully compliant Services must use AuthService for security meaningful decisions.

Such AuthService must allow a complete overview and control of granted accesses in the given Service or on behalf of the User.

2.1.6. Rotation of secrets

All secrets used in automated contexts should be rotated based on both usage time and usage count thresholds. This is a simple measures for possible leak of secrets.

Identification of secrets should also be rotated based on UUID v4 to complicate tracing of actors.

User-defined secrets like password, historical One-Time-Password solution and similar secrets are not required to be rotated.

2.1.7. Prevent indirect information exposure

Some "not important" characteristics may be used to plan attacks and/or to extract business secrets.

2.1.8. Forward Secrecy

General rules of well-known cryptography practices apply here:

2.2. Security Contexts

There are three major security contexts:

Each Service and each Client must trust only one AuthService.

The standard authorization mechanism does not allow Client code to access protected resources in another Service directly. Such feature may be implemented as sub-spec.

In addition, the following optional services exist:

2.3. Holistic pictures

2.3.1. Request Processing

It may look as too much overhead for a single request processing, but any decent system does exactly the same in fact: security processing is centralized in some application module.

FutoIn converts modules to (micro-)services by fundamental design. There should be efficient in-process calling mechanism to minimize penalties.

It is known that symmetric cryptography is much faster to process than asymmetric. Therefore, message signing is based on automatically rotated shared symmetric secrets which unique to each pair of peers. However, asymmetric cryptography is used for key rotation to ensure forward secrecy.

Client         Service        AuthService   DefenseSystem
    |             .                  .            .
    |-- request ->|                  .            .
    .             |----------- onCall() --------->|
    .             .                  .     [may fail early]
    .             |<------ defense action --------|
    .             |-- checkAuth() -->|            .
    .             |<-- auth rsp -----|            .
    .             |- checkAccess() ->|            .
    .             .           [fail on error]     .
    .       [defense action]         .            .
    .         [process]              .            .
    .             |----------- onResult() ------->|
    |<- response -|                  .            .
    |             .                  .            .

2.3.2. Service to AuthService registration

It's assumed that one of Message Authentication Code approaches used requires a preshared secret and secure updates of that.

Initial manual registration:

Operator                AuthService
    |                        .
    |--- register Service -->|
    .                 [gen initial secret]
    |<-- clear text secret --|
[manually configures Service] .
    .                        .

Initial automatic registration through secure channel, if allowed:

Service                            AuthService
   |                                   .
[gen temporary assymetric key]         .
   |----- request registration ------->|
   .                [depend on transport's MitM security]
   .                    [save request for approval]
   |<----- provide ticket ID ----------|
   .                                   .
[put ticket ID in .well-known]         |
   .                                   .
   |-- try to complete registration -->|
   .                            [ticket is not validated]
   |<-- HTTPS GET .well-known ticket --|
   |------- return ticket ID --------->|
   .                            [ticket is validated]
   |<- "pending" or "rejected" error --|
   .                                   .
[reasonable delay]               [wait approval]
   .                                   .
   |-- try to complete registration -->|
   |<- "pending" or "rejected" error --|
   .                                   .
[reasonable delay]         [operator/auto approval]
   .                                   .
   |-- try to complete registration -->|
   .                         [generate new secret]
   |<-- return new encrypted secret ---|
[decrypt secret and discard key]       .
[use new secret]                       .
   |                                   .

Shared secret secure exchange:

Service                            AuthService
   |                                   .
[gen temporary assymetric key]         .
   |----- request new secret --------->|
   .             [use current secret for MitM security]
   .                         [generate new secret]
   |<-- return new encrypted secret ---|
[decrypt secret and discard key]       .
[use new secret]                       .
   |                                   .

2.3.3. Single Sign-On (SSO)

General goal is to concentrate user authentication and access grants in single place - AuthService. See below for description of Access Request Templates.

Register authorization access request templates:

Service                           AuthService
   |                                   .
   |-------- create template --------->|
   .                 [register template of required access requests]
   |<----- return redirect details ----|
[use secret to sign & check redirects on both peers]
   |                                   .

First visit of Service ever:

Client                Service                AuthService
    |                    .                        .
    |------ visit ------>|                        .
    .      [create signed redirect from template]
    |<---- redirect -----|                        .
    |----- provide signed payload --------------->|
    .                    .                     [verify sig]
    .                    .     [drive user registration/login process]
    .                    .       [ask user for access]
    |<---- redirect user back --------------------|
    |-- signed result -->|                        .
    .              [verify sig]                   .
    .                    |-- register session --->|
    .                    |<-------- OK -----------|
    |<--- logged in -----|                        .
[use of Service]         .                        .
    .               [periodic renew]              .
    .                    |--- renew session ----->|
    .                    |<-------- OK -----------|
[use of Service]         .                        .    
    |---- logout ------->|                        .
    .                    |----- end session ----->|
    .                    |<-------- OK -----------|
    |<--- logged out ----|                        .
    |                    .                        .

Second visit of Service from known device:

Client          Service                 AuthService
    |               .                        .
    |---- visit --->|                        .
    .        [check known user]              .
    .               |--- renew session ----->|
    .               |<-------- OK -----------|
    |<- logged in --|                        .
[use of Service]    .                        .
    |               .                        .

Immediate logout:

Service                 AuthService
   |                          .
   |---- listen for events -->|
   .                          .
   .                          .
   |<--- invalidate session --|
[invalidate local session]    .
   .                          .

Foreign users (local AuthService acts as proxy):

Client          Service                 AuthService         ForeignAuthService
    .               .                        .                      .
    .               .                        |---- register self -->|
    .               .                        .                      .
    .               |-- listen for events -->|                      .
    .               .                        |- listen for events ->|
    |---- visit --->|                        .                      .
    .           [unknown user]               .                      .
    |<-- redirect --|                        .                      .
    |--- provide signed payload ------------>|                      .
    .               .                     [verify sig]              .
    .               .                [user chooses extral auth]     .
    |<----- redirect to foreign -------------|                      .
    |-------------------------- provide signed payload ------------>|
    .               .                        .                [verify sig]
    .               .                        .              [process user auth]
    |<--------- return back local AuthService ----------------------|
    |--- provide signed return ------------->|                      .
    .               .                   [verify sig]                .
    .               .                        |-- register session ->|
    .               .                        |<------ OK -----------|
    |<-- return back Service ----------------|                      .
    |-- get back -->|                        .                      .
    .          [verify sig]                  .                      .
    .               |-- register session --->|                      .
    .               |<-------- OK -----------|                      .
    |<- logged in --|                        .                      .
[use of Service]    .                        .                      .
    |               .                        .                      .

2.3.4. Access on behalf of user

On-behalf-of calls is a standard feature of FTN3.

User is a generic term for security subject. It can be a human, a specific service, a group or even some object.

Each Service registers a list of generic access descriptors it provides. Those can be granted by User to another User or Service.

Another Service creates Access Request Templates as a list of generic access descriptors it wants to ask from User. When User grants the required access, Service can call another Service on behalf of the User.

It's assumed that user has full access to own resources protected only by required security levels. User can grant resource access to another User (Service) based on Access Control descriptors.

Local user:

Client         Service1     Service2                 AuthService
    .             .             .                         .
    .             .             |- register descriptors ->|
    .             .             .                         .
    .             |-- create template ------------------->|
    .             .             .                         .
    |-- visit --->|             .                         .
    |<- redirect -|             .                         .
    |----- provide signed payload ----------------------->|
    .             .             .                   [verify sig]
    .             .             .                    [ask user]
    .             .             .                  [grant access]
    |<----- signed redirect back -------------------------|
    |- return --->|             .                         .
    .        [verify sig]       .                         .
    .             |- API call ->|                         .
    .             .     [request checking]                .
    .             .             |---- checkAuth() ------->|
    .             .             |--- checkAccess() ------>|
    .             .     [request processing]              .
    .             |<-- result --|                         .
    .             .             .                         .

Foreign user access just adds extra complexity:

  1. Both user authentication and other user/service authorization is done in foreign AuthService.
  2. User can review & control all grants in home services.
  3. Local to Service AccessControl has to consult with foreign AccessControl for access, cache it and handle revoke events similar to user sessions.
  4. AuthService acts as proxy:

2.3.5. Exceptional operation confirmation

In many cases Service needs to securely confirm some action like bank transfer approval. For that reason, Service creates special confirmation request in AuthService and redirects the user there.

Client               Service                 AuthService
    |                   .                         .
    |- request action ->|                         .
    .                   |- prepare confirmation ->|
    .                   .               [store with timeout]
    .                   |<-- provide URL ---------|
    |<---- redirect ----|                         .
    |------ use the AuthService URL ------------->|
    .                   .            [ask user confirmation]
    .                   .                  [store result]
    |<---- signed redirect back ------------------|
    |-- return -------->|                         .
    .               [verify sig]                  .
    .                   |-- verify confirmation ->|
    .                   |<----- OK ---------------|
    .            [complete action]                .
    .                   .                         .

2.4. AuthService and scope of identification

The scope of AuthService is arbitrary - it is formed by AuthService itself. However, AuthService should have a single domain name which is used as global scope identifier.

2.5. User identification

Each user has a unique local ID and global ID.

Local ID is arbitrary and assigned by AuthService. Base64-coded UUID without padding is suggested.

Global ID is based on local ID and scope name of home AuthService. Typically, email address is the global identifier for users and fully qualified domain name is the global identifier for services.

2.6. Foreign users

If the Service or Client trust different AuthServices then it is responsibility of AuthService to establish communication to another AuthService. In such case, Client is called foreign AuthService user or simply foreign user.

Foreign users are detected based on mismatch of associated global ID scope name and current AuthService scope name.

Theoretically, AuthService can auto-discover and establish registration to any other foreign AuthService. However, it may be undesired from security point of view. So, only whitelisted foreign AuthServices should be allowed. Whitelist can be either local or global in form of association of AuthService providers.

2.7. Service to Service interaction (and Service to AuthService in particular)

Each Service as logical entity is assumed to have own user ID in scope of the related AuthService. Therefore, communication authentication follows the same pattern as Client-to-Service pattern.

2.8. Authentication assumptions

Client-to-Service and Service-to-Service communication has different natural aspects:

Therefore, specification is separated for Client(human) and Service(software) cases.

2.9. Interoperation with non-compliant AuthService scopes

There are many OpenID, OAuth, SAML and other single sign-on alternatives. Particular AuthService may easily integrate with those.

2.10. Service & AuthService in single instance

It's assumed that each Service is accompanied by unified AuthService logic to efficiently process requests on scale. Such AuthService can be either full-featured or limited to support of only foreign users.

2.11. General MAC generation requirements

MAC stays for Message Authentication Code which helps to ensure message integrity.

2.11.1 Rules of MAC payload generation

Note 1: MAC logic must be abstract of JSON as far as possible to be efficiently used in other message coding methods.

Note 2: research to be done to support TupleHash and non-JSON representation of fields as an option.

Note 3: for performance and simplification a special FutoIn interface to be created for message-in-signed-message-field approach.

2.11.2. Predefined MAC algorithms

Executor may refuse to support any MAC algo and throw SecurityError.

Note: current suggested default is HS256

2.11.3. Response "sec" field with MAC

If request comes signed with any MAC then response must also be signed with MAC using exactly the same secret key and MAC algorithm.

Invoker must validate response "sec" field and fail on mismatch or absence of one.

2.11.4. Key derivation strategies

It is called "Strategy", but not "Function" on purpose as the same KDF may be used different ways.

2.11.4.1. General derived key ID

Derived Key ID must be transmitted as Base64 encoding string without padding. Key ID or salt should be of recommended size, if applicable.

Based on this strategy, no key ID or a fixed minimal derived key ID may be used for current version of the specification to minimize performance impact and simplify Executor's derived key caching logic. Master Secret itself should provide enough entropy to ensure derived key's quality. So, key update gets bound to frequency of Master Secret update. Key derivation would be used only to get different keys based on its purpose.

2.11.4.2. Key purpose name

Below is a list of ASCII values to use for altering key derivation logic.

2.11.4.3. Performance tradeoff

In most cases, it's not feasible to generate a new derived key for every message, so Invoker should be able to reuse the key at own discretion.

As a defensive measure, Executor peer is allowed to reject requests, if derived key either changes too often or used for too long. Executor should cache derived keys for reasonable time, but still prevent their leaking outside. Executor should consider that Invoker may be clustered with unique derived key at every node.

Executor can be configured to support only certain types of strategies named below and to reject requests with "SecurityError" on mismatch.

2.11.4.4. Key derivation strategies names

  1. HKDF256 - HKDF with SHA-256
  2. HKDF512 - HKDF with SHA-512

2.11.4.5. HKDF notes

[HKDF][] is well-known modern method which has the following parameters: hash function, salt and info.

2.12. Security Levels

It's important to understand characteristics of performed user authentication in many cases.

2.13. Client fingerprints

The following fingerpints are essential to enforce extra level of protection.

2.14. Authentication rejection limits and blocking

The specification suggests the following limits to be enforced. Hit of the limits must block any processing. The blocking must be done for entire period of enforcement.

Advanced System should have more light protection measures first to protect legit user access.

2.15. Manage service events

3. Interface

3.1. Common types

{
    "iface" : "futoin.auth.types",
    "version" : "{ver}",
    "ftn3rev" : "1.9",
    "imports" : [
        "futoin.types:1.0"
    ],
    "types" : {
        "LocalUserID" : "UUIDB64",
        "LocalUser" : {
            "type" : "string",
            "regex" : "^[a-zA-Z]([a-zA-Z0-9_.-]{0,30}[a-zA-Z0-9])?$"
        },
        "LocalService" : "LocalUser",
        "GlobalService" : {
            "type" : "Domain",
            "maxlen" : 128
        },
        "GlobalUser" : {
            "type" : "Email",
            "maxlen" : 128
        },
        "GlobalUserID" : [ "GlobalUser", "GlobalService" ],
        "DomainList" : {
            "type" : "array",
            "elemtype" : "Domain",
            "minlen" : 1
        },
        "MACAlgo" : {
            "type" : "enum",
            "items" : [
                "HMD5",
                "HS256",
                "HS384",
                "HS512",
                "KMAC128",
                "KMAC256"
            ]
        },
        "Password" : {
            "type" : "string",
            "minlen" : 8,
            "maxlen" : 32
        },
        "PasswordLength" : {
            "type" : "integer",
            "min" : 8,
            "max" : 32
        },
        "KeyBits" : {
            "type" : "enum",
            "items" : [256, 512]
        },
        "MACKey" : {
            "type": "Base64",
            "minlen" : 42,
            "maxlen" : 87
        },
        "StatelessSecret": [ "Password", "MACKey" ],
        "MACValue" : {
            "type" : "Base64",
            "minlen" : 1,
            "maxlen" : 128
        },
        "MACBase" : {
            "type" : "data",
            "minlen" : 8
        },
        "MasterSecretID" : "UUIDB64",
        "MasterScope" : "Domain",
        "KeyDerivationStrategy" : {
            "type" : "enum",
            "items" : [
                "HKDF256",
                "HKDF512"
            ]
        },
        "KeyPurpose" : {
            "type" : "enum",
            "items" : [
                "MAC",
                "ENC",
                "EXPOSED"
            ]
        },
        "ExchangeKeyType" : {
            "type" : "enum",
            "items" : [
                "RSA",
                "X25519",
                "X448"
            ]
        },
        "ExchangeKey" : {
            "type" : "Base64",
            "minlen" : 1,
            "maxlen" : 20000
        },
        "EncryptedKey" : {
            "type" : "Base64",
            "minlen" : 1,
            "maxlen" : 1000
        },
        "EncryptedMasterSecret" : "EncryptedKey",
        "UserAgent" : {
            "type" : "string",
            "maxlen" : 256
        },
        "X509Cert" : {
            "type" : "Base64",
            "maxlen" : 20000
        },
        "SSHPubKey" : {
            "type" : "string",
            "maxlen" : 1000
        },
        "ClientToken" : {
            "type" : "Base64",
            "maxlen" : 342,
            "desc" : "Unique per Service per Client device persistent token"
        },
        "ClientFingerprints" : {
            "type" : "map",
            "fields" : {
                "user_agent" : {
                    "type" : "UserAgent",
                    "optional" : true
                },
                "source_ip" : {
                    "type" : "IPAddress",
                    "optional" : true
                },
                "x509" : {
                    "type" : "X509Cert",
                    "optional" : true
                },
                "ssh_pubkey" : {
                    "type" : "SSHPubKey",
                    "optional" : true
                },
                "client_token" : {
                    "type" : "ClientToken",
                    "optional" : true
                },
                "misc" : {
                    "type" : "map",
                    "optional" : true
                }
            }
        },
        "AuthInfo" : {
            "type" : "map",
            "fields" : {
                "local_id" : "LocalUserID",
                "global_id" : "GlobalUserID"
            }
        },
        "RedirectURL" : {
            "type" : "string",
            "regex" : "^https?://[a-z0-9-]+(\\.[a-z0-9-]+)*\\.[a-z]{2,}/[a-zA-Z0-9_/-]*(\\?[a-zA-Z][a-zA-Z0-9]*=)?$",
            "maxlen" : 128
        },
        "ResourceURL" : {
            "type" : "string",
            "regex" : "^https?://[a-z0-9-]+(\\.[a-z0-9-]+)*\\.[a-z]{2,}/[a-zA-Z0-9_/?=%&;.-]*$",
            "maxlen" : 128
        },
        "ParamConstraint" : {
            "type" : "map",
            "elemtype" : "array"
        },
        "AccessControlDescriptor" : {
            "type" : "map",
            "fields" : {
                "iface" : {
                    "type" : "FTNFace",
                    "optional" : true
                },
                "ver" : {
                    "type" : "FTNVersion",
                    "optional" : true
                },
                "func" : {
                    "type" : "FTNFunction",
                    "optional" : true
                },
                "params" : {
                    "type" : "ParamConstraint",
                    "optional" : true,
                    "desc": "Named paramater must match one of the values"
                }
            },
            "desc" : "Granted API access constraints in scope of arbitrary Service"
        },
        "AccessControlDescriptorList" : {
            "type" : "array",
            "elemtype" : "AccessControlDescriptor",
            "desc" : "List of granted API access in scope of arbitrary Service"
        },
        "AccessGroupName" : {
            "type" : "GenericIdentifier",
            "maxlen" : 32,
            "desc" : "Service-specific arbitrary ACD grouping identifier"
        },
        "AccessGroup" : {
            "type" : "map",
            "fields" : {
                "id" : "AccessGroupName",
                "name" : "ItemTranslations",
                "desc" : "ItemTranslations",
                "acds" : "AccessControlDescriptorList",
                "icon" : "ResourceURL"
            },
            "desc" : "Services-specific arbitrary ACD grouping definition"
        },
        "AccessGroupList" : {
            "type" : "array",
            "elemtype" : "ServiceAccessGroup",
            "desc" : "List of Service-specific ACD groupings"
        },
        "ServiceAccessGroup" : {
            "type" : "map",
            "fields" : {
                "service" : "GlobalService",
                "access_group" : "AccessGroupName"
            },
            "desc" : "Global pointer to ACD group of specific Service"
        },
        "ServiceAccessGroupList" : {
            "type" : "array",
            "elemtype" : "ServiceAccessGroup",
            "desc" : "List of global pointers to Service-specific ACD groups"
        }
    }
}

3.2. AuthService management

{
    "iface" : "futoin.auth.manage",
    "version" : "{ver}",
    "ftn3rev" : "1.9",
    "imports" : [
        "futoin.ping:1.0",
        "futoin.auth.types:{ver}"
    ],
    "funcs" : {
        "setup" : {
            "params" : {
                "domains" : "DomainList",
                "clear_auth" : {
                    "type" : "boolean",
                    "default" : null
                },
                "mac_auth" : {
                    "type" : "boolean",
                    "default" : null
                },
                "master_auth" : {
                    "type" : "boolean",
                    "default" : null
                },
                "master_auto_reg" : {
                    "type" : "boolean",
                    "default" : null
                },
                "auth_service" : {
                    "type" : "boolean",
                    "default" : null
                },
                "password_len" : {
                    "type" : "PasswordLength",
                    "default" : null
                },
                "key_bits" : {
                    "type" : "KeyBits",
                    "default" : null
                },
                "def_user_ms_max" : {
                    "type" : "NotNegativeInteger",
                    "default" : null
                },
                "def_service_ms_max" : {
                    "type" : "NotNegativeInteger",
                    "default" : null
                }
            },
            "result" : "boolean",
            "seclvl" : "System"
        },
        "genConfig" : {
            "result" : {
                "domains" : "DomainList",
                "clear_auth" : "boolean",
                "mac_auth" : "boolean",
                "master_auth" : "boolean",
                "master_auto_reg" : "boolean",
                "auth_service" : "boolean",
                "password_len" : "PasswordLength",
                "key_bits" : "KeyBits",
                "def_user_ms_max" : "NotNegativeInteger",
                "def_service_ms_max" : "NotNegativeInteger"
            },
            "seclvl" : "System"
        },
        "ensureUser" : {
            "params" : {
                "user" : "LocalUser",
                "domain" : "GlobalService"
            },
            "result" : "LocalUserID",
            "seclvl" : "System"
        },
        "ensureService" : {
            "params" : {
                "hostname" : "LocalService",
                "domain" : "GlobalService"
            },
            "result" : "LocalUserID",
            "seclvl" : "System"
        },
        "getUserInfo" : {
            "params" : {
                "local_id" : "LocalUserID"
            },
            "result" : {
                "local_id" : "LocalUserID",
                "global_id" : "GlobalUserID",
                "is_local" : "boolean",
                "is_enabled" : "boolean",
                "is_service" : "boolean",
                "ms_max" : "NotNegativeInteger",
                "ds_max" : "NotNegativeInteger",
                "created" : "Timestamp",
                "updated" : "Timestamp"
            },
            "throws" : [
                "UnknownUser"
            ],
            "seclvl" : "System"
        },
        "setUserInfo" : {
            "params" : {
                "local_id" : "LocalUserID",
                "is_enabled" : {
                    "type": "boolean",
                    "default": null
                },
                "ms_max" : {
                    "type": "NotNegativeInteger",
                    "default": null
                },
                "ds_max" : {
                    "type": "NotNegativeInteger",
                    "default": null
                }
            },
            "result" : "boolean",
            "throws" : [
                "UnknownUser"
            ],
            "seclvl" : "System"
        }
    },
    "requires" : [
        "SecureChannel",
        "MessageSignature"
    ]
}

=END OF SPEC=