Socket
Socket
Sign inDemoInstall

hawk

Package Overview
Dependencies
1
Maintainers
1
Versions
85
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.0.8 to 0.1.0

86

lib/index.js

@@ -10,3 +10,5 @@ // Load modules

var internals = {};
var internals = {
randomSource: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
};

@@ -45,5 +47,8 @@

*
* hostHeaderName - optional header field name, used to override the default 'Host' header when used
* behind a cache of a proxy. Apache2 changes the value of the 'Host' header while preserving
* the original (which is what the module must verify) in the 'x-forwarded-host' header field.
* hostHeaderName - optional header field name, used to override the default 'Host' header when used
* behind a cache of a proxy. Apache2 changes the value of the 'Host' header while preserving
* the original (which is what the module must verify) in the 'x-forwarded-host' header field.
*
* nonceFunc - optional nonce validation function. The function signature is function(nonce, ts, callback)
* where 'callback' must be called using the signature function(err).
*/

@@ -56,5 +61,10 @@

// Default options
options.hostHeaderName = (options.hostHeaderName ? options.hostHeaderName.toLowerCase() : 'host');
options.nonceFunc = options.nonceFunc || function (nonce, ts, callback) { return callback(); };
// Check required HTTP headers: host, authentication
var hostHeader = (options.hostHeaderName ? req.headers[options.hostHeaderName.toLowerCase()] : req.headers.host);
var hostHeader = req.headers[options.hostHeaderName];
if (!hostHeader) {

@@ -82,2 +92,3 @@ return callback(Err.badRequest('Missing Host header'), null, null);

!attributes.ts ||
!attributes.nonce ||
!attributes.mac) {

@@ -127,10 +138,19 @@

var mac = exports.calculateMAC(credentials.key, credentials.algorithm, attributes.ts, req.method, req.url, host, port, attributes.ext);
if (mac !== attributes.mac) {
var mac = exports.calculateMAC(credentials.key, credentials.algorithm, attributes.ts, attributes.nonce, req.method, req.url, host, port, attributes.ext);
if (!exports.fixedTimeComparison(mac, attributes.mac)) {
return callback(Err.unauthorized('Bad mac'), credentials, attributes.ext);
}
// Successful authentication
// Check nonce
return callback(null, credentials, attributes.ext);
options.nonceFunc(attributes.nonce, attributes.ts, function (err) {
if (err) {
return callback(Err.unauthorized('Invalid nonce'), credentials, attributes.ext);
}
// Successful authentication
return callback(null, credentials, attributes.ext);
});
});

@@ -142,3 +162,3 @@ };

exports.calculateMAC = function (key, algorithm, timestamp, method, uri, host, port, ext) {
exports.calculateMAC = function (key, algorithm, timestamp, nonce, method, uri, host, port, ext) {

@@ -152,2 +172,3 @@ // Parse request URI

var normalized = timestamp + '\n' +
nonce + '\n' +
method.toUpperCase() + '\n' +

@@ -194,3 +215,3 @@ url.pathname + (url.search || '') + '\n' +

var attributesRegex = /(id|ts|ext|mac)="([^"\\]*)"\s*(?:,\s*|$)/g;
var attributesRegex = /(id|ts|nonce|ext|mac)="([^"\\]*)"\s*(?:,\s*|$)/g;
var verify = headerParts[2].replace(attributesRegex, function ($0, $1, $2) {

@@ -207,3 +228,3 @@

}
return attributes;

@@ -219,3 +240,3 @@ };

exports.getAuthorizationHeader = function (credentials, method, uri, host, port, ext, timestamp) {
exports.getAuthorizationHeader = function (credentials, method, uri, host, port, ext, timestamp, nonce) {

@@ -235,3 +256,4 @@ // Check request

timestamp = timestamp || Math.floor(((new Date()).getTime() / 1000));
var mac = exports.calculateMAC(credentials.key, credentials.algorithm, timestamp, method, uri, host, port, ext);
nonce = nonce || exports.randomString(6);
var mac = exports.calculateMAC(credentials.key, credentials.algorithm, timestamp, nonce, method, uri, host, port, ext);

@@ -244,5 +266,39 @@ if (!mac) {

var header = 'Hawk id="' + credentials.id + '", ts="' + timestamp + (ext ? '", ext="' + ext : '') + '", mac="' + mac + '"';
var header = 'Hawk id="' + credentials.id + '", ts="' + timestamp + '", nonce="' + nonce + (ext ? '", ext="' + ext : '') + '", mac="' + mac + '"';
return header;
};
// Generate a random string of given size (not for crypto)
exports.randomString = function (size) {
var result = [];
var len = internals.randomSource.length;
for (var i = 0; i < size; ++i) {
result.push(internals.randomSource[Math.floor(Math.random() * len)]);
}
return result.join('');
};
// Compare two strings using fixed time algorithm (to prevent time-based analysis of MAC digest match)
exports.fixedTimeComparison = function (a, b) {
var mismatch = (a.length === b.length ? 0 : 1);
if (mismatch) {
b = a;
}
for (var i = 0, il = a.length; i < il; ++i) {
var ac = a.charCodeAt(i);
var bc = b.charCodeAt(i);
mismatch += (ac === bc ? 0 : 1);
}
return (mismatch === 0);
};

2

package.json
{
"name": "hawk",
"description": "HTTP Hawk Authentication Scheme",
"version": "0.0.8",
"version": "0.1.0",
"author": "Eran Hammer <eran@hueniverse.com> (http://hueniverse.com)",

@@ -6,0 +6,0 @@ "contributors": [],

@@ -6,3 +6,3 @@ ![hawk Logo](https://raw.github.com/hueniverse/hawk/master/images/hawk.png)

Current version: **0.0.x**
Current version: **0.1.0**

@@ -83,3 +83,3 @@ [![Build Status](https://secure.travis-ci.org/hueniverse/hawk.png)](http://travis-ci.org/hueniverse/hawk)

Http.createServer(handler).listen(8000, '127.0.0.1');
Http.createServer(handler).listen(8000, 'example.com');
```

@@ -105,6 +105,6 @@

var options = {
uri: 'http://127.0.0.1:8000/resource/1?b=1&a=2',
uri: 'http://example.com:8000/resource/1?b=1&a=2',
method: 'GET',
headers: {
authorization: Hawk.getAuthorizationHeader(credentials, 'GET', '/resource/1?b=1&a=2', '127.0.0.1', 8000, 'some-app-data')
authorization: Hawk.getAuthorizationHeader(credentials, 'GET', '/resource/1?b=1&a=2', 'example.com', 8000, 'some-app-data')
}

@@ -127,3 +127,3 @@ };

GET /resource/1?b=1&a=2 HTTP/1.1
Host: 127.0.0.1:8000
Host: example.com:8000
```

@@ -141,14 +141,15 @@

* Key identifier: dh37fgj492je
* Key: werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn
* Algorithm: hmac-sha-256
* Key identifier: dh37fgj492je
* Key: werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn
* Algorithm: hmac-sha-256
The client generates the authentication header by calculating a timestamp (e.g. the number of seconds since January 1,
1970 00:00:00 GMT) and constructs the normalized request string (newline separated values):
1970 00:00:00 GMT), generates a nonce, and constructs the normalized request string (newline separated values):
```
1353832234
j4h3g2
GET
/resource/1?b=1&a=2
127.0.0.1
example.com
8000

@@ -162,3 +163,3 @@ some-app-data

```
/uYWR6W5vTbY3WKUAN6fa+7p1t+1Yl6hFxKeMLfR6kk=
hpf5lg0G0rtKrT04CiRf0Q+IDjkGkyvKdMjtqu1XV/s=
```

@@ -171,4 +172,4 @@

GET /resource/1?b=1&a=2 HTTP/1.1
Host: 127.0.0.1:8000
Authorization: Hawk id="dh37fgj492je", ts="1353832234", ext="some-app-data", mac="/uYWR6W5vTbY3WKUAN6fa+7p1t+1Yl6hFxKeMLfR6kk="
Host: example.com:8000
Authorization: Hawk id="dh37fgj492je", ts="1353832234", ext="some-app-data", mac="hpf5lg0G0rtKrT04CiRf0Q+IDjkGkyvKdMjtqu1XV/s="
```

@@ -175,0 +176,0 @@

@@ -43,3 +43,3 @@ // Load modules

req.headers.authorization = Hawk.getAuthorizationHeader(credentials, req.method, req.url, 'example.com', 8080, 'some-app-data', 1353809207);
req.headers.authorization = Hawk.getAuthorizationHeader(credentials, req.method, req.url, 'example.com', 8080, 'some-app-data');

@@ -68,3 +68,3 @@ Hawk.authenticate(req, credentialsFunc, {}, function (err, credentials, ext) {

req.headers.authorization = Hawk.getAuthorizationHeader(credentials, req.method, req.url, 'example.com', 8080, 'some-app-data', 1353809207);
req.headers.authorization = Hawk.getAuthorizationHeader(credentials, req.method, req.url, 'example.com', 8080, 'some-app-data');
req.url = '/something/else';

@@ -87,3 +87,3 @@

headers: {
authorization: 'Hawk id="1", ts="1353788437", mac="lDdDLlWQhgcxTvYgzzLo3EZExog=", ext="hello"',
authorization: 'Hawk id="1", ts="1353788437", nonce="k3j4h2", mac="qrP6b5tiS2CO330rpjUEym/USBM=", ext="hello"',
host: 'example.com:8080'

@@ -107,7 +107,7 @@ },

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'
authorization: 'Hawk id="dh37fgj492je", ts="1353832234", nonce="j4h3g2", mac="hpf5lg0G0rtKrT04CiRf0Q+IDjkGkyvKdMjtqu1XV/s=", ext="some-app-data"',
host: 'example.com:8000'
},
method: 'GET',
url: '/resource/4?filter=a'
url: '/resource/1?b=1&a=2'
};

@@ -123,2 +123,40 @@

it('should fail on a replay', function (done) {
var req = {
headers: {
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="ZPa2zWC3WUAYXrwPzJ3DpF54xjQ2ZDLe8GF1ny6JJFI=", ext="hello"',
host: 'example.com:8080'
},
method: 'GET',
url: '/resource/4?filter=a'
};
var memoryCache = {};
var options = {
nonceFunc: function (nonce, ts, callback) {
if (memoryCache[nonce]) {
return callback(new Error());
}
memoryCache[nonce] = true;
return callback();
}
};
Hawk.authenticate(req, credentialsFunc, options, function (err, credentials, ext) {
expect(err).to.not.exist;
expect(credentials.user).to.equal('steve');
Hawk.authenticate(req, credentialsFunc, options, function (err, credentials, ext) {
expect(err).to.exist;
expect(err.toResponse().payload.message).to.equal('Invalid nonce');
done();
});
});
});
it('should fail on an invalid authentication header: wrong scheme', function (done) {

@@ -165,3 +203,3 @@

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"'
},

@@ -180,7 +218,7 @@ method: 'GET',

it('should fail on an missing authorization attribute', function (done) {
it('should fail on an missing authorization attribute (id)', function (done) {
var req = {
headers: {
authorization: 'Hawk ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'

@@ -200,2 +238,59 @@ },

it('should fail on an missing authorization attribute (ts)', function (done) {
var req = {
headers: {
authorization: 'Hawk id="123", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'
},
method: 'GET',
url: '/resource/4?filter=a'
};
Hawk.authenticate(req, credentialsFunc, {}, function (err, credentials, ext) {
expect(err).to.exist;
expect(err.toResponse().payload.message).to.equal('Missing attributes');
done();
});
});
it('should fail on an missing authorization attribute (nonce)', function (done) {
var req = {
headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'
},
method: 'GET',
url: '/resource/4?filter=a'
};
Hawk.authenticate(req, credentialsFunc, {}, function (err, credentials, ext) {
expect(err).to.exist;
expect(err.toResponse().payload.message).to.equal('Missing attributes');
done();
});
});
it('should fail on an missing authorization attribute (mac)', function (done) {
var req = {
headers: {
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", ext="hello"',
host: 'example.com:8080'
},
method: 'GET',
url: '/resource/4?filter=a'
};
Hawk.authenticate(req, credentialsFunc, {}, function (err, credentials, ext) {
expect(err).to.exist;
expect(err.toResponse().payload.message).to.equal('Missing attributes');
done();
});
});
it('should fail on an unknown authorization attribute', function (done) {

@@ -205,3 +300,3 @@

headers: {
authorization: 'Hawk id="123", ts="1353788437", x="3", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", x="3", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'

@@ -244,3 +339,3 @@ },

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080:90'

@@ -264,3 +359,3 @@ },

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'

@@ -289,3 +384,3 @@ },

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'

@@ -314,3 +409,3 @@ },

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'

@@ -345,3 +440,3 @@ },

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcUyW6EEgUH4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'

@@ -377,3 +472,3 @@ },

headers: {
authorization: 'Hawk id="123", ts="1353788437", mac="/qwS4UjfVWMcU4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
authorization: 'Hawk id="123", ts="1353788437", nonce="k3j4h2", mac="/qwS4UjfVWMcU4jlr7T/wuKe3dKijvTvSos=", ext="hello"',
host: 'example.com:8080'

@@ -409,3 +504,3 @@ },

expect(Hawk.calculateMAC('dasdfasdf', 'hmac-sha-0', Date.now() / 1000, 'GET', '/resource/something', 'example.com', 8080)).to.equal('');
expect(Hawk.calculateMAC('dasdfasdf', 'hmac-sha-0', Date.now() / 1000, 'k3k4j5', 'GET', '/resource/something', 'example.com', 8080)).to.equal('');
done();

@@ -425,4 +520,4 @@ });

var header = Hawk.getAuthorizationHeader(credentials, 'POST', '/somewhere/over/the/rainbow', 'example.net', 443, 'Bazinga!', 1353809207);
expect(header).to.equal('Hawk id="123456", ts="1353809207", ext="Bazinga!", mac="LYUkYKYkQsQstqNQHcnAzDXce0oHsmS049rv4EalMb8="');
var header = Hawk.getAuthorizationHeader(credentials, 'POST', '/somewhere/over/the/rainbow', 'example.net', 443, 'Bazinga!', 1353809207, 'Ygvqdz');
expect(header).to.equal('Hawk id="123456", ts="1353809207", nonce="Ygvqdz", ext="Bazinga!", mac="qSK1cZEkqPwE2ttBX8QSXxO+NE3epFMu4tyVpGKjdnU="');
done();

@@ -456,3 +551,47 @@ });

});
describe('#fixedTimeComparison', function () {
var a = Hawk.randomString(50000);
var b = Hawk.randomString(150000);
it('should take the same amount of time comparing different string sizes', function (done) {
var now = Date.now();
Hawk.fixedTimeComparison(b, a);
var t1 = Date.now() - now;
now = Date.now();
Hawk.fixedTimeComparison(b, b);
var t2 = Date.now() - now;
expect(t2 - t1).to.be.within(-1, 1);
done();
});
it('should return true for equal strings', function (done) {
expect(Hawk.fixedTimeComparison(a, a)).to.equal(true);
done();
});
it('should return false for different strings (size, a < b)', function (done) {
expect(Hawk.fixedTimeComparison(a, a + 'x')).to.equal(false);
done();
});
it('should return false for different strings (size, a > b)', function (done) {
expect(Hawk.fixedTimeComparison(a + 'x', a)).to.equal(false);
done();
});
it('should return false for different strings (size, a = b)', function (done) {
expect(Hawk.fixedTimeComparison(a + 'x', a + 'y')).to.equal(false);
done();
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc