Skip to content

Instantly share code, notes, and snippets.

@agrueneberg
Last active March 19, 2023 06:48
Show Gist options
  • Save agrueneberg/6585680 to your computer and use it in GitHub Desktop.
Save agrueneberg/6585680 to your computer and use it in GitHub Desktop.
HMAC-SHA256 example for verifying both the data integrity and the authentication of a request in Node.js and web browsers.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>HMAC-SHA256 Example</title>
</head>
<body>
<script src="http://crypto.stanford.edu/sjcl/sjcl.js"></script>
<script>
var sharedSecret, query, signature, hmac, xhr;
// No longer secret shared secret ;-)
sharedSecret = "super-secret";
query = "key=value";
hmac = new sjcl.misc.hmac(sjcl.codec.utf8String.toBits(sharedSecret), sjcl.hash.sha256);
signature = sjcl.codec.hex.fromBits(hmac.encrypt(query));
xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:1337/?" + query);
xhr.setRequestHeader("X-Signature", signature);
xhr.onload = function () {
console.log(xhr.status, xhr.responseText);
}
xhr.send(null);
</script>
</body>
</html>
var http, crypto, sharedSecret, query, signature;
http = require("http");
crypto = require("crypto");
sharedSecret = "super-secret";
query = "key=value";
signature = crypto.createHmac("sha256", sharedSecret).update(query).digest("hex");
http.get({
port: 1337,
path: "/?" + query,
headers: {
"X-Signature": signature
}
}, function (res) {
console.log(res.statusCode);
});
var http, url, crypto, sharedSecret;
http = require("http");
url = require("url");
crypto = require("crypto");
sharedSecret = "super-secret";
http.createServer(function (req, res) {
var retrievedSignature, parsedUrl, computedSignature;
// Deal with CORS.
res.setHeader("Access-Control-Allow-Origin", "*");
if (req.method === "OPTIONS") {
res.setHeader("Access-Control-Allow-Headers", "X-Signature");
res.writeHead(204);
res.end();
} else {
// Get signature.
retrievedSignature = req.headers["x-signature"];
// Recalculate signature.
parsedUrl = url.parse(req.url);
computedSignature = crypto.createHmac("sha256", sharedSecret).update(parsedUrl.query).digest("hex");
// Compare signatures.
if (computedSignature === retrievedSignature) {
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.end("Hello World\n");
} else {
res.writeHead(403, {
"Content-Type": "text/plain"
});
res.end("Get Out\n");
}
}
}).listen(1337);
console.log("Server running on port 1337");
@xeaone
Copy link

xeaone commented Aug 21, 2018

@mfn I think this should fixes the timing attack.

Modified Fork

        const computedSignatureBuffer = Buffer.from(computedSignature, 'hex');
        const retrievedSignatureBuffer = Buffer.from(retrievedSignature, 'hex');
        const valid = crypto.timingSafeEqual(computedSignatureBuffer, retrievedSignatureBuffer);

        if (valid) {
            res.writeHead(200, {
                "content-type": "text/plain"
            });
            res.end("hello world\n");
        } else {
            res.writeHead(403, {
                "content-type": "text/plain"
            });
            res.end("get out\n");
        }

@shide1989
Copy link

Awesome ! thanks !

@Blackening999
Copy link

@mfn I think this should fixes the timing attack.

Modified Fork

        const computedSignatureBuffer = Buffer.from(computedSignature, 'hex');
        const retrievedSignatureBuffer = Buffer.from(retrievedSignature, 'hex');
        const valid = crypto.timingSafeEqual(computedSignatureBuffer, retrievedSignatureBuffer);

        if (valid) {
            res.writeHead(200, {
                "content-type": "text/plain"
            });
            res.end("hello world\n");
        } else {
            res.writeHead(403, {
                "content-type": "text/plain"
            });
            res.end("get out\n");
        }

That helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment