Documentation for Product Version:
The Credentials API allows you to securely lookup whether a given username/password combination exists in our database of compromised account credentials. The typical example where this API can be useful is on your website’s or company’s login form. As users login, you can check their credentials against this API to ensure that they have not been compromised. In the event that a user’s credentials have been exposed, you can force them to reset their password. In this way, you can prevent criminals from logging in using stolen credentials and ensure that they are changed by the legitimate user the very next time they login.
The Hash-Based Credentials API allows you to securely query the Enzoic credentials database without passing the raw credentials.
TO DRAMATICALLY SIMPLIFY THE PROCESS OF CALLING THIS API, WE STRONGLY RECOMMEND USING ONE OF OUR LIBRARIES.
Using the Hash-Based Credentials API is a multi step process, as follows:
More detail for each step follows below.
The first step is to make a GET call to the /accounts API endpoint to retrieve the account-specific salt value and a list of required hashes. The /accounts call is as follows:
GET https://api.enzoic.com/accounts?username=<username or hash>
Parameter | Type | Description |
---|---|---|
username | string | The username/email or SHA-256 hash of the lowercase username/email of the user |
Response | Description |
---|---|
200 | The username was found in the Enzoic database and the response contains the user-specific salt value and an array of password hashes which must be calculated for a subsequent Credentials API call. |
404 | The username was not found in the Enzoic database and has no compromised credentials associated with it. No further action is necessary. |
Member | Type | Description |
---|---|---|
salt | string | The salt value to use when constructing the Argon2 Credentials Hashes |
passwordHashesRequired | PasswordHashSpecification[] | An array of password hash specifications. |
lastBreachDate | string | A string containing the date/time of the last credentials exposure found for this account. This can be used to more intelligently check credentials for a user, i.e. if the lastBreachDate is less than the last time you performed a credentials check, you can skip the remaining steps. |
Member | Type | Description |
---|---|---|
hashType | PasswordHashType | The hash algorithm to use (see PasswordHashType enum). The password should be in UTF-8 encoding prior to hashing. |
salt | string | The salt value to use with the algorithm, if applicable. If an empty string, no salt value should be used. |
Value | Description | Output |
---|---|---|
1 | MD5 hash algorithm without salt | Hex string |
2 | SHA1 hash algorithm without salt | Hex string |
3 | SHA256 hash algorithm without salt | Hex string |
5 | Composite Algorithm: md5(md5(salt) + md5(password)) | Hex string |
6 | Composite Algorithm: md5(md5(password) + salt) | Hex string |
7 | Composite Algorithm: md5(md5(password) + salt) | Hex string |
8 | BCrypt algorithm using provided salt | Bcrypt string |
9 | CRC32 hash algorithm without salt | Hex string |
10 | PHPBB3 (PHPass) | PHPass string |
11 | Composite Algorithm: xor(sha512(password + salt), whirlpool(salt + password)) | Hex string |
13 | Composite Algorithm: md5(password + salt) | Hex string |
14 | SHA512 hash algorithm without salt | Hex string |
15 | Composite Algorithm with fixed salt: md5(“kikugalanet” + password) | Hex string |
16 | MD5Crypt algorithm using provided salt | MD5Crypt string |
17 | Composite Algorithm: bcrypt(md5(password)) using provided salt for BCrypt | BCrypt string |
18 | Composite Algorithm: sha256(md5(password + salt)) | Hex string |
19 | Composite Algorithm: md5(salt + password) | Hex string |
20 | DESCrypt algorithm using provided salt | DESCrypt string |
21 | MySQL (pre 4.1) algorithm | Hex string |
22 | Composite Algorithm: “*” + sha1(sha1(password)) | Hex string prefixed with “*” |
23 | Composite Algorithm: base64(sha1(UTF16Bytes(password))) | Hex string |
24 | Composite Algorithm: sha1(salt + sha1(password)) | Hex string |
25 | Composite Algorithm: sha1(password + salt) | Hex string |
26 | Partial MD5 – first 20 bytes of MD5 hash of password | Hex string |
27 | Composite Algorithm: md5(md5(password)) | Hex string |
28 | Composite Algorithm: “md5$” + salt + “$” + md5(salt + password) | Hex string |
29 | Composite Algorithm: “sha1$” + salt + “$” + sha1(salt + password) | Formatted hex string |
30 | Partial MD5 – first 29 bytes of MD5 hash of password | Hex string |
31 | Composite Algorithm: salt + sha1(salt + password) | Formatted hex string |
32 | Composite Algorithm: sha1(username + password) | Hex string |
33 | NTLM | Hex string |
34 | Composite Algorithm: sha1(“–” + salt + “–” + password + “–“) | Hex string |
35 | SHA384 | Hex string |
36 | Composite Algorithm: hmac-sha256(sha1(salt) + password) HMAC key: “d2e1a4c569e7018cc142e9cce755a964bd9b193d2d31f02d80bb589c959afd7e” |
Hex string |
37 | Composite Algorithm: sha256(salt + password) | Hex string |
38 | Composite Algorithm: sha512<11 times>(sha512(password + salt)) | Hex string |
39 | SHA512Crypt | SHA512Crypt string |
40 | Composite Algorithm: sha512(password + “:” + salt) | Hex string |
41 | SHA256Crypt | SHA256Crypt string |
42 | Composite Algorithm (AuthMeSHA256): “$SHA$” + salt + “$” + sha256(sha256(password) + salt)) | Formatted hex string with salt prepended |
curl -v --header "authorization: basic {your auth string}" "https://api.enzoic.com/accounts?username=sample@email.tst"
{ "salt": "aa101973b4ea4ad698b42d20303a9527", "passwordHashesRequired": [ { "hashType": 1, "salt": "" }, { "hashType": 2, "salt": "" } ], "lastBreachDate": "2016-12-10T02:05:03.000Z" }
Next, iterate over the passwordHashesRequired array returned in step 1, calculating a password hash as specified by the hashType of each PasswordHashSpecification entry.
Some of the hashTypes are standard hashing algorithms, such as SHA-1 and BCrypt, while others are composite algorithms, combining the password, salt, and potentially multiple standard hashes together to calculate a final hash value. For example, hashType 5 is a composite with the following algorithm: md5(md5(salt) + md5(password)). Thus, it would be necessary to calculate an MD5 of the provided salt and concatenate this with an MD5 of the password. Finally, an MD5 of the resultant string is calculated and this is the final hash. Unless otherwise specified, the standard hash steps in a composite algorithm should all be outputting lowercase hex string representations of the value.
Note that some of the hashTypes require salt values and some do not, depending on the hash algorithm. When the salt value is not required for the specified algorithm, it will be an empty string.
Calculating these hashes will involve language-specific libraries to provide standard hashing functions (MD5, SHA-1, SHA-256, etc). Typically these functions are either built-in or available as open-source libraries for practically any widely-used programming language.
For each item in the password hashes array produced in step 2, concatenate the username and password hash and then hash the result with the Argon2 algorithm and the account salt returned in step 1. Lastly, extract just the hash value from the Argon2 hash (the string will also contain the algorithm hash parameters and salt value used). This should result in an array of Credential Hashes, which will be the arguments passed to the Credentials API in the final step.
Example:
for (var i = 0; i < passwordHashes.length; i++) { // concatenate the username, "$", and password hash value from step 2 into a single string var toHash = username + "$" + passwordHashes[i]; // calculate the Argon2 hash (Argon2 library for your programming language required) var argon2Hash = Argon2(toHash, accountSalt); // extract just the hash value from the resultant Argon2 string (should be everything after the last "$" credentialHashes[i] = argon2Hash.substr(argon2Hash.lastIndexOf("$") + 1); }
When calculating the Credential Hash, the Argon2 algorithm should be called with the following settings:
Setting | Value |
---|---|
Type | Argon2d |
Iterations/TimeCost | 3 |
Memory | 1024 |
Parallelism | 2 |
HashLength | 20 |
The final step is to make a GET call to the /credentials API endpoint, passing in a list of the first 10 hex characters from each Credential Hash calculated in step 3. Note that many Argon2 libraries will return a hash result in Base64, so it may be necessary to convert the hash into a hex string before extracting the first 10 characters. The /credentials call in this case is as follows:
GET https://api.enzoic.com/credentials?partialHashes=<hash1>&partialHashes=<hash2>&partialHashes=<hash3>
Parameter | Type | Description |
---|---|---|
partialHashes | string[] | A list of the first 10 hex characters of Argon2 Credential Hashes |
Response | Description |
---|---|
200 | One or more candidate credentials matches were found in Enzoic’s database of compromised credentials. The candidate hashes are returned in the response body. You should compare the full hash(es) to the list of returned candidates. If a match is found, the credentials are compromised. |
404 | The credentials were not found in Enzoic’s database and have not been compromised. |
The response body contains an array of candidate credential hashes which match one or more of the hash prefixes provided. Each candidate entry should be compared against the full Credential Hashes you calculated in step 3. If a match exists, the credentials are compromised.
Member | Type | Description |
---|---|---|
candidateHashes | string[] | A list of credential hashes from the Enzoic database that match the first 10 characters of the hash(es) which were passed in |
curl -v --header "authorization: basic {your auth string}" "https://api.enzoic.com/credentials?partialHashes=9d5e070553&partialHashes=9d0d41df26"
{ "candidateHashes": [ "9d5e070553366cd970393f51309f81e2668a67e9" ] }
Test accounts are available for all of the hash types above. Test accounts all have the same form:
Username: eicar_type(passwordType)@enzoic.com Password: ~7N8?g(Vyw-W^`A<
So, for example, to test an account with a BCrypt password in the Enzoic database, you would use a username of:
eicar_type8@enzoic.com