FTN8: FutoIn Security Concept Version: 0.2DV Date: 2017-12-29 Copyright: 2014-2018 FutoIn Project (http://futoin.org) Authors: Andrey Galkin
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.
There are three major security contexts:
Each Service and each Client must trust only one AuthService.
The standard auth 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:
It may look as too much overhead for a single request processing, but any decent system does exactly the same in fact. However, instead of modules are used instead of separate microservices. FutoIn converts modules to services by fundamental design. There should be efficient in-process calling mechanism to minimize penalties.
Client Service AuthService DefenseService
| . . .
|-- request ->| . .
. |----------- onCall() --------->|
. . . [may fail early]
. |<------ defense action --------|
. |-- checkAuth() -->| .
. |<-- auth rsp -----| .
. |- checkAccess() ->| .
. . [fail on error] .
. [defense action] . .
. [process] . .
. |----------- onResult() ------->|
|<- response -| . .
| . . .
It's assumed that one of Message Authentication Code approaches are used which require a preshared secret and secure updates of that.
Initial manual registration:
Operator AuthService
| .
|--- register Service -->|
. [gen initial secret]
|<-- clear text secret --|
[manually configure 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] .
| .
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] . . .
| . . .
On-behalf-of calls is standard feature of FTN3.
Each Service registers a list of generic access descriptors it provides which can be granted by user to another user(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 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 or 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:
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] .
. . .
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.
Each user has a unique local ID and global ID.
Local ID is arbitrary and assigned by AuthService.
Global ID based on local ID and scope name of home AuthService. Typically, email address is the global identifier.
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.
Each Service as logical entity assumes to have own user ID in scope of related AuthService. Therefore, communication authentication follows the same pattern as Client-to-Service pattern.
Client-to-Service and Service-to-Service communication has different natural aspects:
Therefore, specification is separated for Client(human) and Service(software) cases.
There are many OpenID, OAuth, SAML and other single sign-on alternatives. Particular AuthService may easily integrate with those.
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.
MAC stays for Message Authentication Code which prevents decryptable transmission of authentication secrets and helps to ensure message integrity.
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.
Executor may refuse to support any MAC algo and throw SecurityError.
HMAC-MD5
- HMAC MD5 128-bit (acceptably secure, even though MD5 itself is weak)HMAC-SHA-256
- SHA v2 256-bit (acceptably secure)HMAC-SHA-512
- SHA v2 512-bit (acceptably secure)HMAC-SHA-512-256
- SHA v2 512-bit truncated to 256-bit (acceptably secure)HMAC-SHA3-256
- SHA v3 256-bit (high secure at the moment)HMAC-SHA3-512
- SHA v3 512-bit (high secure at the moment)HMAC-SHA3-512-256
- SHA v3 512-bit truncated to 256-bit (acceptably secure)KMAC128
- Keccak MAC 128-bit (high secure at the moment)KMAC256
- Keccak MAC 256-bit (high secure at the moment)Note: current suggested default is either HMAC-SHA-256
or HMAC-SHA-512-256
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.
It is called "Strategy", but not "Function" on purpose as the same KDF may be used different ways.
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 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 purpose.
Below is list of ASCII values to use for altering key derivation logic.
MAC
- for message signing.ENC
- for general encryption.EXPOSED
- for signature generating which definitely goes through untrusted exposed
channel (e.g. user's web browser).In most cases, it's not feasibile 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.
HKDF0
- use [HKDF][] with per GlobalServiceID as "salt" and
purpose name for "info" to derive unique keys per purpose from shared Master Secret.MAC
and EXPOSED
purposeHKDF
- use [HKDF][] with UUID for "salt" and purpose name for "info" to
derive unique keys per purpose from shared Master Secret.ENC
purposeIt's important to understand characteristics of performed user authentication in many cases.
Anonymous
- placeholder for not authenticated userInfo
- read-only access to private informationSafeOps
- Info + access to operation, which should not seriously compromise the systemPrivilegedOps
- SafeOps + access to operations, which may compromise the system. Requires SecureChannelExceptionalOps
- PrivilegedOps + access to very sensitive operations, like password changeSystem
- internal calls inside the same Service (can be cross-process)The following fingerpints are essential to enforce extra level of protection.
user_agent
- refers to HTTP 'User-Agent' or similarsource_ip
- refers ot IPv4/IPv6 source address of Clientx509
- X.509 certificate, if provided by clientssh_pubkey
- SSH public key, if provided by clientclient_token
- Service-specific identification of the Client devicemisc
- implementation-defined mapflavour
- implementation typeThe 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.
{
"iface" : "futoin.auth.types",
"version" : "{ver}",
"ftn3rev" : "1.8",
"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])?$"
},
"GlobalService" : "Domain",
"GlobalUserID" : [ "Email", "GlobalService" ],
"MACAlgo" : {
"type" : "enum",
"items" : [
"HMAC-MD5",
"HMAC-SHA-224",
"HMAC-SHA-256",
"HMAC-SHA-384",
"HMAC-SHA-512",
"HMAC-SHA3-224",
"HMAC-SHA3-256",
"HMAC-SHA3-384",
"HMAC-SHA3-512",
"KMAC128",
"KMAC256"
]
},
"StatelessSecret" : {
"type" : "string",
"minlen" : 8,
"maxlen" : 32
},
"MACValue" : {
"type" : "Base64",
"minlen" : 1,
"maxlen" : 128
},
"MACBase" : {
"type" : "string",
"minlen" : 8
},
"MasterSecretID" : "UUIDB64",
"MasterScope" : "Domain",
"KeyDerivationStrategy" : {
"type" : "enum",
"items" : [
"HKDF",
"HKDF0"
]
},
"KeyPurpose" : {
"type" : "enum",
"items" : [
"MAC",
"ENC",
"EXPOSED"
]
},
"ExchangeKeyType" : {
"type" : "enum",
"items" : [
"RSAE-2048",
"RSAE-4096",
"ECDHE-Curve25519",
"ECDHE-Curve448"
]
},
"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-Z0-9_/-]*(\\?[a-Z][a-Z0-9]*=)?$",
"maxlen" : 128
},
"ResourceURL" : {
"type" : "string",
"regex" : "^https?://[a-z0-9-]+(\\.[a-z0-9-]+)*\\.[a-z]{2,}/[a-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"
}
}
}
{
"iface" : "futoin.auth.manage",
"version" : "{ver}",
"ftn3rev" : "1.8",
"imports" : [
"futoin.ping:1.0",
"futoin.auth.types:{ver}"
],
"funcs" : {
"setup" : {
"params" : {
"domain" : "Domain",
"clear_auth" : {
"type" : "boolean",
"default" : false
},
"mac_auth" : {
"type" : "boolean",
"default" : true
},
"master_auth" : {
"type" : "boolean",
"default" : true
},
"master_auto_reg" : {
"type" : "boolean",
"default" : false
},
"auth_service" : {
"type" : "boolean",
"default" : false
}
},
"result" : "boolean"
},
"genConfig" : {
"result" : {
"domain" : "Domain",
"clear_auth" : "boolean",
"mac_auth" : "boolean",
"master_auth" : "boolean",
"master_auto_reg" : "boolean"
}
},
"ensureUser" : {
"params" : {
"user" : "LocalUser",
"global_id" : {
"type": "GlobalUserID",
"default" : null,
"desc" : "Defaults to auto-generated"
},
"hostname" : {
"type" : "Domain",
"default" : null,
"desc" : "Required for Service users"
}
},
"result" : "LocalUserID",
"throws" : [
"GlobalUserIDMismatch"
]
},
"ensureService" : {
"params" : {
"hostname" : {
"type" : "GlobalService",
"default" : null,
"desc" : "Act as both local and global user name"
}
},
"result" : "LocalUserID"
}
},
"requires" : [
"SecureChannel",
"MessageSignature"
]
}
=END OF SPEC=