Rails link_to with urls

by Martin Westin in


Being something I do seldom this little gotcha caught me today.

So, you want to put a link into an email template or a rendered pdf or something similar that requires the full url of your route. Being an email or a pdf it is also likely that the contents will be printed to just rendering our the entire url in the template can be a good thing. However...

# the basic
link_to something_url
# is not the same as
link_to something_url, something_url

The first one looks good. You get the full url including https:// and all. The gotcha is that Rails, being the opinionated and free-thinking framework it is, will strip out the url-part and just put the path part into the href. To make the visible link match the href you actually need to tell Rails explicitly that's what you want.


Transitioning to more secure passwords

by Martin Westin in


With all the news of hacked databases (mostly at Sony) and the clear-text or poorly hashed passwords in their datasets, I thought I might offer my standard trick for transitioning to a more secure form of hashing. I think some sites don't change passwords security for fear of annoying users or the workload involved in managing a transition. This simple technique is completely invisible to the user and very low maintenance for the developer.

I will be giving examples from the Devise library for Rails apps, since I recently implemented it there.

The technique is very very simple

You configure your authentication to check passwords against both the old and the new form of hashed password. And when you find a match for the old hash you update your database with the version of the password encoded using the new hash. You keep this dual check in place until all (or most likely most) of your users have logged in and had their passwords changed. The unlucky few can use your password recovery feature if you have one.

Metacode of the basic principle:

if new_hash(password) == stored_password
  // ALLOW LOGIN USING AN UP-TO-DATE PASS
else
  if old_hash(password) == stored_password
    // UPDATE PASSWORD IN DB
    // ALLOW LOGIN
  else
    // DISALLOW LOGIN
  end
end

How to implement this transition in Devise

I implemented this by overriding the method valid_password? injected into your User model.

class User

  def valid_password?(incoming_password)
    result = super incoming_password
    if !result
      # try old encryptor during transition
      digest = Devise::Encryptors::LegacyEncryptor.digest(incoming_password, self.class.stretches, self.password_salt, self.class.pepper)
      result = Devise.secure_compare(digest, self.encrypted_password)
      if result
        # update password to use new encryptor when there is a match
        self.password = incoming_password
        self.save
      end
    end
    result
  end

end

Fairly simple. You may need to hard-code some parameters (salt, stretching, pepper) if they cause problems.

If you are changing from, say, sha1 to sha256, you can easily check the character lengths of the passwords in your database to check the "adoption rate" of the new hashes.

Implications on Security

You should realize that you ARE lowering your security level slightly by effectively allowing 2 different password checks. In reality this problem is small and only really matters if you have plain passwords you are transitioning from (and you really shouldn't have). The problem then becomes real since I could login using a stolen new (supposedly) secure hash as the given password. In this case I would definitely disallow any password of the same length as, or simple reg-ex match for, your new hashing system to avoid this hole.

You will also not fully benefit from the new hashing system until you remove the "dual check" after a reasonable period of time.

If you can live with that to gain the benefits of a clean migration for you and your users this is a nice technique. I know from reading and talking to developers that I am far from the only of the first to come up with something like this. Many apps and sites have used and continue to use this kind of technique to beef-up password hash-strength without bothering users.


Graylog2 on Mac OS X

by Martin Westin in


I have been playing with Graylog2 on my Mac today. Since the setup guides are all for Debian and not fully compatible with Mac OS X I thought I'd mention the changes I needed to make to get thing rolling smoothly. The guides are good, so go read them in the wikis on Github. I won't re-iterate them, only point out the minor changes and tweaks I had to make.

Graylog2 comes in two main parts. The server and the web interface. I'll start with the server component.

Install The Server

https://github.com/Graylog2/graylog2-server/wiki/Installing

Mac OS X has java bundled with the OS (for now). There is no need to install anything. The configuration file needs one non-obvious tweak.


mongodb_host = 127.0.0.1 # localhost

Java resolves localhost to the strangest thing. It tries to connect to the Bonjour name and external IP (e.g. Martin's Mac/192.168.0.2) instead of 127.0.0.1 which is what you want. Instead of opening MongoDB up to external access I changed the configuration to point to the loopback IP directly.

Starting The Server

https://github.com/Graylog2/graylog2-server/wiki/Starting-the-server

I didn't get the daemon script to start and did not investigate is since I run Graylog2 for evaluation and development and like seeing the output. Starting by running the jar file requires that you sudo.


sudo java -jar graylog2-server.jar debug

That gets Graylog2 running and spitting out a lot of fun info so you know you are logging thing as you expect.

Installing The Web Interface

https://github.com/Graylog2/graylog2-web-interface/wiki/Installing-the-web-interface-on-Debian-5.0

You can follow most of those steps if you don't have rails and Bundler and that stuff installed. For testing and development, I would suggest running the interface using Passenger Standalone instead of Apache. And, you install Passenger as a gem and not apt, of-course.

http://www.modrails.com/documentation/Users%20guide%20Standalone.html

The cool thing about installing passenger standalone is that it will compile and run itself the first time you call passenger start. It will take a few minutes that first time but after that it will start instantly.

Logging from your Rails app

https://github.com/Graylog2/graylog2_exceptions

In the Rails app I want to log from I installed Graylog2 Exceptions. It is a small Rack middleware with practically no configuration. Only problem is that it has not been updated to comply with the current version of the Graylog2 server. Until it is updated, you have to modify the source for it. A very small mod. For me it is ok as long as I am still on my Mac and not a server.

first

> cd /to/my/app/dir
> bundle open graylog2_exceptions

This should get you the installed gem open in your editor. In the file lib/graylog2_exceptions.rb you need to add the version parameter to the notification message. Possibly this should be added to the gelf gem instead. I am not sure how that version string is supposed to be used.

Here is the modified method that does the actual notification:

  def send_to_graylog2 err
    begin
      notifier = GELF::Notifier.new(@args[:hostname], @args[:port])
      puts notifier.notify!(
        :version => "1.0",
        :short_message => err.message, # <- this line is new!!!
        :full_message => err.backtrace.join("\n"),
        :level => @args[:level],
        :host => @args[:local_app_name],
        :file => err.backtrace[0].split(":")[0],
        :line => err.backtrace[0].split(":")[1]
      )
    rescue => i_err
      puts "Graylog2 Exception logger. Could not send message: " + i_err.message
    end
  end

So, that is it. Finally I get all my exceptions in Graylog2. To try it out you can just raise some dummy exception – raise "Dummy Exception Error" – here and there and see them pop up in Graylog2.