8-Digest-AMQP

This document describes Digest-AMQP, a specification for exchanging Digest authentication data between a front-end requesting application and a back-end credentials service over an AMQP network. Note: this document needs to be updated to take into account changes in RestMS draft/2.

  • Name: gro.pqma.ikiw|PQMA-tsegiD-8#gro.pqma.ikiw|PQMA-tsegiD-8
  • Editor: Pieter Hintjens <moc.xitami|hp#moc.xitami|hp>
  • Contributors: Brad Clements <moc.skrowkrum|ckb#moc.skrowkrum|ckb>

License

Copyright (c) 2008 iMatix Corporation.

This Specification is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This Specification is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses>.

Change Process

This document is governed by the gro.pqma.ikiw|SSOC-1#gro.pqma.ikiw|SSOC-1 specification.

Summary

Digest-AMQP is a specification for exchanging authentication data between two peers, a front-end requesting application and a back-end credentials server over an AMQP network. This specification consists of three components:

  1. An XML format for credential Request messages, sent from a front-end application to a back-end application.
  2. An XML format for credential Response messages, send from the back-end application to the front-end application.
  3. AMQP routing rules for ensuring that the front-end and back-end application can properly exchange Request and Response messages.

Digest-AMQP is not in itself a secured scheme, it assumes that either the AMQP network lies entirely within a secured zone, or else that the data is carried over some unspecified secured AMQP transport.

Purpose

The intended purpose of Digest-AMQP is to allow the separation of front-end applications that need to authenticate users, from back-end credential services. Specifically, the front-end applications that Digest-AMQP targets are web services using Digest authentication[1]. A typical back-end credential service would be LDAP[2].

Architecture

The following diagram shows the general architecture of a Digest-AMQP deployment.

digest-amqp.png

The Request (in red) travels as follows:

  1. A browser requests a method on a resource that is protected by Digest authentication.
  2. The WWW server challenges the browser to provide credentials, which it does.
  3. The WWW server asks for credentials from the Digest-AMQP requestor.
  4. The Digest-AMQP requestor formats a Digest-AMQP Request message and sends this to the AMQP server via an AMQP API layer.
  5. The AMQP server routes the Request to the Digest-AMQP service, which retrieves the message via its AMQP API layer.
  6. The Digest-AMQP service requests digest or password information from the credentials service.

The Response (in blue) then travels as follows:

  1. The credentials service provides the Digest-AMQP service with the necessary credentials data.
  2. The Digest-AMQP service formats a Response message and sends this to the AMQP server via its AMQP API layer.
  3. The AMQP server routes the Response to the Digest-AMQP requestor, which retrieves the message via its AMQP API layer.
  4. The Digest-AMQP requestor provides the WWW server with the credentials.
  5. The WWW server validates the brower-provided credentials and either accepts or denies the browser's request.

One of the key features of the Digest scheme is that passwords do not cross the network. That is, the browser and the WWW server, and the Digest-AMQP requestor and the Digest-AMQP service exchange digests of the password along with other data.

Note that:

  • The web server must be capable of executing a plug-in module to implement Digest-AMQP.
  • The credentials service must be capable either of returning a properly formed Digest, or of providing the clear-text password to the Digest-AMQP service.

Specification

Request and Response formats

The Digest-AMQP request is an XML document with this format:

<digest-amqp
    xmlns="http://www.imatix.com/schema/digest-amqp"
    version="1.0">
    <request
        user = "user-name"
        realm = "realm"
        algorithm = "algorithm"
        reply_to = "routing key"
    />
</digest-amqp>

The user, realm, and algorithm properties have the meaning defined in RFC 2617. The reply_to property is the name of a queue created by the requestor.

The Digest-AMQP response is an XML document with this format:

<digest-amqp
    xmlns="http://www.imatix.com/schema/digest-amqp"
    version="1.0">
    <response
        user = "user-name"
        realm = "realm"
        algorithm = "algorithm"
        digest = "digest-string"
    />
</digest-amqp>

The user, realm, and algorithm properties are identical to that supplied in the Request. The digest is the hex value of H(user ":" realm ":" password), where H is the hashing algorithm. If the user and/or realm are not valid, the digest will be an empty string.

Message routing

Digest-AMQP uses the RestMS[3] convention of addressing queues by name via the amq.direct exchange. In this section we'll provide recipes for both requestor and service, in both AMQP and RestMS styles.

The AMQP service works as follows:

  • It declares a queue, normally called "Digest-AMQP" and declared as non-exclusive.
  • It binds this queue to the "amq.direct" exchange, using the routing key "Digest-AMQP".
  • It consumes from that queue and waits for incoming requests.
  • When it receives a request, it executes it, and prepares a response.
  • It publishes the response to the "amq.direct" exchange using the reply_to property of the request as the routing key.

The AMQP requestor works as follows:

  • It declares a queue, normally named by the server, and declared as exclusive, for responses.
  • It binds this queue to the "amq.direct" exchange, using the queue name as routing key.
  • It prepares a request and publishes that to the "amq.direct" exchange using the routing key "Digest-AMQP", and the 'mandatory' option set to TRUE.
  • It consumes from that queue and waits for incoming messages.
  • When it receives a response, it processes it.
  • If it receives a returned message, it can conclude that the Digest-AMQP service is not running.

Either the requestor and/or service can be implemented using RestMS rather than AMQP. The RestMS service works as follows:

  • It creates a service feed called "Digest-AMQP".
  • It creates a server-named pipe.
  • It creates a join between its pipe and the Digest-AMQP feed using the address "*@Digest-AMQP".
  • It waits for a request message on the pipe, and when one arrives, it executes it and prepares a response.
  • It posts the response to an address consisting of the reply_to property of the request followed by "@amq.direct".
  • It deletes the message from the pipe.

The RestMS requestor works as follows:

  • It creates a server-named pipe, for responses.
  • It creates a join between its pipe and the amq.direct feed using the address "{pipe-name}@amq.direct".
  • It prepares a request, setting the reply_to property to the name of its pipe.
  • It posts the request to the address "request@Digest-AMQP".
  • It waits for a response message on the pipe.
  • When it receives a response, it processes it, and deletes the message from the pipe.

In the RestMS case, if the Digest-AMQP service is not running, the requestor's attempt to post to "request@Digest-AMQP" will fail with an error. RestMS does not use AMQP's 'mandatory' and returned message mechanism.

Note that all strings used in routing are case-sensitive.

Worked example

We use the example from RFC 2617. The requestor requests credentials for the user "Mufasa" in the realm "moc.tsoh|mlaertset#moc.tsoh|mlaertset" by sending this Request:

Exchange: amq.direct
Routing-key: Digest-AMQP
Content-type: application/x-Digest-AMQP

<digest-amqp
    xmlns="http://www.imatix.com/schema/digest-amqp"
    version="1.0">
    <request
        user = "Mufasa"
        realm = "testrealm@host.com"
        algorithm = "MD5"
        reply_to = "queue-0123"
        />
</digest-amqp>

The actual format of the request is a binary AMQP Basic.Publish command. Here we use a simple text format to represent the significant arguments: exchange and routing-key are arguments to Basic.Publish and content-type is a property of the content. The XML is passed as the content body of the Basic.Publish commands.

The password for this user/realm is "Circle Of Life". The Digest-AMQP service calculates the hash as MD5("Mufasa:moc.tsoh|mlaertset#moc.tsoh|mlaertset:Circle Of Life"), which "939e7578ed9e3c518a452acee763bce9". It sends this Response:

Exchange: amq.direct
Routing-key: queue-0123
Content-type: application/x-Digest-AMQP

<digest-amqp
    xmlns="http://www.imatix.com/schema/digest-amqp"
    version="1.0">
    <response
        user = "Mufasa"
        realm = "testrealm@host.com"
        algorithm = "MD5"
        digest = "939e7578ed9e3c518a452acee763bce9"
        />
</digest-amqp>

Sample implementations

This is an implementation of a Digest-AMQP service in PAL. It demonstrates the AMQP routing aspects:

<?xml?>
<!--
    This is a demo Digest-AMQP service that shows how to send
    a response back to a Digest-AMQP requestor.  The response is
    hard-coded on the RFC2617 example.
 -->
<pal script = "amq_pal_gen">
    <session>
        <queue_declare
            queue = "Digest-AMQP" />
        <queue_bind
            queue = "$queue"
            exchange = "amq.direct"
            routing_key = "$queue" />
        <basic_consume
            queue = "$queue" />
        <echo>I: Digest-AMQP service is registered</echo>
        <repeat>
            <wait />
            <basic_arrived>
                <set name = "content_type"
                    value = "$content_type" />
                <if name = "content_type"
                    value = "application/x-Digest-AMQP">
                    <echo>I: received valid request</echo>
                </if>
                <else>
                    <echo>W: received invalid request</echo>
                </else>
            </basic_arrived>
            <basic_content
                content_type = "application/x-Digest-AMQP">
                &lt;digest-amqp
                    xmlns=\"http://www.imatix.com/schema/digest-amqp\"
                    version=\"1.0\"&gt;
                    &lt;response
                        user = \"Mufasa\"
                        realm = \"testrealm@host.com\"
                        algorithm = \"MD5\"
                        digest = \"939e7578ed9e3c518a452acee763bce9\"
                        /&gt;
                &lt;/digest-amqp&gt;
            </basic_content>
            <basic_publish
                exchange = "amq.direct"
                routing_key = "$reply_to"
                />
        </repeat>
    </session>
</pal>

Here is a corresponding Digest-AMQP requestor in PAL:

<?xml?>
<!--
    This is a demo Digest-AMQP requestor that shows how to send
    a request to a Digest-AMQP service.  The request is based
    on the RFC2617 example.
 -->
<pal script = "amq_pal_gen">
    <session>
        <queue_declare
            exclusive = "1" />
        <queue_bind
            queue = "$queue"
            exchange = "amq.direct"
            routing_key = "$queue" />
        <basic_consume
            queue = "$queue" />
        <echo>I: sending Digest-AMQP request...</echo>
        <basic_content
            content_type = "application/x-Digest-AMQP">
            &lt;digest-amqp
                xmlns=\"http://www.imatix.com/schema/digest-amqp\"
                version=\"1.0\"&gt;
                &lt;request
                    user = \"Mufasa\"
                    realm = \"testrealm@host.com\"
                    algorithm = \"MD5\"
                    reply_to = \"$queue\"
                    /&gt;
            &lt;/digest-amqp&gt;
        </basic_content>
        <basic_publish
            exchange = "amq.direct"
            routing_key = "Digest-AMQP"
            mandatory = "1"
            immediate = "1"
            />
        <wait
            timeout = "1000" />
        <basic_arrived>
            <set
                name = "content_type"
                value = "$content_type" />
            <if name = "content_type"
                value = "application/x-Digest-AMQP">
                <echo>I: received valid response</echo>
            </if>
            <else>
                <echo>E: unrecognised response</echo>
            </else>
        </basic_arrived>
        <empty>
            <echo>E: no response from Digest-AMQP service</echo>
        </empty>
        <basic_returned>
            <echo>E: Digest-AMQP service is not running</echo>
        </basic_returned>
    </session>
</pal>

Here is an example Perl program that calculates the MD5 hash for a given user, realm, and password:

#! /usr/bin/perl
#
use Digest::MD5;
#   These arguments are passed on the command line
my ($user, $realm, $password) = @ARGV;
die "E: syntax: command user realm password\n" unless $password;
print "digest = \"".Digest::MD5::md5_hex("$user:$realm:$password")."\"\n";

Further fragments showing how to calculate digests in other programming languages are given in the Apache password format documentation.[4]

Security implications

  • The Digest-AMQP design does not attempt to secure data traveling over the AMQP network. We assume that this network is sufficiently secure.
  • Passwords never leave the Digest-AMQP service, or the credentials service if it is capable of generating the neccessary digests.
  • All parties that know the digest can falsely represent the user. Thus, if the WWW server is compromised, and it has stored digests, those digests can be used to create valid responses for Digest Authentication challenges, to any server relying on the same digest.
  • The MD5 algorithm is not considered secure[5]. Other algorithms such as SHA-1 and the SHA-2 family may be considered. However, these are not implemented by browsers[6] so currently, only the MD5 algorithm can be used.
  • HTTP Digest Authentication does not encrypt data.

Considering these security implications, applications should weigh the interest of using a certificate based security model, for data that is considered sensitive.

References

[1] HTTP Authentication: Basic and Digest Access Authentication (RFC 2617)
[2] Lightweight Directory Access Protocol (LDAP): Technical Specification Road Map (RFC 4510)
[3] gro.pqma.ikiw|SMTSER-7#gro.pqma.ikiw|SMTSER-7.
[4] Apache HTTP Server Version 2.2 Password Formats.
[5] MD5 collisions crack CA certificate (Heise Online)
[6] Digest Authentication is not secure (Mozilla bug 472823)

Comments

Add a New Comment

Edit | Files | Tags | Source | Print | Talk

Use one of these tags to define the specification's state:

  • raw - new specification
  • draft - has at least one implementation.
  • stable - has been deployed to real users.
  • legacy - is being replaced by newer specifications.
  • retired - has been replaced and is no longer used.
  • deleted - abandoned before becoming stable.
raw