Migrated password hashes support

(Michael - DiscourseHosting.com) #1

Here is the password migration support plugin:

The original thread follows.

We do regular forum conversions (yes, we’ll open source the converter once it’s stable enough) and one of the big quirks when migrating to Discourse is the fact that all users have to set a new password, because the password in the original forum is encrypted.

So we thought of the following mechanism.

  • when creating users in the conversion step, we store the original password hash in a custom field
  • when a user logs in for the first time and the password that was entered is incorrect, the login mechanism uses the original hash method of the former forum and calculates and compares the hash with the stored value
  • if there is a match, the password is set to the entered password and the user is logged in. The original hash can now be cleared.

This sounds like a neat plugin :smile:

Two questions:

  • Any comments, objections (security?) and maybe even something that is even better?
  • Can someone give us any pointers about which places to hook and how to store such a custom field?

Questions About Migrating From Xenforo
Importer support for migrated passwords
I need ideas for a migration strategy from dual logins and phpBB to new SSO and Discourse
How to connect my (existing) User Database?
Importing from phpBB3
Members problem on importing Mybb to Discourse
How to migrate from Vanilla to Discourse!
Password settings changed after Rebuild. Users unable to login
Docker deployment with custom site_settings.yml
[Paid] Migration from Flarum to Discourse
(lid) #2

1.The plugin should probably have its own table To manage the process.

User_id | hash | hash_type | progress_status

2.This kind of plugin will introduce additional overhead for all login. But it is not that bad.
And it may be worth it for the extra convenience to the end user.

3.The plugin should be disabled after the majority of user base finalized their login.

4.Depends on the system you are migrating. You might need to implement different hash algorithms and be aware if “salt” was used.

5.You will have to keep track of migrated users and only perform the additional check on their first login. If the user reset their password the plugin will not get involve in any future login of that user.

I am not sure what is the best practices to introduce custom fields to built in tables on discourse

(Michael - DiscourseHosting.com) #3

Thanks for your feedback,

  1. hash_type could be a global setting, after all, all users on a certain forum will have the same hash type, progress_status is not needed, since the hash can be cleared after use. So a custom field will do.

  2. no, only for a failed login, and indeed (3) only for the first few days/weeks

Ok - I found the way how to add a custom user field

Now we just need to know how to override the login…

(Jens Maier) #4

This is reeeeally hacky, barely tested and requires Ruby ≥ 2.0:

The idea here is that old passwords are stored as salted SHA1 hashes and the import process has stored these in the form "#{salt}:#{password}" in a custom field named import_pass.

(Kane York) #5

You’ve got a rogue rescue and binding.pry in there… :wink:

(Jens Maier) #6

Whoops… but I did say that it’s hacky. :wink:

Suggestion: ability to send password reset emails from the Admin page of a user
(Michael - DiscourseHosting.com) #7

Nice - I’m going to turn this into a plugin!

(Michael - DiscourseHosting.com) #8

Ok, extended this to a neat little plugin that supports vBulletin, MD5 and Wordpress hashes

Thanks @elberet !

(Jens Maier) #9

Nice work. Though I’d have extended the code a bit to support an algorithm identifier as part of the password string that would be used to delegate the crypto to a single class instead of trying all available hash functions… :slight_smile:

(Michael - DiscourseHosting.com) #10

Yeah, I (of course :smiley: ) actually thought about that, but decided not to, for a few reasons:

  • this would require naming all those non-standard algorithms, introducing a new kind of semi-standard, which requires more thought that you’d expect. For instance, I would be naming the md5(md5(pass) + salt) method vbulletin and two months I’d find out that forum X uses the same algorithm, and we would end up with something very counter-intuitive.
  • ease of parsing. Your gist had a nasty bug in salt, password_hash = self.custom_fields['import_pass'].split(':') because salt can contain a colon. I turned them around and used the limit parameter in this case but this made me decide to treat all crypted password strings as an opaque string.

(Jens Maier) #11

Did I mention that it was really hacky? :grin:

I actually thought of that case but decided against spending more time on a solution because, after all, both packing the password data into a custom field and parsing that data would be heavily implementation specific. Although in hindsight, packing and unpacking JSON data would have been sufficiently simple to implement. :sweat_smile:

(Michael - DiscourseHosting.com) #12

Hm. Didn’t think of that either.


I used this plugin for a migration yesterday and although it was working okay in a test migration, now on the live site it’s failing. :frowning:

They are all md5 hashes but the console shows a problem with BCrypt:

BCrypt::Errors::InvalidHash (invalid hash)
/var/www/discourse/plugins/discourse-migratepassword/gems/2.3.1/gems/bcrypt-3.1.3/lib/bcrypt/password.rb:60:in `initialize' 

Can I disable the use of this gem as I don’t need it? I commented out all lines in plugin.rb that my migration won’t need but that seems to have no effect, the error remains. :thinking:

(Michael - DiscourseHosting.com) #14

I’ve pushed a fix.
Can you please upgrade the plugin to latest and try again?


Yes, now it is working again! :smiley: :heart:

(Prakarsh Upmanyu) #16

Hi everyone,
I have been trying to migrate from vBulletin to Discourse. I was following this link to do that: Importing from vBulletin 4
I updated the import script to copy over the password hashes and salt from my vBulletin DB to Discourse DB. The database has been successfully migrated.

The problem I am having is that the old users are not able to log in due to different password hashing scheme followed by Discourse. So basically copying over those password hashes was futile.

Then I came across this plugin which would convert my old hashes to the ones that Discourse uses.
After installing this plugin, I am still not able to log in using an old user.
I am not sure where to make make the suggested change for this plugin to work:

user = User.find_by(username: 'user')
user.custom_fields['import_pass'] = '5f4dcc3b5aa765d61d8327deb882cf99'

Can anyone guide me with this?

Thanks in advance.


Hi. I am thankful for this plugin, it’s proving very useful for my Kunena4 migration.

But I ran into problems which were quite hard to understand - only after setting up an IDE and debugging through the code I was able to figure out why my logins weren’t being accepted.

The passwords I was trying were right, and the hashing was also working fine. The problem was with Discourse’s password requirements:

  • I had to go in Admin / Settings / Plugins and check migratepassword allow insecure passwords

  • I had to go in the database and run UPDATE email_tokens SET confirmed = TRUE to bypass the email tokens mechanism (note that my approach was a bit brute force here - if you have existing users you’re not migrating, use a less aggressive approach)

The difficulty was especially with the first part, because I had no clue why the login was being rejected. I hope this helps someone.

And again, thanks for the plugin! :slight_smile:

(Michael - DiscourseHosting.com) #18

This is more an issue with the importer, independent of the migratepassword plugin.