CINXE.COM

Troy Hunt: I've Just Launched "Pwned Passwords" V2 With Half a Billion Passwords for Download

<!doctype html> <html class="no-js" lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Troy Hunt: I&#x27;ve Just Launched &quot;Pwned Passwords&quot; V2 With Half a Billion Passwords for Download</title> <meta name="description" content=""> <meta name="HandheldFriendly" content="True" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://www.troyhunt.com/assets/css/main.min.css?v=63b7062e03"> <link href="//fonts.googleapis.com/css?family=Vollkorn:400,400italic,700,700italic" rel="stylesheet" type="text/css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <link rel="icon" href="https://www.troyhunt.com/content/images/size/w256h256/2021/12/Troy-Hunt-LM-0059_square.png" type="image/png"> <link rel="canonical" href="https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"> <meta name="referrer" content="no-referrer-when-downgrade"> <meta property="og:site_name" content="Troy Hunt"> <meta property="og:type" content="article"> <meta property="og:title" content="I&#x27;ve Just Launched &quot;Pwned Passwords&quot; V2 With Half a Billion Passwords for Download"> <meta property="og:description" content="Last August, I launched a little feature within Have I Been Pwned [https://haveibeenpwned.com/] (HIBP) I called Pwned Passwords [https://www.troyhunt.com/introducing-306-million-freely-downloadable-pwned-passwords/]. This was a list of 320 million passwords from a range of different data breaches which organisations could use to better protect their own systems."> <meta property="og:url" content="https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"> <meta property="og:image" content="https://www.troyhunt.com/content/images/size/w1200/2018/02/Searching-for-a-Pwned-Password.jpg"> <meta property="article:published_time" content="2018-02-21T19:00:05.000Z"> <meta property="article:modified_time" content="2018-06-11T11:19:07.000Z"> <meta property="article:tag" content="Have I Been Pwned"> <meta property="article:tag" content="Security"> <meta property="article:tag" content="Passwords"> <meta property="article:tag" content="Pwned Passwords"> <meta property="article:publisher" content="https://www.facebook.com/troyahunt"> <meta property="article:author" content="https://www.facebook.com/troyahunt"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="I&#x27;ve Just Launched &quot;Pwned Passwords&quot; V2 With Half a Billion Passwords for Download"> <meta name="twitter:description" content="Last August, I launched a little feature within Have I Been Pwned [https://haveibeenpwned.com/] (HIBP) I called Pwned Passwords [https://www.troyhunt.com/introducing-306-million-freely-downloadable-pwned-passwords/]. This was a list of 320 million passwords from a range of different data breaches which organisations could use to better protect their own systems."> <meta name="twitter:url" content="https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"> <meta name="twitter:image" content="https://www.troyhunt.com/content/images/size/w1200/2018/02/Searching-for-a-Pwned-Password.jpg"> <meta name="twitter:label1" content="Written by"> <meta name="twitter:data1" content="Troy Hunt"> <meta name="twitter:label2" content="Filed under"> <meta name="twitter:data2" content="Have I Been Pwned, Security, Passwords, Pwned Passwords"> <meta name="twitter:site" content="@troyhunt"> <meta name="twitter:creator" content="@troyhunt"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="560"> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "publisher": { "@type": "Organization", "name": "Troy Hunt", "url": "https://www.troyhunt.com/", "logo": { "@type": "ImageObject", "url": "https://www.troyhunt.com/content/images/2016/04/Troy-Hunt-Profile-Photo.jpg", "width": 60, "height": 60 } }, "author": { "@type": "Person", "name": "Troy Hunt", "image": { "@type": "ImageObject", "url": "//www.gravatar.com/avatar/c5531bfb7d76cdaa370c7baf6053288d?s=250&d=mm&r=x", "width": 250, "height": 250 }, "url": "https://www.troyhunt.com/author/troyhunt/", "sameAs": [ "https://www.troyhunt.com/", "https://www.facebook.com/troyahunt", "https://twitter.com/troyhunt" ] }, "headline": "I&#x27;ve Just Launched &quot;Pwned Passwords&quot; V2 With Half a Billion Passwords for Download", "url": "https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/", "datePublished": "2018-02-21T19:00:05.000Z", "dateModified": "2018-06-11T11:19:07.000Z", "image": { "@type": "ImageObject", "url": "https://www.troyhunt.com/content/images/size/w1200/2018/02/Searching-for-a-Pwned-Password.jpg", "width": 1200, "height": 560 }, "keywords": "Have I Been Pwned, Security, Passwords, Pwned Passwords", "description": "Last August, I launched a little feature within Have I Been Pwned\n[https://haveibeenpwned.com/] (HIBP) I called Pwned Passwords\n[https://www.troyhunt.com/introducing-306-million-freely-downloadable-pwned-passwords/].\nThis was a list of 320 million passwords from a range of different data breaches\nwhich organisations could use to better protect their own systems. How? NIST\nexplains [https://pages.nist.gov/800-63-3/sp800-63b.html]:\n\n&gt; When processing requests to establish and change memorized secr", "mainEntityOfPage": "https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/" } </script> <meta name="generator" content="Ghost 5.109"> <link rel="alternate" type="application/rss+xml" title="Troy Hunt" href="https://www.troyhunt.com/rss/"> <script defer src="https://cdn.jsdelivr.net/ghost/portal@~2.49/umd/portal.min.js" data-i18n="true" data-ghost="https://www.troyhunt.com/" data-key="5e03cd80efbea6ff26214a466b" data-api="https://troyhunt.ghost.io/ghost/api/content/" data-locale="en" crossorigin="anonymous"></script><style id="gh-members-styles">.gh-post-upgrade-cta-content, .gh-post-upgrade-cta { display: flex; flex-direction: column; align-items: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; text-align: center; width: 100%; color: #ffffff; font-size: 16px; } .gh-post-upgrade-cta-content { border-radius: 8px; padding: 40px 4vw; } .gh-post-upgrade-cta h2 { color: #ffffff; font-size: 28px; letter-spacing: -0.2px; margin: 0; padding: 0; } .gh-post-upgrade-cta p { margin: 20px 0 0; padding: 0; } .gh-post-upgrade-cta small { font-size: 16px; letter-spacing: -0.2px; } .gh-post-upgrade-cta a { color: #ffffff; cursor: pointer; font-weight: 500; box-shadow: none; text-decoration: underline; } .gh-post-upgrade-cta a:hover { color: #ffffff; opacity: 0.8; box-shadow: none; text-decoration: underline; } .gh-post-upgrade-cta a.gh-btn { display: block; background: #ffffff; text-decoration: none; margin: 28px 0 0; padding: 8px 18px; border-radius: 4px; font-size: 16px; font-weight: 600; } .gh-post-upgrade-cta a.gh-btn:hover { opacity: 0.92; }</style> <script defer src="https://cdn.jsdelivr.net/ghost/sodo-search@~1.5/umd/sodo-search.min.js" data-key="5e03cd80efbea6ff26214a466b" data-styles="https://cdn.jsdelivr.net/ghost/sodo-search@~1.5/umd/main.css" data-sodo-search="https://troyhunt.ghost.io/" data-locale="en" crossorigin="anonymous"></script> <link href="https://www.troyhunt.com/webmentions/receive/" rel="webmention"> <script defer src="/public/cards.min.js?v=63b7062e03"></script> <link rel="stylesheet" type="text/css" href="/public/cards.min.css?v=63b7062e03"> <script defer src="/public/member-attribution.min.js?v=63b7062e03"></script><style>:root {--ghost-accent-color: #15171A;}</style> <a rel="me" href="https://infosec.exchange/@troyhunt">Mastodon</a> <script async src="https://www.googletagmanager.com/gtag/js?id=G-B895JNTH7Z"></script> <script type="text/javascript">function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-B895JNTH7Z");</script> </head> <body class="post-template tag-have-i-been-pwned-3f tag-security tag-passwords tag-pwned-passwords"> <div id="container"> <div class="header_block container"> <div class="header"> <button class="header_navMenu_button i-menu" id="open-button"></button> <div class="header_navMenu_wrap"> <nav role="navigation"> <ul class="header_navMenu"> <li class="navMenu_list" role="presentation"> <a class="navMenu_link" href="https://www.troyhunt.com/">Home</a> </li> <li class="navMenu_list" role="presentation"> <a class="navMenu_link" href="https://www.troyhunt.com/workshops/">Workshops</a> </li> <li class="navMenu_list" role="presentation"> <a class="navMenu_link" href="https://www.troyhunt.com/speaking/">Speaking</a> </li> <li class="navMenu_list" role="presentation"> <a class="navMenu_link" href="https://www.troyhunt.com/media/">Media</a> </li> <li class="navMenu_list" role="presentation"> <a class="navMenu_link" href="https://www.troyhunt.com/about/">About</a> </li> <li class="navMenu_list" role="presentation"> <a class="navMenu_link" href="https://www.troyhunt.com/contact/">Contact</a> </li> <li class="navMenu_list" role="presentation"> <a class="navMenu_link" href="https://www.troyhunt.com/sponsorship/">Sponsor</a> </li> </ul> </nav> </div> <div class="header_socialIcons light"> <ul> <li class="socialIcons_link"> <a class="fa-brands fa-x-twitter" href="https://twitter.com/troyhunt"></a> </li> <li class="socialIcons_link"> <a class="fa-brands fa-facebook-f" href="https://www.facebook.com/troyahunt"></a> </li> <li class="socialIcons_link"> <a class="fa-brands fa-linkedin-in" href="https://www.linkedin.com/in/troyhunt"></a> </li> <li class="socialIcons_link"> <a class="fa-brands fa-youtube" href="https://www.youtube.com/user/troyhuntdotcom"></a> </li> <li class="socialIcons_link"> <a class="fa fa-rss" href="https://feeds.feedburner.com/TroyHunt"></a> </li> </ul> </div> </div> </div> <div class="sponsor_bar content"> <p id="sponsor_message"><strong>Sponsored by:</strong> <i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></p> </div> <div class="post_header container" style="background-image: url(/content/images/2018/02/Searching-for-a-Pwned-Password.jpg)"> <div class="hover"></div> </div> <div class="main-wrapper"> <div class="content-wrapper"> <div class="article-open container no-cover"> <div class="hover"></div> <article class="article-open_item wrap"> <header class="post_head"> <h1>I&#x27;ve Just Launched &quot;Pwned Passwords&quot; V2 With Half a Billion Passwords for Download</h1> <div class="post_socialIcons"> <ul> <li class="socialIcons_link"> <a class="fa-brands fa-x-twitter article-open_window" href="https://twitter.com/share?url=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/&text=Troy%20Hunt%3A%20I've%20Just%20Launched%20%22Pwned%20Passwords%22%20V2%20With%20Half%20a%20Billion%20Passwords%20for%20Download"></a> </li> <li class="socialIcons_link"> <a class="fa-brands fa-facebook-f article-open_window" href="https://www.facebook.com/sharer/sharer.php?u=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"></a> </li> <li class="socialIcons_link linkedin"> <a class="fa-brands fa-linkedin-in article-open_window" href="https://www.linkedin.com/shareArticle?mini=true&url=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"></a> </li> <li class="socialIcons_link reddit article-open_window"> <a class="fa-brands fa-reddit-alien" href="https://reddit.com/submit?url=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/&title=Troy%20Hunt%3A%20I've%20Just%20Launched%20%22Pwned%20Passwords%22%20V2%20With%20Half%20a%20Billion%20Passwords%20for%20Download"></a> </li> <li class="socialIcons_link"> <a class="fa fa-envelope" href="mailto:?subject=Troy%20Hunt%3A%20I've%20Just%20Launched%20%22Pwned%20Passwords%22%20V2%20With%20Half%20a%20Billion%20Passwords%20for%20Download&amp;body=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"></a> </li> </ul> </div> <span class="post_info">22 February 2018</span> </header> </article> </div> <main class="article-open_content container"> <article class="wrap"> <section class="article_text post"> <!--kg-card-begin: markdown--><p>Last August, I launched a little feature within <a href="https://haveibeenpwned.com/?ref=troyhunt.com">Have I Been Pwned</a> (HIBP) I called <a href="https://www.troyhunt.com/introducing-306-million-freely-downloadable-pwned-passwords/">Pwned Passwords</a>. This was a list of 320 million passwords from a range of different data breaches which organisations could use to better protect their own systems. How? <a href="https://pages.nist.gov/800-63-3/sp800-63b.html?ref=troyhunt.com">NIST explains</a>:</p> <blockquote> <p>When processing requests to establish and change memorized secrets, verifiers SHALL compare the prospective secrets against a list that contains values known to be commonly-used, expected, or compromised.</p> </blockquote> <p>They then go on to recommend that passwords &quot;obtained from previous breach corpuses&quot; should be disallowed and that the service should &quot;advise the subscriber that they need to select a different secret&quot;. This makes a lot of sense when you think about it: if someone is signing up to a service with a password that has previously appeared in a data breach, either it's the same person reusing their passwords (bad) or two different people who through mere coincidence, have chosen <em>exactly</em> the same password. In reality, this means they probably both have dogs with the same name or some other personal attribute they're naming their passwords after (also bad).</p> <p>Now all of this was great advice from NIST, but they stopped short of providing the one thing organisations really need to make all this work: the passwords themselves. That's why I created Pwned Passwords - because there was a gap that needed filling - and let's face it, I do have access to rather a lot of them courtesy of running HIBP. So 6 months ago I launched the service and today, I'm pleased to launch version 2 with more passwords, more features and something I'm particularly excited about - more privacy. Here's what it's all about:</p> <h2 id="theresnow501636842pwnedpasswords">There's Now 501,636,842 Pwned Passwords</h2> <p>Back at the V1 launch, I explained how the original data set was comprised of sources such as <a href="https://www.troyhunt.com/password-reuse-credential-stuffing-and-another-1-billion-records-in-have-i-been-pwned/">the Anti Public and Exploit.in combo lists</a> as well as &quot;a variety of other data sources&quot;. In V2, I've expanded that to include a bunch of data sources along with 2 major ones:</p> <ol> <li>The 711 million record <a href="https://www.troyhunt.com/inside-the-massive-711-million-record-onliner-spambot-dump/">Onliner Spambot dump</a>. This was a <em>lot</em> of work to parse varying data formats and if you read the comments on that blog post, you'll get a sense of how much people wanted this (and why it was problematic).</li> <li>The 1.4B <a href="https://www.troyhunt.com/making-light-of-the-dark-web-and-debunking-the-fud/">clear text credentials from the &quot;dark web&quot;</a>. This data resulted in many totally overblown news stories (and contributed to my &quot;dark web&quot; FUD blog post last week), but it did serve as a useful reference for V2. This data also had a bunch of integrity problems which meant the actual number was somewhat less. For example, the exact same username and password pairs appearing with different delimiters:</li> </ol> <p><img src="https://www.troyhunt.com/content/images/2017/12/1.4B-Data-Integrity.png" alt="1.4B-Data-Integrity" loading="lazy"></p> <p>There's also <em>a heap</em> of other separate sources there where passwords were available in plain text. As with V1, I'm not going to name them here, suffice to say it's a broad collection from many more breaches than I used in the original version. It's taken a heap of effort to parse through these but it's helped build that list up to beyond the half billion mark which is a <em>significant</em> amount of data. From a defensive standpoint, this is good - more data means more ability to block risky passwords.</p> <p>But I haven't just <em>added</em> data, I've also removed some. Let me explain why and to begin with, let's do a quick recap on the rationale for hashing them.</p> <h2 id="theyrestillsha1hashedbutwithsomejunkremoved">They're Still SHA-1 Hashed, But with Some Junk Removed</h2> <p>When I launched V1, I explained why I SHA-1 hashed them:</p> <blockquote> <p>It doesn't matter that SHA1 is a fast algorithm unsuitable for storing your customers' passwords with because that's not what we're doing here, it's simply about ensuring the source passwords are not immediately visible.</p> </blockquote> <p>That's still 100% true as of today. There are certainly those that don't agree with this approach; they claim that either the data is easily discoverable enough online anyway or conversely, that SHA-1 is an insufficiently robust algorithm for password storage. They're right, too - on both points - but that's not what this is about. The entire point is to ensure that any personal info in the source data is obfuscated such that it requires a concerted effort to remove the protection, but that the data is still usable for its intended purposes. SHA-1 has done that in V1 and I'm still confident enough in the model to use the same approach in V2.</p> <p>One of the things that did surprise me a little in V1 was the effort some folks went to in order to crack the passwords. I was surprised primarily because the vast majority of those passwords were already available in the clear via the 2 combo lists I mentioned earlier anyway, so why bother? Just download the (easily discoverable) lists! The penny that later dropped was that it presented a challenge - and people like challenges!</p> <p>One upside from people cracking the passwords for fun was that <a href="https://twitter.com/cynoprime?ref=troyhunt.com">CynoSure Prime</a> managed to identify a bunch of junk. Due to the integrity of the source data being a bit patchy in places, there were entries such as the following.</p> <ol> <li>$HEX[e3eeeb]</li> <li>6dcc978317511fd8</li> <li>&lt;div align=\\\'center\\\' style=\\\'font:bold 11px Verdana; width:310px\\\'&gt;&lt;a style=\\\'background-color:#eeeeee;display:block;width:310px;border:solid 2px black; padding:5px\\\' href=\\\'http://...</li> </ol> <p>Of course, it's <em>possible</em> people actually used these strings as passwords but applying a bit of <a href="https://en.wikipedia.org/wiki/Occam%27s_razor?ref=troyhunt.com">Occam's Razor</a> suggests that it's simply parsing issues upstream of this data set. In total, CynoSure Prime identified 3,472,226 junk records which I've removed in V2. (Incidentally, these are the same guys that <a href="http://cynosureprime.blogspot.com.au/2015/09/how-we-cracked-millions-of-ashley.html?ref=troyhunt.com">found the shortcomings in Ashley Madison's password storage approach</a> back in 2015 - they do quality work!)</p> <p>Frankly though, there's little point in removing a few million junk strings. It reduced the overall data size of V2 by 0.69% and other than the tiny fraction of extra bytes added to the set, it makes no practical difference to how the data is used. On that point and in terms of extraneous records, I want to be really clear about the following:</p> <p><strong>This list is not perfect - it's not <em>meant</em> to be perfect - and there will be some junk due to input data quality and some missing passwords because they weren't in the source data sets. It's simply meant to be a list of strings that pose an elevated risk if used for passwords and for that purpose, it's enormously effective.</strong></p> <p>Whilst the total number of records included in V2 is significant, it also doesn't tell the whole story and indeed the feedback from V1 was that the 320M passwords needed something more: an indicator of just how bad each one really was.</p> <h2 id="eachpasswordnowhasacountnexttoit">Each Password Now Has a Count Next to It</h2> <p>Is the password &quot;abc123&quot; worse than &quot;acl567&quot;? Most password strength meters would consider them equivalent because mathematically, they are. But as I've said before, <a href="https://www.troyhunt.com/password-strength-indicators-help-people-make-dumb-choices/">password strength indicators help people make ill-informed choices</a> and this is a perfect example of that. They're both terrible passwords - don't get me wrong - but a predictable keyboard pattern makes the former much worse and that's now reflected in the Pwned Passwords data.</p> <p>Now on the one hand, you could argue that once a password has appeared breached even just once, it's unfit for future use. It'll go into password dictionaries, be tested against the username it was next to and forever more be a weak choice regardless of where it appears in the future. However, I got a lot of feedback from V1 along the lines of &quot;simply blocking 320M passwords is a usability nightmare&quot;. Blocking half a billion, even more so.</p> <p>In V2, every single password has a count next to it. What this means is that next to &quot;abc123&quot; you'll see 2,670,319 - that's how many times it appeared in my data sources. Obviously with a number that high, it appeared many times over in the same sources because many people chose the same password. The password &quot;acl567&quot;, on the other hand, only appeared once. Having visibility to the prevalence means, for example, you might outright block every password that's appeared 100 times or more and force the user to choose another one (there are 1,858,690 of those in the data set), strongly recommend they choose a different password where it's appeared between 20 and 99 times (there's a further 9,985,150 of those), and merely flag the record if it's in the source data less than 20 times. Of course, the password &quot;acl567&quot; may well be deemed too weak by the requirements of the site even without Pwned Passwords so this is by no means the only test a site should apply.</p> <p>In total, there were 3,033,858,815 occurrences of those 501,636,842 unique passwords. In other words, on average, each password appeared 6 times across various data breaches. In some cases, the same password appeared many times in the one incident - <em>often thousands of times</em> - because that's how many people chose the same damn password!</p> <p>Now, having said all that, in the lead-up to the launch of V2 I've had people argue vehemently that they all should be blocked or that none of them should be blocked or any combination in between. That's not up to me, that's up to whoever uses this data, my job is simply to give people enough information to be able to make informed decisions. My own subjective view on this is that &quot;it depends&quot;; different risk levels, different audiences and different mitigating controls should all factor into this decision.</p> <h2 id="ihaventincludedpasswordlength">I <em>Haven't</em> Included Password Length</h2> <p>One request that came up a few times was to include a length attribute on each password hash. This way, those using the data could exclude passwords from the original data set that fall beneath their minimum password length requirements. The thinking there being that it would reduce the data size they're searching through thus realising some performance (and possibly financial) gains. But there are many reasons why this ultimately didn't make sense:</p> <p>The first is that from the perspective of protecting the source data (remember, it contains PII in places), explicitly specifying the length greatly reduces the effort required to crack the passwords. Yes, I know I said earlier that the hashing approach wasn't meant to be highly resilient, but providing a length would be significantly detrimental to the protection that SHA-1 <em>does</em> provide.</p> <p>Then, I actually got a bit scientific about it and looked at what minimum length password websites required. In fact, that's why I wrote the piece on <a href="https://www.troyhunt.com/how-long-is-long-enough-minimum-password-lengths-by-the-worlds-top-sites/">minimum length by the world's top sites</a> a couple of weeks back; I wanted to put hard numbers on it. 11 of the 15 sites I referred to had a minimum length of 6 chars or less. When I then went and looked at the data set I was using, excluding passwords of less than 6 chars would have only reduced the set by less than 1% Excluding anything under 8 chars would have reduced it by just under 16%. They're very small numbers.</p> <p>Then there's the overhead required to host and search this data, that is the overhead those organisations who use it will incur. It should be <em>very</em> close to nothing with the whole half billion data set. Chuck it in a storage construct like Azure Table Storage and you're looking at single digit dollars per month with single digit millisecond lookup times. There's no need for this to be any more complex than that.</p> <p>So in short, it put the protection of the hashing at greater risk, there was very little value gained and it's easy to implement this in a way that's fast and cheap anyway. Some people will disagree, but a lot of thought went into this and I'm confident that the conclusion was the right one.</p> <h2 id="downloadingthedata">Downloading the Data</h2> <p>And now to the pointy bit - downloading the data. As with V1, there's one big 7z archive you can <a href="https://haveibeenpwned.com/Passwords?ref=troyhunt.com">go and pull down immediately from the Pwned Passwords page on HIBP</a>. Also as before, it's available via direct download from the site or via torrent. I want to <em>strongly</em> encourage you to take it via the torrent, let me explain why:</p> <p>The underlying storage construct for this data is Azure Blob storage. If I was to serve the file directly from there, I'd cop a <em>very</em> hefty data bill. Cloudflare came to rescue in V1 and gave me a free plan that enabled a file of that size to be cached as their edge nodes. The impact of that on my bill was <em>massive:</em></p> <blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Another massive thanks to <a href="https://twitter.com/Cloudflare?ref_src=twsrc%5Etfw&ref=troyhunt.com">@Cloudflare</a> for supporting the <a href="https://twitter.com/haveibeenpwned?ref_src=twsrc%5Etfw&ref=troyhunt.com">@haveibeenpwned</a> Pwned Passwords, just did the maths on how much it saved me - whoa! <a href="https://t.co/70kki5Uw7o?ref=troyhunt.com">pic.twitter.com/70kki5Uw7o</a></p>&mdash; Troy Hunt (@troyhunt) <a href="https://twitter.com/troyhunt/status/897571703202603008?ref_src=twsrc%5Etfw&ref=troyhunt.com">August 15, 2017</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <p>Imagine the discussion I'd be having with my wife if it wasn't for Cloudflare's support! And that was before another 6 months' worth of downloads too. Cloudflare might have given me the service for free, but they still have to pay for bandwidth so I'd like to ask for your support in pulling the data down via torrents rather than from the direct download link. To that effect, the UI actively encourages you to grab the torrent:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Download-Pwned-Passwords.png" alt="Download Pwned Passwords" loading="lazy"></p> <p>If you can't grab the torrent (and I'm conscious there are, for example, corporate environments where torrents are blocked), then download it direct but do your bit to help me out by supporting the folks supporting me where you can. As with V1, the torrent file is served directly from HIBP's Blob Storage and you'll find a SHA-1 hash of the Pwned Passwords file next to it so you can check integrity if you're so inclined.</p> <p>So that's the download - go forth and do good things with it! Now for something else cool and that's the online search.</p> <h2 id="queryingthedataonline">Querying the Data Online</h2> <p>In V1, I stood up <a href="https://haveibeenpwned.com/Passwords?ref=troyhunt.com">an online search feature</a> where you could plug in a password and see if it appeared in the data set. That sat on top of an API which I also made available for independent consumption should people wish to use it. And many people did use it. In fact, some of the entrants to <a href="https://www.troyhunt.com/introducing-306-million-freely-downloadable-pwned-passwords/">my competition to win a Lenovo laptop</a> leveraged that particular endpoint including the winner of the competition, 16,year-old Félix Giffard. He created <a href="https://passwordsecurity.info/?ref=troyhunt.com">PasswordSecurity.info</a> which directly consumes the Pwned Passwords API via the client side:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/PasswordSecurity.info.png" alt="PasswordSecurity.info" loading="lazy"></p> <p>Getting back to the online search, being conscious of not wanting to send the wrong message to people, immediately before the search box I put a very clear, very <strong>bold</strong> message: &quot;Do not send any password you actively use to a third-party service - even this one!&quot;</p> <p>But people don't always read these things. The service got <em>a heap</em> of press and millions of people descended on the site to check their passwords. At least I <em>assume</em> it was their passwords, I certainly don't log those searches but based on the news articles and social media commentary, yeah, it would have been a heap of real passwords. And I'm actually ok with that - let me explain:</p> <p>As much as I don't want to encourage people to plug their real password(s) into random third-party sites, I can <em>guarantee</em> that a sizable number of people got a positive hit and then changed their security hygiene as a result. One of the biggest things that's resonated with me in running HIBP is how much impact it's had on changing user behaviour. Seeing either your email address or your password pwned has a way of making people reconsider some of their security decisions.</p> <p>The online search works <em>almost</em> identically to V1 albeit with the count of the password now represented too:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Searching-for-a-Pwned-Password.jpg" alt="Pwned Search" loading="lazy"></p> <p>Pretty simple stuff and for the most part, also pretty familiar. But there's one <em>really</em> important - and <em>really</em> cool - difference. Let me explain:</p> <h2 id="cloudflareprivacyandkanonymity">Cloudflare, Privacy and k-Anonymity</h2> <p>In what proved to be very fortuitous timing, <a href="https://twitter.com/icyapril?ref=troyhunt.com">Junade Ali</a> from Cloudflare reached out to me last month with an idea. They wanted to build a tool to search through Pwned Passwords V1 but to do so in a way that allowed external parties to use it <em>and</em> maintain anonymity. You see, the problem with my existing implementation was that whilst you could pass just a SHA-1 hash of the password, if it returned a hit and I was to take that and reverse it back to the clear (which I could easily do because I created the hashes in the first place!) I'd know the password. That made the service hard to justify sending <em>real</em> passwords to.</p> <p>Junade's idea was different though; he proposed using a mathematical property called <a href="https://en.wikipedia.org/wiki/K-anonymity?ref=troyhunt.com"><em>k</em>-anonymity</a> and within the scope of Pwned Passwords, it works like this: imagine if you wanted to check whether the password &quot;P@ssw0rd&quot; exists in the data set. (Incidentally, the hackers have worked out people do stuff like this. I know, it sucks. They're onto us.) The SHA-1 hash of that string is &quot;21BD12DC183F740EE76F27B78EB39C8AD972A757&quot; so what we're going to do is take <em>just the first 5 characters</em>, in this case that means &quot;21BD1&quot;. That gets sent to the Pwned Passwords API and it responds with 475 hash <em>suffixes</em> (that is everything after &quot;21BD1&quot;) and a count of how many times the original password has been seen. For example:</p> <ol> <li>(21BD1) <strong>0018A45C4D1DEF81644B54AB7F969B88D65:1</strong> (password &quot;lauragpe&quot;)</li> <li>(21BD1) <strong>00D4F6E8FA6EECAD2A3AA415EEC418D38EC:2</strong> (password &quot;alexguo029&quot;)</li> <li>(21BD1) <strong>011053FD0102E94D6AE2F8B83D76FAF94F6:1</strong> (password &quot;BDnd9102&quot;)</li> <li>(21BD1) <strong>012A7CA357541F0AC487871FEEC1891C49C:2</strong> (password &quot;melobie&quot;)</li> <li>(21BD1) <strong>0136E006E24E7D152139815FB0FC6A50B15:2</strong> (password &quot;quvekyny&quot;)</li> <li>...</li> </ol> <p>I added the prefix in brackets beforehand and the source passwords in brackets afterwards simply to illustrate what we're doing here; they're all just different strings that hash down to values with the same first 5 characters. In other words, they're all within the same &quot;range&quot; and you'll see that term referenced more later on. Using this model, someone searching the data set just gets back the hash suffixes and counts (everything in bold after the first 5 chars) and they can then see if everything after the first 5 chars of <em>their</em> hash matches any of the returned strings. Now keep in mind that as far as I'm concerned, the partial hash I was sent could be any one of 475 different possible values. Or it could be something totally different, I simply don't know and therein lies the anonymity value.</p> <p>For the sake of perspective, here are some stats on what this means for the data within Pwned Passwords:</p> <ol> <li>Every hash prefix from 00000 to FFFFF is populated with data (16^5 combinations)</li> <li>The average number of hashes returned is 478</li> <li>The smallest is 381 (hash prefixes &quot;E0812&quot; and &quot;E613D&quot;)</li> <li>The largest is 584 (hash prefixes &quot;00000&quot; and &quot;4A4E8&quot;)</li> </ol> <p>Junade has written a great piece that's just gone live on Cloudflare's blog titled <a href="https://blog.cloudflare.com/validating-leaked-passwords-with-k-anonymity/?ref=troyhunt.com">Validating Leaked Passwords with k-Anonymity</a> and he goes into more depth in that piece. As he explains, there are other cryptographic approaches which could also address the desire for anonymity (for example, private set intersections), but not with the ease and level of simplicity Junade proposed. I loved it so much that I offered to build and run it as a service out of HIBP. Junade (and Cloudflare) thought that was a great idea so they offered to point folks over to the HIBP version rather than build out something totally separate. That's a partnership I'm <em>enormously</em> happy with I appreciate their confidence in my running it.</p> <p>This model of anonymity is what now sits behind the online search feature. You can see it in action by trying a search for &quot;P@ssw0rd&quot; which will return the screen in the previous image. If we drop down and take a look at the dev tools, here's the actual request that's been made:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Searching-for-a-range.png" alt="Searching for a range" loading="lazy"></p> <p>The password has been hashed client side and just the first 5 characters passed to the API (I'll talk more about the mechanics of that shortly). Here's what then comes back in the response:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Hit-found-in-password-range.png" alt="Hit found in password range" loading="lazy"></p> <p>As mentioned earlier, there are 475 hashes beginning with &quot;21BD1&quot;, but only 1 which matches the remainder of the hash for &quot;P@ssw0rd&quot; and that record indicates that the password has previously been seen 47,205 times. And that's it - that's what I've done with Cloudflare's support and that's what we've done together to protect anonymity and make the service available to everyone. Let me now talk about how you can use the API.</p> <h2 id="consumingtheapiandthemechanicsbehindtherangesearch">Consuming the API (and the Mechanics Behind the Range Search)</h2> <p><a href="https://haveibeenpwned.com/API/v2?ref=troyhunt.com#PwnedPasswords">The existing API documentation on HIBP</a> has been updated so you can go there for all the implementation details. There are a few things in particular I want to call out though:</p> <p>Firstly, you'll notice that I'm serving this API from a different domain to the other HIBP APIs and indeed from V1 of the Pwned Passwords service. For V2, I've stood up an Azure Function on the api.pwnedpasswords.com domain which gets the API out of the HIBP website and running on serverless infrastructure instead. <a href="https://www.troyhunt.com/azure-functions-in-practice/">I've written about Azure Functions in the past</a> and they're an awesome way of building a highly scalable, resilient &quot;code as a service&quot; architecture. It ensures that load comes off the HIBP website and that I can scale the Pwned Passwords service infinitely, albeit with a line directly to my wallet! It's also given me the flexibility to do things like trim off a bunch of excessive headers such as the content security policy HIBP uses (that's of no use to a lone API endpoint).</p> <p><del>Secondly, the existing API (that many people have created dependencies on!) still works just fine.</del> <strong>Note: due to the success of the k-anonymity model, <a href="https://www.troyhunt.com/enhancing-pwned-passwords-privacy-by-exclusively-supporting-anonymity/">searching by password was discontinued on the 1st of June 2018</a>.</strong> It also points to the storage repository for V2 of the password set so it's now searching through the full half billion records. I'll leave this running for the foreseeable future, but if you are using it then I'd <em>prefer</em> you roll over to the endpoint on api.pwnedpasswords.com for the reasons mentioned above, and for these other reasons:</p> <p>If you were using the original API via HTTP GET, rolling over to the new one changes <em>absolutely nothing</em> in your implementation other than the URL which will look like this:</p> <pre><code>GET https://api.pwnedpasswords.com/pwnedpassword/{password}</code></pre> <p>It'll still return HTTP 200 when a password is found and 404 when it's not. The only difference (and this shouldn't break any existing usages), is that the 200 response now also contains a count in the body by way of a single integer:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Password-Search.png" alt="Password Search" loading="lazy"></p> <p>And as before, you can always pass a hash if preferred:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Password-Search-by-Hash.png" alt="Password Search by Hash" loading="lazy"></p> <p>But, of course, we've just had the anonymity chat and you would have seen the path for calling that endpoint earlier on. Just to point it out again here, you can pass the first 5 chars of the hash to this address:</p> <pre><code>https://api.pwnedpasswords.com/range/{hashPrefix}</code></pre> <p>Which returns a result like this:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Range-Search-Results.png" alt="Range Search Results" loading="lazy"></p> <p>Remember, these are all hash <em>suffixes</em> (followed by a count) so the full value of the first hash, for example, is &quot;21BD10018A45C4D1DEF81644B54AB7F969B88D65&quot;. Incidentally, input to the API is not case sensitive so &quot;21bd1&quot; works just as well as &quot;21BD1&quot;. All hash suffixes returned (and indeed those provided in the downloadable data) are uppercase simply because that's the default output from <a href="https://docs.microsoft.com/en-us/sql/t-sql/functions/hashbytes-transact-sql?ref=troyhunt.com">SQL Server's HASHBYTES function</a> (I processed the source data in a local RDBMS instance).</p> <p>Unlike the original version, there's no rate-limiting. That was a construct I needed primarily to protect personal data in the breached account search (i.e. when you search for your email address amongst data breaches), but I extended it to Pwned Passwords as well to help protect the infrastructure. Now running on serverless Azure Functions, I don't have that concern so I've dropped it altogether. I'd also dropped version numbers, I'll deal with that when I need them which may not be for a long time (if ever).</p> <p>Now, a few more things around some design decisions I've made: I'm <em>very</em> wary of the potential impact on my wallet of running the service this way. It's one thing to stand up V1 that only returned an HTTP response code, was rate-limited and really wasn't designed to be called in bulk by a single consumer (considering the privacy implications), it's quite another to do what I've done with V2, especially when each search of the range API returns hundreds of records. That &quot;P@ssw0rd&quot; search, for example, returns 9,730 bytes when gzipped (that's a pretty average size) and I'm paying for egress bandwidth out of Azure, the execution of the function and the call to the underlying storage. Tiny amounts each time, mind you, but I've had to reduce that impact on me as far as possible through a range of measures.</p> <p>For example, the result of that range query is not a neatly formatted piece of JSON, it's just colon delimited rows. That impacts my ability to add attributes at a later date and pretty much locks in the current version to today's behaviour, but it saves on the response size. Yes, I know some curly braces and quotes wouldn't add a lot of size, but every byte counts when volumes get large.</p> <p>You'll also notice there's a long max-age on the cache-control header:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Cache-Settings.png" alt="Cache Settings" loading="lazy"></p> <p>This is 31 days' worth of cache and the subsequent Cloudflare cache status header explains why: by routing through their infrastructure, they can aggressively cache these results which ensures not only is the response <em>lightning</em> fast (remember, they presently have <a href="https://www.cloudflare.com/?ref=troyhunt.com">121 edge nodes around the world</a> so there's one near you), but that I don't wear the financial hit of people hammering my origin. Especially when you consider the extent to which multiple people use the same password, when we're talking about the range search where many different passwords have identical hash prefixes, there's some significant benefits to be had from caching. As mentioned earlier, there are 16^5 different hash prefixes (1,048,576) within the range of 00000 to FFFFF so you can see how extensive usage would benefit greatly from caching across many millions of searches. The performance difference alone when comparing a cached result with a non-cached one makes a compelling argument:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Cached-versus-non-cached-V2-queries.png" alt="Cached versus non-cached V2 queries" loading="lazy"></p> <p>This means that even though the response is significantly larger than in V1, if I can serve a request to the new API from cache there's actually a <em>massive</em> improvement. Here's a series of hits to V1 where every single time, the request had to go all the way to the origin server, hit the API and then query 320M records:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/Query-times-of-V1-API.png" alt="Query times of V1 API" loading="lazy"></p> <p>In order to make aggressive caching feasible, I'm also <em>only</em> supporting HTTP GET. Now, some people will lose their minds over this because they'll say &quot;that means it goes into logs and you'll track the passwords being searched for&quot;. If you're worried about me tracking anything, don't use the service. That's not intended to be a flippant statement, rather a simple acknowledgment that you need to trust the operator of the service if you're going to be sending passwords in any shape or form. Offsetting that is the whole k-Anonymity situation; even if you <em>don't</em> trust the service or you think logs may be leaked and abused (and incidentally, <em>nothing</em> is explicitly logged, they're transient system logs at most), the range search goes a very long way to protecting the source. If you <em>still</em> don't trust it, then just download the hashes and host them yourself. No really, that's the whole point of making them available and in all honesty, if it was me building on top of these hashes then I'd definitely be querying my own repository of them.</p> <p>In summary, if you're using the range search then you get protection of the source password <em>well</em> in excess of what I was able to do in V1 plus it's <em>massively</em> faster if anyone else has done a search for any password that hashes down to the same first 5 characters of SHA-1. Plus, it helps me out an awful lot in terms of keeping the costs down!</p> <h2 id="pwnedpasswordsinaction">Pwned Passwords in Action</h2> <p>Lastly, I want to call out a number of examples of the first generation of Pwned Passwords in action. My hope is that they inspire others to build on top of this data set and ultimately, make a positive difference to web security for everyone.</p> <p>For example, Workbooks.com (they make CRM software, among other things) <a href="https://www.workbooks.com/node/1798?ref=troyhunt.com">explains to customers that a Pwned Password is weak or has previously appeared in a data breach</a>.</p> <p>Then there's Colloq (they help you discover conferences) who've <a href="https://colloq.io/blog/how-our-password-check-works?ref=troyhunt.com">written up a great piece with loads of performance stats</a> about their implementation of the data.</p> <p>Or <a href="https://toepoke.co.uk/user.aspx/create?ref=troyhunt.com">try creating an account on Toepoke</a> with a password of &quot;P@ssw0rd&quot; and see how that goes for you:</p> <p><img src="https://www.troyhunt.com/content/images/2018/02/toepoke.co.uk-password-check.png" alt="toepoke.co.uk password check" loading="lazy"></p> <p><a href="https://safepass.me/?ref=troyhunt.com">safepass.me</a> also picked up the data and wrapped it into an offline commercial Active Directory filter (plus a free home version).</p> <p>On the mobile front, there's <a href="https://play.google.com/store/apps/details?id=pwnedpasswords.pwnedpasswords&ref=troyhunt.com">Pwned Pass in the Google Play store</a> which sits on top of the existing API.</p> <h2 id="thisisallstillfreeandistilllikebeer">This is All Still Free (and I Still Like Beer!)</h2> <p>Nothing gains traction like free things! Keeping HIBP free to search your address (or your entire domain) was the best thing I ever did in terms of making it stick. A few months after I launched the service, I stood up <a href="https://haveibeenpwned.com/Donate?ref=troyhunt.com">a donations page</a> where you could buy me some beers (or coffee or other things). It only went up after people specifically asked for it (&quot;hey awesome service, can I get you a coffee?&quot;) and I've been really happy with the responses to it. As I say on the page, it's more the <em>time</em> commitment that really costs me (I'm independent so while I'm building something like Pwned Passwords, I'm not doing something else), but there are also costs that may surprise you:</p> <blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Just burned through $100 of mobile data so that I could finish processing Pwned Passwords this weekend. 110kbps on unlimited broadband plan or 8,286kbps on 4G at $10/GB. It was going to be hard to get it live next week otherwise ?</p>&mdash; Troy Hunt (@troyhunt) <a href="https://twitter.com/troyhunt/status/964785654847627265?ref_src=twsrc%5Etfw&ref=troyhunt.com">February 17, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <p>This is one of those true &quot;Australianisms&quot; courtesy of the fact my <em>up-speed</em> maxes out at about 1.5Mbps (and is then shared across all the things in my house that send data out). Down-speed is about 114 but getting anything up is a nightmare. (And for Aussie friends, no, there's no NBN available in my area of the Gold Coast yet, but apparently it's not far off.) And no, this is not a solvable problem by doing everything in the cloud and there are many reasons why that wouldn't have worked (I'll blog them at a later date).</p> <p>If you want to help kick in for these costs and <a href="https://haveibeenpwned.com/Donate?ref=troyhunt.com">shout me a sympathy coffee or beer(s)</a>, it's still very much appreciated!</p> <h2 id="closing">Closing</h2> <p>Pwned Passwords V2 is now live! Everything you need to use them is over on <a href="https://haveibeenpwned.com/Passwords?ref=troyhunt.com">the Pwned Passwords page of HIBP</a> where you can check them online, learn about the API or just download the whole lot. All those models are free, unrestricted and don't even require attribution if you don't want to provide it, just take what's there and go do good things with it ?</p> <!--kg-card-end: markdown--> <section class="article-open_tag"> <a class="tag" href="/tag/have-i-been-pwned-3f/">Have I Been Pwned</a> <a class="tag" href="/tag/security/">Security</a> <a class="tag" href="/tag/passwords/">Passwords</a> <a class="tag" href="/tag/pwned-passwords/">Pwned Passwords</a> </section> </section> <footer class="post"> <section class="article-open_shere"> <a class="article-open_shere-twitter article-open_window" href="https://twitter.com/share?text=Troy%20Hunt%3A%20I've%20Just%20Launched%20%22Pwned%20Passwords%22%20V2%20With%20Half%20a%20Billion%20Passwords%20for%20Download&amp;url=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"><i class="fa-brands fa-x-twitter"></i> Tweet</a> <a class="article-open_shere-facebook article-open_window" href="https://www.facebook.com/sharer/sharer.php?u=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"><i class="fa-brands fa-facebook-f"></i> Post</a> <a class="article-open_shere-linkedin article-open_window" href="https://www.linkedin.com/shareArticle?mini=true&url=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"><i class="fa-brands fa-linkedin-in"></i> Update</a> <a class="article-open_shere-email" href="mailto:?subject=Troy%20Hunt%3A%20I've%20Just%20Launched%20%22Pwned%20Passwords%22%20V2%20With%20Half%20a%20Billion%20Passwords%20for%20Download&amp;body=https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"><i class="fa fa-envelope"></i> Email</a> <a class="article-open_shere-feed" href="https://feeds.feedburner.com/TroyHunt"><i class="fa fa-rss"></i> RSS</a> </section> <section class="article-open_author"> <figure class="article-open_author_image"> <div class="img" style="background-image: url(//www.gravatar.com/avatar/c5531bfb7d76cdaa370c7baf6053288d?s&#x3D;250&amp;d&#x3D;mm&amp;r&#x3D;x)"><span class="hidden">Troy Hunt's Picture</span></div> </figure> <div class="article-open_author_bio"> <h5 itemprop="author" itemscope itemtype="http://schema.org/Person">Troy Hunt</h5> <p>Hi, I&#x27;m Troy Hunt, I write this blog, create courses for Pluralsight and am a Microsoft Regional Director and MVP who travels the world speaking at events and training technology professionals <a href="/about" class="article_link-more fa fa-long-arrow-right"></a></p> </div> </section> <div id="disqus_thread"></div> <div id="disqus_post_url" data="https://www.troyhunt.com/ive-just-launched-pwned-passwords-version-2/"></div> <div id="disqus_post_identifier" data="ghost-59f98585c227c9002366007d"></div> <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript> </footer> </article> </main> </div> <div class="content-sidebar"> <div class="widget sidebar-featured"> <h4 class="sidebar-featured-header">Troy Hunt</h4> <p>Hi, I&#x27;m Troy Hunt, I write this blog, run &quot;Have I Been Pwned&quot; and am a Microsoft Regional Director and MVP who travels the world speaking at events and training technology professionals <a href="/about" class="article_link-more fa fa-long-arrow-right"></a></p> </div> <div class="widget sidebar-featured"> <h4 class="sidebar-featured-header">Upcoming Events</h4> <p>I often run <a href="/workshops">private workshops</a> around these, here's upcoming events I'll be at:</p> <div id="events_list"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i></div> </div> <div class="widget sidebar-featured"> <h4 class="sidebar-featured-header">Must Read</h4> <ul> <li><a href="/data-breach-disclosure-101-how-to-succeed-after-youve-failed/">Data breach disclosure 101: How to succeed after you've failed</a></li> <li><a href="/data-from-connected-cloudpets-teddy-bears-leaked-and-ransomed-exposing-kids-voice-messages/">Data from connected CloudPets teddy bears leaked and ransomed, exposing kids' voice messages</a></li> <li><a href="/heres-how-i-verify-data-breaches/">Here's how I verify data breaches</a></li> <li><a href="/when-nation-is-hacked-understanding/">When a nation is hacked: Understanding the ginormous Philippines data breach</a></li> <li><a href="/how-i-optimised-my-life-to-make-my-job/">How I optimised my life to make my job redundant</a></li> </ul> </div> <div class="widget sidebar-featured"> <a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2F" class="pluralsight-logo"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 344.3 74.3" xml:space="preserve"><g><g><g><linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="37.3433" y1="2.431" x2="37.3433" y2="73.614"><stop offset="0" style="stop-color:#F05A28" /><stop offset="1" style="stop-color:#EB008B" /></linearGradient><circle fill="url(#SVGID_1_)" cx="37.3" cy="37.3" r="36" /></g><g><path fill="#FFFFFF" d="M31.2,24l23.1,13.3L31.2,50.6L31.2,24 M28.1,18.6l0,37.4l32.4-18.7L28.1,18.6L28.1,18.6z" /><path fill="#FFFFFF" d="M23.7,28l16,9.3l-16,9.3V28 M20.6,22.7v29.2L46,37.3L20.6,22.7L20.6,22.7z" /></g></g><g><g><path fill="#231F20" d="M100.3,37.7c2.6,0,4.2-1.4,4.2-3.5v-0.1c0-2.3-1.6-3.5-4.2-3.5h-4v7H100.3z M92.9,27.5h7.7c4.5,0,7.4,2.6,7.4,6.5v0.1c0,4.4-3.5,6.7-7.8,6.7h-3.9V47h-3.4V27.5z" /></g><polygon fill="#231F20" points="116.5,27.5 119.9,27.5 119.9,43.9 130.2,43.9 130.2,47 116.5,47 " /><g><path fill="#231F20" d="M137.9,38.8V27.5h3.4v11.1c0,3.6,1.9,5.6,4.9,5.6c3,0,4.9-1.8,4.9-5.4V27.5h3.4v11.1c0,5.8-3.3,8.8-8.4,8.8C141.1,47.4,137.9,44.4,137.9,38.8" /><path fill="#231F20" d="M172.7,37.2c2.5,0,4-1.3,4-3.3v-0.1c0-2.1-1.5-3.2-4-3.2h-5v6.6H172.7z M164.3,27.5h8.7c2.5,0,4.4,0.7,5.6,2c1,1.1,1.6,2.5,1.6,4.2v0.1c0,3.2-1.9,5.1-4.7,5.9l5.3,7.4h-4l-4.8-6.8h-4.3V47h-3.4V27.5z" /></g><path fill="#231F20" d="M201.2,39.3l-3.4-7.8l-3.3,7.8H201.2z M196.3,27.4h3.2l8.6,19.6h-3.6l-2-4.7h-9.2l-2,4.7h-3.5L196.3,27.4z" /><polygon fill="#231F20" points="216.4,27.5 219.9,27.5 219.9,43.9 230.1,43.9 230.1,47 216.4,47 " /><g><path fill="#231F20" d="M237.4,44.2l2.1-2.5c1.9,1.6,3.7,2.5,6.2,2.5c2.1,0,3.5-1,3.5-2.5v-0.1c0-1.4-0.8-2.1-4.4-3c-4.2-1-6.5-2.2-6.5-5.8v-0.1c0-3.3,2.8-5.7,6.7-5.7c2.8,0,5.1,0.9,7.1,2.5l-1.8,2.6c-1.8-1.3-3.5-2-5.3-2c-2,0-3.2,1-3.2,2.3v0.1c0,1.5,0.9,2.2,4.6,3.1c4.1,1,6.3,2.5,6.3,5.7v0.1c0,3.7-2.9,5.8-7,5.8C242.5,47.3,239.7,46.3,237.4,44.2" /></g><rect x="261.7" y="27.5" fill="#231F20" width="3.4" height="19.5" /><g><path fill="#231F20" d="M274.5,37.4L274.5,37.4c0-5.5,4.2-10.1,10.1-10.1c3.4,0,5.5,0.9,7.5,2.6l-2.2,2.6c-1.5-1.3-3-2.1-5.5-2.1c-3.6,0-6.3,3.2-6.3,6.9v0.1c0,4,2.6,7,6.6,7c1.8,0,3.5-0.6,4.7-1.5v-3.7h-5v-3h8.3v8.2c-1.9,1.6-4.7,3-8.1,3C278.5,47.4,274.5,43,274.5,37.4" /></g><polygon fill="#231F20" points="302.2,27.5 305.6,27.5 305.6,35.7 315,35.7 315,27.5 318.4,27.5 318.4,47 315,47 315,38.8 305.6,38.8 305.6,47 302.2,47 " /><polygon fill="#231F20" points="327,27.5 327,30.7 333.2,30.7 333.2,47 336.6,47 336.6,30.7 342.8,30.7 342.8,27.5 " /></g></g></svg> </a> <p>Don't have Pluralsight already? <a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fbilling.pluralsight.com%2Findividual%2Fcheckout">How about a 10 day free trial?</a> That'll get you access to thousands of courses amongst which are dozens of my own including:</p> <ol> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fowasp-top10-aspdotnet-application-security-risks">OWASP Top 10 Web Application Security Risks for ASP.NET</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fhttps-every-developer-must-know">What Every Developer Must Know About HTTPS</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fhack-yourself-first">Hack Yourself First: How to go on the Cyber-Offense</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Finformation-security-big-picture">The Information Security Big Picture</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fethical-hacking-social-engineering">Ethical Hacking: Social Engineering</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fmodernizing-websites-microsoft-azure">Modernizing Your Websites with Azure Platform as a Service</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fbrowser-security-headers">Introduction to Browser Security Headers</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fethical-hacking-sql-injection">Ethical Hacking: SQL Injection</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fweb-security-owasp-top10-big-picture">Web Security and the OWASP Top 10: The Big Picture</a></li> <li><a href="https://pluralsight.pxf.io/c/1196446/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fcourses%2Fethical-hacking-web-applications">Ethical Hacking: Hacking Web Applications</a></li> </ol> </div> </div> </div> <aside class="article_readNext"> <a class="article_readNext_story col-2" href="/weekly-update-75/"> <section class="post"> <h4>Weekly Update 75</h4> </section> </a> <a class="article_readNext_story prev col-2" href="/weekly-update-74/"> <section class="post"> <h4>Weekly Update 74</h4> </section> </a> </aside> </div> <footer id="footer"> <div class="footer_block" id="floating_subscribe"> <div class="footer"> <a href="#subscribe" id="subscribe_link">Subscribe <i class="fa fa-envelope-o" aria-hidden="true"></i></a> <div class="subscribe_close"> <a href="#" id="close_floating_subscribe"><i class="fa fa-times" aria-hidden="true"></i></a> </div> </div> </div> <div class="footer_subscription_block container"> <div class="footer_subscription wrap"> <div class="footer_subscription_formGroup"> <a id="subscribe"></a> <h4>Subscribe Now!</h4> <form name="subscribe_form" id="subscribe_form"> <div id="new_subscription"> <div id="subscribe_unsuccessful"></div> <p> Send new blog posts:<br /> <div class="radio_button_group"><input type="radio" name="email_cadence" id="Daily" value="Daily" checked> <label for="Daily">daily</label></div> <div class="radio_button_group"><input type="radio" name="email_cadence" id="Weekly" value="Weekly"> <label for="Weekly">weekly</label></div> </p> <input type="email" value="" name="email_to_subscribe" id="email_to_subscribe" placeholder="email address" required> <input type="submit" value="go!" id="submit_subscribe" /> </div> <div id="confirm_captcha"><p>Hey, just quickly confirm you're not a robot:</p></div> <div id="subscribe_loading"><p><i class="fa fa-cog fa-spin" aria-hidden="true"></i> Submitting...</p></div> <div id="subscribe_successful"><p>Got it! Check your email, click the confirmation link I just sent you and we're done.</p></div> </form> </div> </div> </div> <div class="footer_informations_block container"> <div class="footer_informations wrap"> <div class="footer_information-1 col-3"> <h6>Copyright 2025, Troy Hunt</h6> <p>This work is licensed under a <a rel="license" href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>. In other words, share generously but provide attribution.</p> </div> <div class="footer_information-2 col-3"> <h6>Disclaimer</h6> <p>Opinions expressed here are my own and may not reflect those of others. Unless I'm quoting someone, they're just my own views.</p> </div> <div class="footer_information-3 col-3"> <h6>Published with Ghost</h6> <p>This site runs entirely on <a href="https://ghost.org/">Ghost</a> and is made possible thanks to their kind support. Read more about <a href="https://www.troyhunt.com/its-a-new-blog/">why I chose to use Ghost</a>.</p> </div> </div> </div> <div class="footer_info_block container"> <div class="footer_info wrap"> <div class="footer_socialIcons"> <ul> <li class="socialIcons_link"> <a class="fa-brands fa-x-twitter" href="https://twitter.com/troyhunt"></a> </li> <li class="socialIcons_link"> <a class="fa-brands fa-facebook-f" href="https://www.facebook.com/troyahunt"></a> </li> <li class="socialIcons_link"> <a class="fa-brands fa-linkedin-in" href="https://www.linkedin.com/in/troyhunt"></a> </li> <li class="socialIcons_link"> <a class="fa-brands fa-youtube" href="https://www.youtube.com/user/troyhuntdotcom"></a> </li> <li class="socialIcons_link"> <a class="fa fa-rss" href="https://feeds.feedburner.com/TroyHunt"></a> </li> </ul> </div> </div> </div> </footer> <script type="text/javascript"> !function(s){"use strict";function e(s){return new RegExp("(^|\\s+)"+s+"(\\s+|$)")}function n(s,e){var n=a(s,e)?c:t;n(s,e)}var a,t,c;"classList"in document.documentElement?(a=function(s,e){return s.classList.contains(e)},t=function(s,e){s.classList.add(e)},c=function(s,e){s.classList.remove(e)}):(a=function(s,n){return e(n).test(s.className)},t=function(s,e){a(s,e)||(s.className=s.className+" "+e)},c=function(s,n){s.className=s.className.replace(e(n)," ")});var i={hasClass:a,addClass:t,removeClass:c,toggleClass:n,has:a,add:t,remove:c,toggle:n};"function"==typeof define&&define.amd?define(i):s.classie=i}(window); !function(){function e(){n()}function n(){i.addEventListener("click",t),d&&d.addEventListener("click",t),c.addEventListener("click",function(e){var n=e.target;a&&n!==i&&t()})}function t(){a?classie.remove(o,"show-menu"):classie.add(o,"show-menu"),a=!a}var o=document.body,c=document.querySelector("#container"),i=document.getElementById("open-button"),d=document.getElementById("close-button"),a=!1;e();for(var r=function(e){e.preventDefault(),window.open(this.href,"social-share","width=580,height=296")},s=document.getElementsByClassName("article-open_window"),u=0;u<s.length;u++)s[u].addEventListener("click",r,!1)}(); function showUnsponsored(){document.getElementById("sponsor_message").innerHTML='<a href="/sponsorship"><strong>Sponsored by:</strong> I\'m unsponsored today, click to learn more</a>'}function showNoEvents(){document.getElementById("events_list").innerHTML="<p>No upcoming events were found</p>"}function showSponsor(e){null===e?showUnsponsored():document.getElementById("sponsor_message").innerHTML='<a href="'+e.campaignUrl+'" target="_blank" rel="noopener"><strong>Sponsored by:</strong> '+e.message+' <i class="fa fa-external-link" aria-hidden="true"></i></a>'}function showEvents(e){if(0===e.length)showNoEvents();else{var n="<ol>";e.forEach(function(e){n+=null!==e.url?'<li><a href="'+e.url+'">'+e.name+": "+e.date+", "+e.location+"</a></li>":"<li>"+e.name+": "+e.date+", "+e.location+"</li>"}),n+="</ol>",document.getElementById("events_list").innerHTML=n}}var xmlhttp=new XMLHttpRequest,url="https://bloghelpers.troyhunt.com/api/BlogData";xmlhttp.onreadystatechange=function(){if(4===this.readyState&&200===this.status){var e=JSON.parse(this.responseText);null===e||void 0===e?(showUnsponsored(),showNoEvents()):(showSponsor(e.currentSponsor),showEvents(e.events))}else 4===this.readyState&&200!==this.status&&(showUnsponsored(),showNoEvents())},xmlhttp.open("GET",url,!0),xmlhttp.send(); var disqus_config=function(){this.page.url=document.getElementById("disqus_post_url").getAttribute("data"),this.page.identifier=document.getElementById("disqus_post_identifier").getAttribute("data")};!function(){if(null!=document.getElementById("disqus_post_identifier")&&null!=document.getElementById("disqus_post_url")){var t=document,e=t.createElement("script");e.src="//troyhunt.disqus.com/embed.js",e.setAttribute("data-timestamp",+new Date),(t.head||t.body).appendChild(e)}}(); function reCaptchaSolved(){document.getElementById("subscribe_loading").style.display="block",document.getElementById("confirm_captcha").style.display="none";for(var e,t=encodeURIComponent(document.getElementById("email_to_subscribe").value),n=encodeURIComponent(window.location),c=document.getElementsByName("email_cadence"),s=0,l=c.length;s<l;s++)if(c[s].checked){e=c[s].value;break}var a=document.getElementById("g-recaptcha-response").value,i="Email="+t+"&EmailCadence="+e+"&SourceUrl="+n+"&g-recaptcha-response="+a,o=new XMLHttpRequest;o.open("POST","https://bloghelpers.troyhunt.com/api/subscribe",!0),o.setRequestHeader("Content-type","application/x-www-form-urlencoded"),o.onload=function(){if(document.getElementById("subscribe_loading").style.display="none",200===this.status)document.getElementById("subscribe_successful").style.display="block";else{var e=JSON.parse(this.responseText),t="FakeOrInvalid"===e?"Uh oh, apparently that's a fake or invalid email, sorry!":"Uh oh, that didn't work.Try submitting it again.";document.getElementById("subscribe_unsuccessful").innerHTML="<p>"+t+"</p >",document.getElementById("subscribe_unsuccessful").style.display="block",document.getElementById("new_subscription").style.display="block"}},o.send(i)}function closeFloatingSubscribe(){floating_subscribe.style.display="none",document.cookie="closeFloatingSubscribe=true; expires=1 Jan 2030 00:00:00 UTC; path=/"}document.getElementById("subscribe_form").addEventListener("submit",function(e){if(e.preventDefault(),document.getElementById("new_subscription").style.display="none",window.___grecaptcha_cfg)grecaptcha.reset();else{var t=document.createElement("script");t.setAttribute("src","https://www.google.com/recaptcha/api.js"),document.head.appendChild(t);var n=document.createElement("div");n.setAttribute("class","g-recaptcha"),n.setAttribute("data-sitekey","6LdqYhoUAAAAADieTdwsCSDl0-zIpp6Ga-JzatIZ"),n.setAttribute("data-callback","reCaptchaSolved"),document.getElementById("confirm_captcha").appendChild(n)}document.getElementById("confirm_captcha").style.display="inline-block"});var floating_subscribe=document.getElementById("floating_subscribe");document.cookie.indexOf("closeFloatingSubscribe")===-1&&(floating_subscribe.style.display="block");var closeElement=document.getElementById("close_floating_subscribe");closeElement.addEventListener("click",function(e){closeFloatingSubscribe(),e.preventDefault()},!1);var subscribeElement=document.getElementById("subscribe_link");subscribeElement.addEventListener("click",function(){closeFloatingSubscribe()},!1); </script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10