<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eden</title>
    <description>The latest articles on DEV Community by Eden (@eande171).</description>
    <link>https://dev.to/eande171</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3861026%2F71b94582-0dc7-4382-ab4d-4a3bb7d35e27.png</url>
      <title>DEV Community: Eden</title>
      <link>https://dev.to/eande171</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eande171"/>
    <language>en</language>
    <item>
      <title>Why Your Signup Form Is Less Secure Than You Think (And How to Fix It)</title>
      <dc:creator>Eden</dc:creator>
      <pubDate>Wed, 08 Apr 2026 04:22:00 +0000</pubDate>
      <link>https://dev.to/eande171/why-your-signup-form-is-less-secure-than-you-think-and-how-to-fix-it-3m3f</link>
      <guid>https://dev.to/eande171/why-your-signup-form-is-less-secure-than-you-think-and-how-to-fix-it-3m3f</guid>
      <description>&lt;p&gt;You've seen those password rules. &lt;em&gt;"Must be more than 8 characters. Must include a symbol. Must contain a number."&lt;/em&gt; They have good intentions, but have one fatal flaw. Us.&lt;/p&gt;

&lt;p&gt;You would hope everyone uses something more than "&lt;code&gt;P@ssword1&lt;/code&gt;" (or any of its many variants), but unfortunately, you'd be wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4a1su87rodk0xjl54h5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4a1su87rodk0xjl54h5b.png" width="800" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo of the&lt;/em&gt; &lt;a href="https://eande171.github.io/bastion/demo/" rel="noopener noreferrer"&gt;&lt;em&gt;Bastion Demo&lt;/em&gt;&lt;/a&gt; &lt;em&gt;that shows the password: "P@ssword1" being included in 442,781 known breaches.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So what is actually happening, and what can we do about it?&lt;/p&gt;




&lt;p&gt;These "traditional" password rules weren't &lt;em&gt;wrong&lt;/em&gt; to exist, but they focus on the wrong thing. Primarily, they focus on what makes a password &lt;em&gt;look&lt;/em&gt; complex, rather than what a machine considers "complex".&lt;/p&gt;

&lt;p&gt;That'd be fine, except most attacks don't work that way. Most passwords susceptible to this are brute forced, rather than recreated from over your shoulder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NIST&lt;/strong&gt; (the National Institute of Standards and Technology) actually updated their guidelines recently, recommending longer minimum lengths and preventing the use of common, expected and compromised passwords.&lt;/p&gt;

&lt;p&gt;Most people, understandably (my younger self included), took shortcuts, prioritising short, simple passwords (usually for memorisation) over complexity.&lt;/p&gt;

&lt;p&gt;Now databases are littered with these "valid", but trivial passwords that can take machines minutes, if that, to crack (obviously this is still better than NO rules... "&lt;code&gt;123456&lt;/code&gt;" has been found an embarrassingly large number of times in breaches).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qrb1vx3rznpw6lbb385.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qrb1vx3rznpw6lbb385.png" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo of the&lt;/em&gt; &lt;a href="https://eande171.github.io/bastion/demo/" rel="noopener noreferrer"&gt;&lt;em&gt;Bastion Demo&lt;/em&gt;&lt;/a&gt; &lt;em&gt;that shows the password: "123456" being included in 209,972,844 known breaches.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What we &lt;strong&gt;actually&lt;/strong&gt; want to measure is the complexity of guessing (or brute forcing) said password, which is a very different problem.&lt;/p&gt;




&lt;p&gt;zxcvbn (I only just realised now, while writing this, that it's the bottom row of the QWERTY keyboard... never clicked when I was using it for the API ;-;) was built by Dropbox and takes a very different approach to password strength. Rather than checking for these rules, it uses more complex pattern matching to estimate the number of guesses it would take to crack a password.&lt;/p&gt;

&lt;p&gt;zxcvbn checks against things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Most common passwords, names, and dates&lt;/li&gt;
&lt;li&gt;  Keyboard patterns (&lt;code&gt;qwerty&lt;/code&gt;, &lt;code&gt;123456&lt;/code&gt;, and even &lt;code&gt;zxcvbn&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  Common substitutions (&lt;code&gt;@&lt;/code&gt; for &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;3&lt;/code&gt; for &lt;code&gt;e&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  Repeating/reversed characters or strings (&lt;code&gt;aaabbbccc&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a score, crack times against multiple scenarios (throttled online, offline fast hash, etc.), as well as warnings and suggestions for weaker passwords.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad password example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc75mm1fhj0l0lmim29g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc75mm1fhj0l0lmim29g.png" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo of the&lt;/em&gt; &lt;a href="https://eande171.github.io/bastion/demo/" rel="noopener noreferrer"&gt;&lt;em&gt;Bastion Demo&lt;/em&gt;&lt;/a&gt; &lt;em&gt;that shows the password: "P@ssword1" being included in 442,781 known breaches. It also shows the estimated crack time ranging from 4 days to less than a second. Below that is a warning with a list of suggestions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good password example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjoz4pxkrvw7t5z60a2x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjoz4pxkrvw7t5z60a2x.png" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo of the&lt;/em&gt; &lt;a href="https://eande171.github.io/bastion/demo/" rel="noopener noreferrer"&gt;&lt;em&gt;Bastion Demo&lt;/em&gt;&lt;/a&gt; &lt;em&gt;that shows the password: "correct-horse-battery-staple" being included in no known breaches. It also shows the estimated crack time ranging from centuries to 57 years.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  In Practice:
&lt;/h2&gt;

&lt;p&gt;Here's what it looks like to actually check a password against an API that uses zxcvbn:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://bastion.eande171.workers.dev/v1/evaluate 
BODY { "password": "P@ssword1" }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Weak"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entropy_bits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;13.425215903299385&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"crack_times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"online_throttled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4 days"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"online_unthrottled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"18 minutes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"offline_slow_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1 second"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"offline_fast_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"less than a second"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"warning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is similar to a commonly used password."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"suggestions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Add another word or two. Uncommon words are better."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Capitalization doesn't help very much."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Predictable substitutions like '@' instead of 'a' don't help very much."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This response gives you a lot of information to work with. The &lt;code&gt;score&lt;/code&gt; and &lt;code&gt;strength&lt;/code&gt; fields give you a numerical and human-readable version of the password strength.&lt;/p&gt;

&lt;p&gt;An attacker with throttled attempts would find this password in 4 days (likely because the form would start rejecting too many attempts). This drops to 18 minutes without any throttling, and if they got access to the database, they could calculate it in no time at all.&lt;/p&gt;

&lt;p&gt;The warning and suggestion fields are an addition given to weak passwords to give users clear, actionable feedback to improve the quality of their passwords.&lt;/p&gt;

&lt;p&gt;This can be compared to a password (or passphrase), which is just a little bit longer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST https://bastion.eande171.workers.dev/v1/evaluate 
BODY { "password": "correct-horse-battery-staple" }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Very Strong"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"entropy_bits"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"crack_times"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"online_throttled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"centuries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"online_unthrottled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"centuries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"offline_slow_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"centuries"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"offline_fast_hash"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"57 years"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"warning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"suggestions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fills me with much more confidence, knowing that even if someone did get access to a database, it would take them 57 years (or possibly centuries) to crack it (unless it's stored in plain text... but that's a separate issue- although just as bad).&lt;/p&gt;




&lt;p&gt;Password rules certainly aren't useless, but genuine protection requires measuring complexity, not enforcing arbitrary rules. zxcvbn (still can't believe I didn't get that) gives you a way to do that without reinventing the wheel.&lt;/p&gt;

&lt;p&gt;Unfortunately, this is only one half of the problem... what happens if a password is strong but is already in a breach? &lt;em&gt;It's more common than you think.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If any of you made it to this bit... THANK YOU!!! If you want to poke around the API I made for this &lt;strong&gt;exact&lt;/strong&gt; purpose, you can find it &lt;a href="https://rapidapi.com/eande171-RQXKDUFxT/api/password-strength-and-breach-detection-api" rel="noopener noreferrer"&gt;here.&lt;/a&gt; It would really mean a lot if you could check it out (even just the free/demo versions).&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>api</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
