Subversion Repositories SE.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
12 7u83 1
Introduction
2
------------
3
 
4
This example shows how to make an Amazon-style HMAC authentication system for an API with mochiweb.
5
 
6
Purpose
7
-------
8
 
9
The purpose of this example is to:
10
* make it easy to implement an API in mochiweb
11
  - using a proven approach so that 'amateurs' don't have to reinvent crypto
12
* make it easy to generate client libraries for that API so that client-side implementers can:
13
  - reuse closely related code examples
14
  - build compatibility unit tests instead of fiddling around debugging their library against live implementations of the system
15
 
16
Scope
17
-----
18
 
19
The scope of this document is:
20
* a description of the client-server exchange
21
* a reference implementation of
22
  - the server-side implementation of the exchange
23
  - the client-side implementation of the exchange
24
* developing a custom implementation of an API
25
* deploying that implementation to new client-side users to build their client libraries
26
 
27
Contents
28
--------
29
 
30
Subsequent sections of this document are:
31
* the client-server exchange
32
* the reference implementation in this example
33
* building a custom implementation
34
* deploying a custom implementation
35
 
36
The Client-Server Exchange
37
--------------------------
38
 
39
OVERVIEW
40
 
41
This section describes the client-server exchange for an Amazon-style API authentication schema. It has the following characteristics:
42
* based on a public key/private key
43
* used to authenticate non-SSL api requests
44
* not a full once-use schema and is vulnerable to replay attacks within a short time window
45
 
46
TYPE OF API
47
 
48
The api described in this document is:
49
* suitable for machine-machine communication
50
 
51
The api described in this document is NOT:
52
* an implementation of 2-legged OAUTH
53
  - see https://github.com/tim/erlang-oauth
54
* an implementation of 3-legged OAUTH
55
 
56
It is not suitable for use in applications where an end user has to log into a service and piggy-back on top of a keypair security system.
57
 
58
THE CLIENT LIBRARY HERE IS **NOT** AN AMAZON CLIENT LIBRARY. AMAZON DOES FUNKY STUFF WITH HOSTNAMES AND PUSHES THEM ONTO THE URL IN CANONICALIZATION! THE CLIENT LIBRARY IS AMAZON-A-LIKE ENOUGH TO USE THE AMAZON DOCOS TO BUILD A TEST SUITE.
59
 
60
STEP 1
61
 
62
The client is issued with a pair of keys, one public, one private, for example:
63
* public:  "bcaa49f2a4f7d4f92ac36c8bf66d5bb6"
64
* private: "92bc93d6b8aaec1cde772f903e06daf5"
65
 
66
In the Amazon docs these are referred to as:
67
* AWSAccessKeyId     (public)
68
* AWSSecretAccessKey (private)
69
 
70
These can be generated by the function:
71
hmac_api_lib:get_api_keypair/0
72
 
73
This function returns cryptographically strong random numbers using the openSSL crypto library under the covers.
74
 
75
The public key is used as a declaration of identity, "I am bcaa49..."
76
 
77
The private key is never passed over the wire and is used to construct the same hash on both the client- and the server-side.
78
 
79
STEP 2
80
 
81
The client prepares their request:
82
* url
83
* time of request
84
* action (GET, POST, etc)
85
* type of request (application/json, etc)
86
* contents of request
87
* etc, etc
88
 
89
These components are then turned into a string called the canonical form.
90
 
91
The HTTP protocol is permissive; it treats different requests as if they were the same. For instance it doesn't care about the order in which headers are sent, and allows the same header to contain multiple values as a list or be specified multiple times as a key-value pair.
92
 
93
Intermediate machines between the client and server MAY pack and repack the HTTP request as long as they don't alter its meaning in a narrow sense. This means that the format of the HTTP request is not guaranteed to be maintained.
94
 
95
The canonical form simply ensures that all the valid ways of making the same request are represented by the same string - irrespective of how this is done.
96
 
97
The canonical form handles POST bodies and query parameters and silently discards anchors in URL's.
98
 
99
A hash of this string is made with the private key.
100
 
101
STEP 3
102
 
103
The client makes the request to the server:
104
* the signature is included in the request in the standard HTTPAuthorization header. (As the Amazon documentation points out this is infelicitous as it is being used for Authentication not Authorization, but hey!).
105
 
106
The Authorization header constructed has the form:
107
<schema name><space><public key><colon><signature>
108
 
109
An Amazon one looks like:
110
Authorization: AWS 0PN5J17HBGZHT7JJ3X82:frJIUN8DYpKDtOLCwo//yllqDzg=
111
               --- -------------------- ----------------------------
112
               sch    public key                 signature
113
 
114
The HTTP request is made.
115
 
116
STEP 4
117
 
118
The request is processed:
119
* the server receives the request
120
* the server constructs the canonical form from the attributes of the request:
121
  - url
122
  - date header
123
  - action (GET, POST, etc)
124
  - content type of request (application/json, etc)
125
  - some custom headers
126
  - etc, etc
127
* the server takes the client's public key from the HTTPAuthorization header and looks up the client's private key
128
* the server signs the canonical form with the private key
129
* the server compares:
130
  - the signature in the request to the signature it has just generated
131
  - the time encoded in the request with the server time
132
* the request is accepted or denied
133
 
134
The time comparison is 'fuzzy'. Different server's clocks will be out of sync to a degree, the request may have acquired a time from an intermediate machine along the way, etc, etc. Normally a 'clock skew' time is allowed - in Amazon's case this is 15 minutes.
135
 
136
NOTA BENE: THIS CLOCK SKEW TIME ALLOWS FOR REPLAY ATTACKS WHERE A BAD GUY SIMPLY CAPTURES AND REPLAYS TRAFFIC.
137
 
138
EXTENSION
139
 
140
It is possible to extend this schema to prevent replay attacks. The server issues a nonce token (a random string) which is included in the signature. When the server authorizes the request it stores the token and prevents any request with that token (ie a replay) being authorized again.
141
 
142
The client receives its next nonce token in the response to a successful request.
143
 
144
The Reference Implementation In This Example
145
--------------------------------------------
146
 
147
The reference implementation used in this example is that described in the Amazon documentation here:
148
http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
149
 
150
The try out the reference implementation:
151
* create a new mochiweb project as per the mochiweb README
152
  - make app PROJECT=project_name
153
* copy hmac_api_lib.erl and hmac_api_client.erl into project_name/src
154
* copy hmac_api.hrl into project_name/include
155
* edit project_name_web.erl and add a call to hmac_api_lib:authorize_request/1
156
 
157
authorize/request/1 should be called in the loop of project_name_web.erl as per:
158
 
159
    loop(Req, DocRoot) ->
160
        Auth = hmac_api_lib:authorize_request(Req),
161
        io:format("Auth is ~p~n", [Auth]),
162
        "/" ++ Path = mochiweb_request:get(path, Req),
163
        ...
164
 
165
When this is done you are ready to test the api:
166
* run 'make' in project_name/ to build the Erlang
167
* start the web server with 'start-dev.sh' in project_name/ (this will also open an Erlang shell to the Erlang VM)
168
 
169
To test the api run this command in the Erlang shell:
170
* hmac_api_client:fire().
171
 
172
The reference implementation uses 5 constants defined in hmac_api.hrl.
173
* schema
174
* headerprefix
175
* dateheader
176
* publickey
177
* privatekey
178
 
179
Building A Custom Implementation
180
--------------------------------
181
 
182
The simplest custom implementation is to simply take the existing code and change the values of the following constants:
183
* schema
184
* headerprefix
185
* dateheader
186
 
187
If the API is to be used 'as is', please use the values which are commented out in hmac_api.hrl. This will make easier for software developers to work out which version of which client-side libraries they can use.
188
 
189
Client libraries written in other languages than Erlang can reemployment the test suite in hmac_api_lib.erl.
190
 
191
More sophisticated changes will involve changes to the canonicalization functions.
192
 
193
Use of a generic schema should make reuse of client libraries easier across different platforms.
194
 
195
If you develop an ‘as-is’ client-side library in another language please consider submitting its code to this example.
196
 
197
Deploying A Custom Implementation
198
---------------------------------
199
 
200
When deploying a custom implementation, the server-side code should be released with unit tests so the client-side developer can easily build a robust client.
201
 
202
In addition to that you will need to specify:
203
* description of how the API works:
204
  - ie the acceptable methods and urls
205
  - custom headers and their usage (if appropriate)
206