Using ruby to generate SHA512 crypt-style hashes formatted for /etc/shadow?
After further research:
The mkpasswd command, which on debian is in the
whois
package (weird):mkpasswd -m sha-512
String#crypt does actually call the platform's native crypt() call, however OSX (up to 10.6) does not include support for alternate ciphers. "password".crypt('$6$somesalt') will work on Linux platforms.
Platform-independently generate Linux password in Modular Crypt Format (MFC) SHA512 for /etc/shadow in Ruby
As you discovered, the crypt
method is platform-dependent. The algorithms it has available depend on the host. macOS only supports DES. Modern Linux distros will likely support every modern cipher. Windows -- well that's a total crapshoot and depends on how you're running Ruby. (Are you using WSL? Running Ruby directly on Windows? Something else?)
It's best to leave crypt
out of the picture and use a library to handle it for you. First, install unix-crypt:
gem install unix-crypt
Then use it to generate your hash:
require 'unix_crypt'
# This is the hash you generated on Linux with crypt for your example
expected_hash = '$6$123SaLt9$idgUFrrwVkpDMcoHj7SAYH0UwCY6LymbsR9yTDrelYgcZ2wstoynmLIY83qBF0/BIT4Od.rQL2g3n2jEG/VXp/'
# Generate the same hash using unix-crypt instead
hash = UnixCrypt::SHA512.build('mypassword', '123SaLt9')
# Verify they match (returns true)
expected_hash == hash
This works for me on macOS where your original example does not:
"mypassword".crypt("$6$" + "123SaLt9")
=> "$6MrNddDu3Hf6"
So it should probably work on Windows as well. If it doesn't then I recommend you look at how your Windows users are running Ruby and switch them over to Windows Subsystem for Linux and have them use the specific Linux distro that you are using.
Once they have Linux up and running they can install Ruby and from the perspective of the Ruby interpreter it will be running on Linux, not Windows, so its access to crypt
algorithms should be the same as what you have, and you should be able to use crypt
as you did in your original example.
Alternatively, you can use the unix-crypt library from my example as a more universally compatible method of generating the hashes regardless of distro.
How to convert php crypt function (SHA512) to ruby?
irb(main):001:0> salt = 'fGn9LR75';
irb(main):002:0* hash = 'test'.crypt('$6$' + salt);
irb(main):003:0* hash
=> "$6$fGn9LR75$YpI/vJHjEhvrYp5/eUSRinpiXdMthCxFWSEo0ktFNUaRBsA7pCWYzzmQptmnfyHno9YEJFNHYuESj3nAQmSzc1"
The crypt()
algorithm for SHA256/512 is not simply a base64-encoded hash. It's an intentionally crazy process which involves multiple hashes running in parallel.
Is it possible to generate a longer hash value with string#crypt?
String#crypt
ignores any extra characters, so only the first two are used.
"foobar".crypt("abc") # => abVbJXzHUY99s
"foobar".crypt("abd") # => abVbJXzHUY99s
Instead, you can use SHA1 to give you a 40 character encryption and allow any length of salt.
require 'digest/sha1'
Digest::SHA1.hexdigest("foobar" + "abc") # => 17dd6cae99582672c4b2ccc78fe4ad0888559ce7
Digest::SHA1.hexdigest("foobar" + "abd") # => 8aba27fd409286946504ac78098c41549d182316
UPDATE: as Gaius pointed out, SHA1 is not best for production. Instead use SHA256 or SHA512. See his response for details.
python, get encrypted user password from shadow
Try the spwd module
Platforms: Unix
New in version 2.5.
This module provides access to the Unix shadow password database. It is available on various Unix versions.
You must have enough privileges to access the shadow password database (this usually means you have to be root).
Shadow password database entries are reported as a tuple-like object, whose attributes correspond to the members of the spwd structure (Attribute field below, see ):
>>> import spwd
>>> spwd.getspnam('root')
spwd.struct_spwd(sp_nam='root', sp_pwd='!', sp_lstchg=15238, sp_min=0, sp_max=99999, sp_warn=7, sp_inact=-1, sp_expire=-1, sp_flag=-1)
Remember, you need to have read permission of /etc/shadow
for this to work
Generate cross-platform compatible Blowfish hash (PHP/Ruby)
The BCrypt gem does what we need:
BCrypt::Engine.hash_secret("bob", "$2a$10$ ... salt here ...")
How best to upgrade to password_* functions from hash('sha512','salt')
Hashes created with password_hash
will have a very distinctive $2y$
string at the beginning (or similar $..$
, as long as you're operating with the current default Blowfish cypher), while SHA256 will simply be all hex values. Therefore, you can simply test whether a value is a legacy hash value or a password_hash
value:
function isLegacyHash($hash) {
return !preg_match('/^\$\w{2}\$/', $hash);
}
Using this, you can keep both types of hashes in a single field and upgrade them when the user logs in. Alternatively, you could simply set a flag in a column like hash_version
.
Ruby where is etc module file and can it be customized?
In general, you can use
SomeClass.method(:foo).source_location
to find out where (on disk) a method is defined. I don't think this was available in ruby 1.8, but even it is the result is nil, because these methods are implemented in C. The ruby 1.9 implementation is here for example. Somewhere in /usr/lib/ruby/1.8 there should be an etc.so (on linux or etc.bundle on os x and so on)
This doesn't mean that you can't overwrite the method, but it does mean that you can't just edit the source as you would with a plain .rb file (you'd have to recompile the extension afterwards and move it to the correct location, which is system dependant)
Related Topics
Error Running Heckle? 'Current_Code': Undefined Method 'Translate' for Ruby2Ruby
Sorting a Hash in Ruby by Its Value First Then Its Key
In Ruby or Rails, Why Is "Include" Sometimes Inside the Class and Sometimes Outside the Class
How to Connect to Browser Using Ruby Selenium Webdriver
Grabbing Snapshots from Webcams in Ruby
Run System Command in Ruby and Interact with It
Passing Params to Cancan in Ror
How to Understand Sender and Receiver in Ruby
How to Add New View to Ruby on Rails Spree Commerce App
Rails Active Record: Find in Conjunction with :Order and :Group
Run a Command Line with Custom Environment
Get Response Headers from Curb
Getting Ruby Function Object Itself