Automatic password suggestion for your Rails app

Posted by Jeremy Voorhis Thu, 06 Apr 2006 06:17:00 GMT

Note that this will only work on UNIX systems.

in app/helpers/admin/accounts_helper.rb:

module Admin::AccountsHelper
  def password_recommendation
    return "<p>Suggested password: <code>#{`apg`.split("\n").first}</code></p>" if use_apg?
  end

  # $ apg -v  
  # APG (Automated Password Generator)
  # version 2.2.3 (PRNG: X9.17/CAST)
  # Copyright (c) 1999, 2000, 2001, 2002, 2003 Adel I. Mirzazhanov
  def use_apg?
    `apg -v`.grep( /APG \(Automated Password Generator\)/ ) ? true : false
  end
end 
In app/views/admin/accounts/_form.rhtml:

  ...
  <h3><label for="user[password]">Password</label></h3>
  <div>
    <%= password_recommendation %>
    <%= password_field :user, :password %>
  </div>
  ...

If apg is available on your system, you should see something like the following in your form:

Suggested password: JidIlvEywr

Update

try this instead ;)


def use_apg?
  `apg -v`.grep( /APG \(Automated Password Generator\)/ ).blank?? false : true
end

The original implementation was just plain incorrect, as #grep will return an empty Array when nothing is found. Empty Arrays are #blank?, but will evaluate to true.

Comments

  1. Scott Becker said about 5 hours later:

    Here’s something I use, and it’s all ruby. Need more variation? Just add more source characters! Longer length? 1 up to whatever…

    def generate_password
      source_characters = "0124356789abcdefghijk" 
      password = "" 
      1.upto(8) { password += source_characters[rand(source_characters.length),1] }
      return password
    end
  2. Tomas Jogin said about 7 hours later:

    Because generated passwords are impersonal, I try to generate ones that are easy to speak out loud, in order to remember them:

    <code> def generate_password new_password = ”” consonants = “bcdfghjklmnprstv”; vowels = “aeiou”; </code>

    3.times do
          new_password &lt;&lt; consonants[rand(consonants.size-1)]
          new_password &lt;&lt; vowels[rand(vowels.size-1)]
        end
        new_password &lt;&lt; (rand(89)+10).to_s
    self.password = new_password
    end
  3. Tomas Jogin said about 7 hours later:

    Because generated passwords are impersonal, I like to generate ones that are easy to speak out loud, in order to make them easier to remember:

    <pre><code> def generate_password new_password = ”” consonants = “bcdfghjklmnprstv”; vowels = “aeiou”; 3.times do new_password << consonants[rand(consonants.size-1)] new_password << vowels[rand(vowels.size-1)] end new_password << (rand(89)+10).to_s self.password = new_password end </code></pre>

  4. Tore Darell said about 9 hours later:

    Ruby/Password is also great for working with passwords..

    &gt;&gt; require 'password'
    =&gt; true
    &gt;&gt; Password.phonemic
    =&gt; "fequohhi" 
    &gt;&gt; Password.phonemic(10)
    =&gt; "edoozoodir"

    It can check for weak passwords too:

    &gt;&gt; Password.new('hitler').check
    Password::WeakPassword: it is based on a dictionary word
  5. JV said about 15 hours later:

    Yes, a pure Ruby solution would have been excellent, but our systems administrator David has been using apg lately for things like subversion passwords, etc. The strength of it is that it produces random passwords containing letters and numbers – with an optional pronunciation guide!

    I admittedly did not spend enough time on the subject to research a pure ruby solution, but apg helped me Get Things Done in a pinch.

    By the way, this is getting pushed down from the helper into the controller ;)

  6. Bob said about 16 hours later:

    @Tomas Jogin,

    Your code has a bug. rand(x) will return a random integer in the range 0..x-1. So you should not be subtracting 1 from the size of your strings, and for your final two-digit number, the parameter to rand should be 90.

    Your technique (when implemented correctly) can generate 46 million different passwords, which is 25 bits of randomness. Depending on your adversary, this might not be much at all. If you wanted this to be a front end for 128 bit AES , you would be well short of what would be required, wasting much of the power of AES .

    Bob

  7. Bob said about 16 hours later:

    @Tomas Jogin,

    Your code has a bug. rand(x) will return a random integer in the range 0..x-1. So you should not be subtracting 1 from the size of your strings, and for your final two-digit number, the parameter to rand should be 90.

    Your technique (when implemented correctly) can generate 46 million different passwords, which is 25 bits of randomness. Depending on your adversary, this might not be much at all. If you wanted this to be a front end for 128 bit AES , you would be well short of what would be required, wasting much of the power of AES .

    Bob

  8. Tomas said about 18 hours later:

    @Bob: This technique is not used for a front end of a 128 bit AES , so it’s cool. Thanks for the tip btw.

  9. O-(^_^ Q) said 3 days later:

    {{{{ def use_apg? `apg -v`.grep( /APG \(Automated Password Generator\)/ ).any? end

    def use_apg? !`apg -v`.grep( /APG \(Automated Password Generator\)/ ).empty? end [/code] }}}}

  10. O-(^_^ Q) said 3 days later:

    {{{{ def use_apg? `apg -v`.grep( /APG \(Automated Password Generator\)/ ).any? end

    def use_apg? !`apg -v`.grep( /APG \(Automated Password Generator\)/ ).empty? end [/code] }}}}

  11. O-(^_^ Q) said 3 days later:

    Ok, sorry for the double post (triple now). You really need a little box that says how to format.

    Use #any?, or use !#empty?

  12. Tony Collen said 6 days later:

    Shelling out to a command? No thanks… I’ll stick with a pure Ruby solution.

(leave url/email »)