DEV Community

Cover image for Simple "Have I Been Pwned" API Calls With Clojure

Simple "Have I Been Pwned" API Calls With Clojure

schmudde profile image schmudde Updated on ・5 min read

';--have i been pwned? is the gold standard for seeing if a user's account has been compromised in a data breach. This is usually done using an eMail address, which is what I'll be demonstrating here.

I will be using the Have I Been Pwned (HIBP) API in this notebook. The API requires a key for a nominal charge of $3.50 a month.¹ Obviously, my key is not available to the public.

Here's a full blog post on why ';--have i been pwned? charges for this service. The API is pretty simple, so let's get started.

Grab the Data

Use a curl command to grab the data using the API.

curl -H "hibp-api-key:<your-secret>" -H "user-agent: Beyond the Frame" -sS -o "/pwned-accounts.json"
Enter fullscreen mode Exit fullscreen mode

A breakdown of the switches I used:

  • 🔑 -H "hibp-api-key:<your-secret>": An HIBP subscription key is required to make an authorized call and can be obtained on the API key page. The key is then passed in a hibp-api-key header. Replace <your-secret> with your own key.
  • -H "user-agent: Beyond the Frame": Each request to the API must be accompanied by a user agent request header. Typically this should be the name of the app consuming the service.
  • -o "/pwned-accounts.json": Output the returned JSON data.

The URL has two unique features:

  • My eMail, encoded for a URL.
  • ?truncateResponse=false: return the complete breach data for all data breaches

Examine the data² returned by Have I Been Pwned:

(require '[ :as json])

(def accounts 
    (json/read-str (slurp "/pwned-accounts.json") 
                   :key-fn keyword))

(pprint accounts)
Enter fullscreen mode Exit fullscreen mode

output ⇒

[{"~:Domain":"","~:DataClasses":["Email addresses","Passwords"],"~:IsFabricated":false,"~:IsVerified":true,"~:BreachDate":"2017-06-27","~:PwnCount":17979961,"~:Title":"8tracks","~:IsSensitive":false,"~:IsSpamList":false,"~:LogoPath":"","~:ModifiedDate":"2019-08-25T08:52:21Z","~:Name":"8tracks","~:Description":"In June 2017, the online playlists service known as <a href=\"\" target=\"_blank\" rel=\"noopener\">8Tracks suffered a data breach</a> which impacted 18 million accounts. In their disclosure, 8Tracks advised that &quot;the vector for the attack was an employee’s GitHub account, which was not secured using two-factor authentication&quot;. Salted SHA-1 password hashes for users who <em>didn't</em> sign up with either Google or Facebook authentication were also included. The data was provided to HIBP by whitehat security researcher and data analyst Adam Davies and contained almost 8 million unique email addresses. The complete set of 18M records was later provided by and updated in HIBP accordingly.","~:IsRetired":false,"~:AddedDate":"2018-02-16T07:09:30Z"},{"~:Domain":"","~:DataClasses":["Dates of birth","Email addresses","Genders","Names","Partial credit card data","Passwords","Phone numbers","Physical addresses","Social media profiles"],"~:IsFabricated":false,"~:IsVerified":true,"~:BreachDate":"2019-05-03","~:PwnCount":6353564,"~:Title":"EatStreet","~:IsSensitive":false,"~:IsSpamList":false,"~:LogoPath":"","~:ModifiedDate":"2019-07-19T11:29:35Z","~:Name":"EatStreet","~:Description":"In May 2019, the online food ordering service <a href=\"\" target=\"_blank\" rel=\"noopener\">EatStreet suffered a data breach affecting 6.4 million customers</a>. An extensive amount of personal data was obtained including names, phone numbers, addresses, partial credit card data and passwords stored as bcrypt hashes. The data was provided to HIBP by a source who requested it be attributed to &quot;;.","~:IsRetired":false,"~:AddedDate":"2019-07-19T11:29:35Z"},{"~:Domain":"","~:DataClasses":["Email addresses","Names","Phone numbers","Physical addresses"],"~:IsFabricated":false,"~:IsVerified":true,"~:BreachDate":"2018-05-31","~:PwnCount":26151608,"~:Title":"Ticketfly","~:IsSensitive":false,"~:IsSpamList":false,"~:LogoPath":"","~:ModifiedDate":"2018-07-14T06:06:15Z","~:Name":"Ticketfly","~:Description":"In May 2018, the website for the ticket distribution service <a href=\"\" target=\"_blank\" rel=\"noopener\">Ticketfly was defaced by an attacker and was subsequently taken offline</a>. The attacker allegedly requested a ransom to share details of the vulnerability with Ticketfly but did not receive a reply and subsequently posted the breached data online to a publicly accessible location. The data included over 26 million unique email addresses along with names, physical addresses and phone numbers. Whilst there were no passwords in the publicly leaked data, <a href=\" target=\"_blank\" rel=\"noopener\">Ticketfly later issued an incident update</a> and stated that &quot;It is possible, however, that hashed values of password credentials could have been accessed&quot;.","~:IsRetired":false,"~:AddedDate":"2018-06-03T06:14:14Z"}]
Enter fullscreen mode Exit fullscreen mode

Parse the Data

Grab three parameters from every title returned and print as HTML.

  • Title: A descriptive title for the breach suitable for displaying to end users. It's unique across all breaches but individual values may change in the future (i.e. if another breach occurs against an organisation already in the system). If a stable value is required to reference the breach, refer to the "Name" attribute instead.
  • Domain: The domain of the primary website the breach occurred on.
  • BreachDate: The date (with no time) the breach originally occurred on in ISO 8601 format. This is not always accurate — frequently breaches are discovered and reported long after the original incident. Use this attribute as a guide only.
(require '[ :as ld])

(defn account->html [account]
  (let [title (:Title account)
        link (str "<a href=\"http://" (:Domain account) "\" target=\"_blank\">" title "</a>")
        date (ld/get-year (ld/parse (:BreachDate account)))]
    (str "<li>" link " in " date "</li>")))

(defn print-accounts [accounts]
  (str "<hr /><center><h2>Accounts Associated With This eMail</h2><br /><ul>"
     (apply str (map #(account->html %) accounts))
       "</ul></center><br /><br /><hr />"))
Enter fullscreen mode Exit fullscreen mode

output ⇒

Accounts Associated With This eMail

That's it. Use your key and simply replace the eMail address with any other eMail address and you will get the pertinent results.


¹ The language of HIBP's API is not very clear. If you cancel your subscription, your API key will remain functional until the subscription renewal is due after which it will cease to work. You can always reactivate it by returning to the API key page and purchasing another one.

² The deps.edn file:

 {org.clojure/clojure {:mvn/version "1.10.1"}
  org.clojure/data.json {:mvn/version "1.0.0"} {:mvn/version "0.1.11"}}}
Enter fullscreen mode Exit fullscreen mode

Creative Commons License

This work is licensed under a Creative Commons Attribution 4.0 International License.

Have I Been Pwned is the source of the data on this page. Some of the writing is directly cut from their API v3 documentation, also under CC4.0.

Discussion (0)

Forem Open with the Forem app