User Authentication

  • tags:
  • wip
  • incomplete

This case study looks at providing a useful set of interfaces (with a reference implementation) for authenticating users against a server for resource access.

Architecture

Fig 1: User Authentication Architecture

Attribute-Based Access Control (ABAC)

Capabilites

A capability is a base action that can performed on the system. Such as reading a file. For this example we can use a limited set such as read, write, create, and destroy. But the choice is down to the implementation and the environment that it needs to work within.

For security purposes these capabilities should be decided before the implementation and then enforced via hard coding in software.

Policies

A policy is a set of boolean logic statements that provide the checking mechanisim for authentication. Policies should be cascading so that default statements can be mandated early. Further overriding of these defaults can thus be via specific policies. An example would be mandating all resources as strictly non-readable; then allowing very specific users to read those resources within a specific environment or contextural situation.

Attributes

An attribute can be anything - in our design this is limited to textural information - it can take the form of a simple string tag, a key=value pair, or a blob of json data for instance. All attributes can be used in policy statements.

The attribute forms we will make use of are tag, keyvalue, and ternary.

tag is of a simple textural symbol, if it is present then it exists. keyvalue presents a pair of textural key and value, whilst ternary allows for setting a value as true, unknown, or false. We might implement these forms in golang like thus :-

Raw go file

/* Attributes */
type Attributes map[string]string

/* SetTag */
func (attrs Attributes) SetTag(tag string) {
  if tags,exists := attrs["tags"]; !exists {
    attrs["tags"] = tag		
  } else {
    attrs["tags"] = strings.Join([]string{tags,tag}," ")
  }
}

/* Tag */
func (attrs Attributes) Tag(tag string) bool {
  if tags,exists := attrs["tags"]; exists {
    for _,t := range strings.Split(tags," ") {
      if t == tag {
        return true
      }
    }
  }
  return false
}

/* SetKeyValue */
func (attrs Attributes) SetKeyValue(key,value string) {
  attrs[key] = value
}

/* KeyValue */
func (attrs Attributes) KeyValue(key, def string) string {
  if value,exists := attrs[key]; exists {
    return value
  }
  return def
}

type Ternary string

const (
  True    = Ternary("ternary'true")
  Unknown = Ternary("ternary'unknown")
  False   = Ternary("ternary'false")
)

/* SetTernary */
func (attrs Attributes) SetTernary(key string, value Ternary) {
  attrs[key] = string(ternary)
}

/* Ternary */
func (attrs Attributes) Ternary(key string) Ternary {
  if value,exists := attrs[key]; exists {
    switch Ternary(value) {
      case True,Unknown,False:
       return Ternary(value)
      break
      default:
        return True  
      break
    }
  }
  return Unknown
}

/* Form */
func (attrs Attributes) Form(key string) string {
  if value,exists := attrs[key]; exists {
    switch Ternary(value) {
      case True,Unknown,False:
        return "ternary"
      break
      default:
        return "keyvalue"
      break
    }
  } 
  for _,tag := range strings.Split(attrs["tags"]," ") {
   if tag == key {
     return "tag"
   }
  }
  return "unknown"
}




For this design we will allow for four types: environment, subject, action, and resource. In this way we can also map this design to a role-based access control (RBAC), which allows for more stream-lined client-server interfaces; as well as an easier concept to share with operators.

Environment (as a special attribute)

In this design we will use the environment (which could be a general attribute) as a box inwhich to match policy statements. In this way we can make certain aspects of the design simplier then if we presented the environment as merely another type of attribute. An example would be two different environments that share the same set of policies, for instance workfloor and control. Policies can now be placed into four groups:

policies that have only workfloor environment, that have only control environment, that have both workfloor and control environments, and policies that have neither environnment.

TODO: better set theory here please, probably a diagram.

Subject

This type of attribute refers to the properties of a known user, human or machine operator. This fulfils the capability of classic authentication by allowing a policy statement that includes information of one or more user attributes.

Action

This type of attribute that describe the attempted action.

Resource

This type of attribute describes the accessed object.

Domain Specific Language (DSL)

Examples

Read-Only Example

Raw policy file

policy read-write
  ## drop all capabilities
  drop (cap)
  ## allow read and write capabilies when all 
  ## other statements match
  allow (cap READ) and (cap WRITE)
  action is file-access

policy read-only-logs 
  drop (cap)
  allow (cap READ)
  action is file-access of type log


The policy syntax is formalised on parsing into a symbolic-expression set. The following is the formalised version of our example.

Raw s-expression file

(policy read-write (
  (if (must (
        (equal (attr action "operation") "file-access")))
      (then (
        (drop (cap))
        (grant (
          (cap READ)
          (cap WRITE)))))))) 

(policy read-only-logs (
  (if (must (
        (equal (attr action "operation") "file-access")
        (equal (attr resource "type") "log")))
      (then (
        (drop (cap))
        (grant (
          (cap READ))))))))


You will notice that the formalisation arranges the policy into an if and then statement, this allows the implementation to be direct in it’s useage.

Write-Only Example

Raw policy file

policy write-only
  drop (cap)
  allow (cap WRITE)
  action is file-access
  subject must have attribute "logger"
  subject must not have attribute "public"

test write-only
  as correct
    with action as file-access
    with empty subject 
      apply attribute as tag "logger"
    so only (cap WRITE)
    if failure then break circuit

  as incorrect
    with action as file-access
    with empty subject 
      apply attribute as tag "logger"
      apply attribute as tag "public"
    so not (cap)
    if failure then break circuit

Cascading Example

Raw policy file

# enforce default to drop all permissions
default policy 
  drop (cap)

  test 
    so not (cap)
    if failure then break circuit

# allow read only permission for file-access actions
policy read-only
  ## this statement on it's own will drop all 
  ## permissions then allow read
  allow only (cap READ)
  action is file-access

  test 
    with action as file-access
    so only (cap READ)
    if failure then break circuit

# the following policies also set the environment
# that they apply to, in this case it's the office
policy write-for-staff in office
  allow (cap WRITE)
  ## where is used as syntax-sugar here
  ## to make the children statements easier 
  ## to grok.
  where
    action is file-access
    subject must have attribute "staff"

  test 
    as correct
      with 
        action as file-access
        empty subject apply attribute as tag "staff"
      so (cap READ) and (cap WRITE)
      if failure then break circuit

    as incorrect
      with
        action as file-access
        empty subject
      so (cap READ)
      so not (cap WRITE)
      if failure then break circuit

policy create-for-admin in office
  allow (cap CREATE)
  where
    subject must have
      attribute "staff"
      attribute "admin"
    ## when a user creates a resource we add 
    ## an attribute to it as a specific keyvalue
    ## type. Read the colon symbol as where
  apply attribute to resource as keyvalue :
    key is "glenda-can-delete" 
    ### we set the value from the environment
    ### and enforce the type as ternary value
    ### : true, unknown, or false. If set 
    ### and contents considered truthy then 
    ### value will be true, else false. If 
    ### not set then the value will be unknown
    value is ternary (env GLENDA_ALLOWED)

  test 
    as correct
      with 
        empty subject 
          apply attribute
             as tag "staff"
             as tag "admin"
      so (cap CREATE)
      so resouce has attribute "glenda-can-delete"
        with ternary value 
      if failure then break circuit

     as incorrect
       with empty subject 
       so not (cap CREATE)
       if failure then break circuit 

policy destroy-only-for-glenda in remote-office
  allow (cap DESTROY)
  where
    resource must have attribute "glenda-can-delete"
      value must be true
    subject must have 
      attribute "staff"
      attribute "username"
        value is "glenda"
  test
    as correct
      with 
        empty resource 
          apply attribute as ternary "glenda-can-delete" 
            with value true 
        empty subject 
          apply attribute as tag "staff"
          apply attribute as keyvalue :
            key is "username" 
            value is "glenda"
      so (cap DESTROY)
      if failure then break circuit




Full Example

We combine all policy examples and show some useful premable configuration to make the policy more human readable and to prevent unforseen errors.

Full


# this is an example of a complete policy 
# configuration

# set the default policy 
default policy
  drop (cap)

# here we create some useful tokens in 
# place of the capability default set.

set token as keyvalue :
  key is read
  value is (cap READ)

set token as keyvalue :
  key is write
  value is (cap WRITE)

set token as keyvalue :
  key is create
  value is (cap CREATE)

set token as keyvalue :
  key is destroy
  value is (cap DESTROY)

set token as keyvalue :
  key is execute
  value …
Raw s-expression file

(policy default (then (drop (cap))))
 
(token (keyvalue read (cap READ)))
(token (keyvalue write (cap WRITE)))
(token (keyvalue create (cap CREATE)))
(token (keyvalue destroy (cap DESTROY)))
(token (keyvalue execute (cap EXECUTE)))
(token (keyvalue all (
                      (read)
                      (write)
                      (create)
                      (destroy)
                      (execute))))

(berid (cap))

(policy read-only (
  (if (must (
        (equal (attr action "operation") "file-access")))
      (then (grant read))
      (else (audit warning)))))
        
(policy write-for-staff (
  (if (must (
        (equal (attr enviroment "location") "office")
        (equal (attr action "operation") "file-access")
        (has (attr subject "staff"))))
      (then (grant write))
      (else (audit warning)))))

(policy create-for-admin (
  (if (must (
        (equal (attr environment "location") "office")
        (has (
               (attr subject "staff")
               (attr subject "admin")))))
      (then (
             (grant (create))
             (resource 
               (ternary 
                "glenda-can-delete" 
                (env GLENDA_ALLOWED)))))
      (else (audit warning)))))  


(policy destroy-only-for-glenda (
  (if (must ( 
         (equal (attr environment "location") "remote-office")
         (equal (attr resource "glenda-can-delete") true)
         (has   (attr subject "staff"))
         (equal (attr subject "username") "glenda")))
       (then (grant destroy))
       (else (audit warning)))))


(policy execute-processes (
  (if (must (
        (equal (attr environment "location") "office")
        (within 
          (attr envirnoment "time") 
          (time 08 00) 
          (time 18 00))
        (at-least-1 ( 
                      (has (attr action "begin-process"))
                      (has (attr action "end-process"))))
        (has (attr resource "can-execute"))
        (has (attr subject "staff")
             (attr subject "operations"))))
      (then (grant execute))
      (else (audit warning)))))
 
(policy backup (
  (if (must (
        (equal (attr enviroment "location") "office")
        (within
          (attr environment "time")
          (time 01 00)
          (time 03 00))
        (at-least-1 (  
          (equal (attr environment "weekday") "Sunday")
          (eqaul (attr environment "weekday") "Tuesday")))
        (has (attr action "begin-backup"))
        (has (attr resource "dump"))
        (has (attr subject "operations"))
        (has (attr subject "backups"))))
     (then (
       (grant read)
       (audit log "running backup"))
     (else (audit warning)))))

(policy root (
  (if (must (
        (equal (attr environment "location") "office")
        (has (attr subject "root"))))
      (then (grant all)))))
  

Authentication Interface

The authentication interface provides a limited (read small attack surface) API for doing specific actions. This interface is designed to be as simple as possible. A combination of finite state machines and ABAC security for authenticating users with the service.

Generating a token

The authentication interface generates bearer tokens for a client to make use of.

Timestamping data with auth

We can use the authentication interface to generate a timestamped banner with provided user data that is verified and authenticated. This is useful if we want to pass around verified data amongst users.

Admin Interface

An admin interface is needed to oversee the authentication datastore and setup required policies. We can implement this through the same API using a special environment to allow policy modification.

Audit

An audit of all interactions with the authentication interface is required to ensure correct state.

Circuit Breakers

A circuit breaker is useful to ensure correct state and to prevent use whilst in an incorrect (read unsafe) state. With our design we can place a number of circuit breakers in place.

TODO: insert circuit breaker diagram here

Finite State Machine Processes

We make use of finite state machines to describe processes and required interactions that an operator must follow to gain a particular outcome, for example - a user logging in.

Domain Specific Language (DSL)

To ensure well-defined and correct configuration, we make use of a tiny domain specific language for constructing our required state machines. You will notice that it has the same syntax and structure as the DSL for ABAC (above).

Examples

New User Example

This example covers the basics of adding a new user account. We begin at user-unknown as we have no information about the user and this is the default state. To transition from user-unknown to user-account the user must go through the user-new-account process, this process requires an email, username, and passphrase to produce a valid username attribute.

Raw policy file

interface
  # beginning state
  state user-unknown
    begin here
    subject must not have attribute "username"

  # end state
  state user-account
    subject must have attribute "username"

  # process that needs to be implemented
  process user-new-account
    required as input "email", "username", and "passphrase"
    produces attribute user "username"  

  # transition between beginning and end states 
  transition
    from user-unknown
    to user-account
    via user-new-account


User login example

This is a simple login model between two states of session management - has a session or doesn’t. If the user does not have a session then they are logged out (the default state). To change between logged out and logged in, the user must go through the user-authentication process and provide a username and passphrase.

Raw policy file

interface
  # beginning state
  state user-logged-out
    begin here
    subject must not have token "session"

  state user-logged-in
    subject must have token "session" and be valid

  process user-authentication
    required as input "username" and "passphrase"
    produces token "session"

  transition 
    from user-logged-out 
    to user-logged-in
    via user-authentication

  transition
    from user-logged-in
    to user-logged-out
  

User Attribute Change (passphrase)

A common action for a user is to be able to change their passphrase of their account. In this example we explictly ensure that the logged user has write capabilities for the resource attribute passphrase, if not then the process can never be executed. Also to prevent any mistakes if the user-change-passphrase process errors in any manor any changes will be reverted back.

This is an extermely simple version of a passphrase changing action; generally we would ask for the new passphrase in pairs to ensure correct typing from the user and we would want the current passphrase as well to prevent mistakes.

Raw policy file

interface
  # we only have a single state
  state user-logged-in
    subject must have token "session" and be valid

  process user-change-passphrase
    required as input "new-passphrase"
    subject has (cap write) on attribute "passphrase"

  transition 
    from user-logged-in
    to user-logged-in
    via user-change-passphrase
    on error revert changes

Reference Implementation

Here we place an interactive mithril javascript widget (in the form of cards) that show a working create, auth, login, logout, destroy, attribute amend (includes passphrase changes etc). All these need an audit output stream for the interaction as well as the current state machine. We are focused on the datastore/auth components and NOT the over stuff like passphrase resets.

This is purely an in-memory database; maybe allowing the javascript interface to create a set.

Design Requirements

For each process we need to implement we will need to compose into a policy made up of environment, subject, resource, and action attribute sets which grants capabilities and then we enforce it with the implemention.

  • create a new user account
  • log the user in
  • log the user out
  • destroy user account
  • change account attributes

Policies

Let’s define the default policy and create a set of capability tokens. Notice that we only define tokens that we need, and then prevent direct use of raw capabilities.


default policy 
  drop (cap)

set token as keyvalue : 
  key is read 
  value is (cap READ)

set token as keyvalue : 
  key is write 
  value is (cap WRITE)

set token as keyvalue : 
  key is create
  value is (cap CREATE)

set token as keyvalue : 
  key is destroy 
  value is (cap DESTROY)

berid of (cap)

Create a new user account

First we need to map an operator (subject) to the action so that we can implement a datastore for user accounts. We implement this at the HTML/XHR server fulfulling the environment attribute set. The resources are the user accounts.


# allow create capability 
# for user accounts
policy create-user-accounts in localhost
  allow create
  where
    environment must have state user-unknown
    action is create-account
    subject must have attribute "operator"
    resource must be "/accounts"

# enforce the interactions with the policy
# with an interface 
interface
  state user-unknown
    begin here
    subject must not have attribute "username" 

  state user-known
    subject must have attribute "username"

  process create-account
    required as input :
      "email", "username", and "passphrase"
    produces attribute subject "username"

  transition
    from user-unknown
    to user-known
    via create-account    

dot diagram of states

User login

Because the HTML/XHR server affectly sits between authorisation server and the client we combine attributes directly from the user and the HTML/XHR server when talking to the authorisation server (we overload the subject attribute set).


policy read-user-accounts in localhost
  allow read
  where
    environment must have state no-session
    action is create-session
    subject must have 
      attribute "operator"
      attribute "username" 
      attribute "passphrase"
    resource must be "/accounts"

policy create-session in localhost
  allow create
  where
    environment must have state no-session
    action is create-session
    subject must have 
      attribute "operator"
      attribute "username"
      attribute "session"
    resource must be "/sessions"

interface
  state no-session
    begin here
    subject must not have attribute "session"

  state with-session
    subject must have attribute "session"

  process create-session
    required as input : "username" and "passphrase"
    produces attribute subject "session"

  transition
    from no-session
    to with-session
    via create-session

dot diagram of states

User logout


policy delete-user-session in localhost
  allow destroy
  where
   environment must have state with-session
   action is destroy-session
   subject must have 
     attribute "operator"
     attribute "username"
     attribute "session"
   resource must be "/sessions"

interface
  state with-session
    begin here
    subject must have attribute "session"

  state no-session
    subject must not have attribute "session"

  process destroy-session
    required as input : "session"
    
  transition
    from with-session
    to no-session
    via destroy-session

dot diagram of states

Destroy user account


policy destroy-user-accounts in localhost
  allow destroy
  where
    enviroment must have state with-session
    action is destroy-account
    subject must have 
      attribute "operator"
      attribute "username"
    resource must be "/accounts"

inteface
  state with-session
    begin here
    subject must have attribute "session"

  state no-session
    subject must not have attribute "session"

  process destroy-account
    required as input :
      "session" and "passphrase"

  transition
    from with-session
    to no-session
    via destroy-account

dot diagram of states

Change account attributes


policy update-account-attributes in localhost
  allow write and read
  where
    environment must have state with-session
    action is set-passphrase or set-colour
    subject must have 
      attribute "operator"
      attribute "username"
    resource must be "/accounts"

interface
  state with-session
    begin here
    subject must have attribute "session"

  process set-passphrase
    required as input : 
      "username", "new-passphrase" and "passphrase"
 
  process set-colour
    required as input :
      "username" and "colour"

  transition
    loop with-session
    via set-passphrase

  transition
    loop with-session
    via set-colour

dot diagram of states

Plugins for all modules

This deals with the specifics of each interface implementation, such as the hashing functions etc.

mae