Hiding passwords in Rails log files

If you matter a bit about security, you certainly don't want the user's passwords to be cleartext in the log files of your Rails application. Believe me (or not) but your application is very likely making all the passwords available in production.log if you have not taken any counter-measure yet.

Just because a password is a parameter like the others most of the time, it consequently gets logged by Rails when it reaches a controller method. This is a bit annoying considering the time we spend on advanced security options like a SSL setup or the hashing of passwords in the database.

Hopefully, Rails has the solution, easy to setup. Once again, it's a bit hidden in the tons of methods the framework proposes, but this is the charm and beauty of it.

Assuming you use the word 'password' to name every parameter that deals with a password (in the registration, login and user edition forms), just add one line of code in the app/controllers/application.rb file.

class ApplicationController < ActionController::Base
  # filter out password parameters from log files
  filter_parameter_logging :password  
end

If your naming scheme is a bit more complex, don't worry because filter_parameter_logging accepts a list of words and a block that should be enough for matching all the cases. If it does not convey your choices, think about changing your code to simplify your naming of parameters.

After doing so, the value of any password parameter received by a controller appears as [FILTERED] in the log files of your Rails application, whatever the current environment is.

Thanks Maintainable Software for mentioning this Rails feature in their Rails Logging Tips article.

agiletour.jpgHier après-midi, j'ai assisté à la conférence Agile Tour 2008 (AT2008) organisée à Grenoble par le Club Agile Rhône-Alpes (CARA).

Cette manifestation se déroule dans plusieurs villes en France mais aussi à Genève tout au long du mois d'octobre 2008. Elle a pour but de promouvoir les pratiques agiles dans le développement logiciel. Cette année, les (gentils) organisateurs ont recensé 800 participants au total (dont un peu - de 200 à Grenoble), les inscriptions étant clôturées 2 mois avant les séminaires. Un franc succès donc.

Ce sont Alexandre Boutin et Patrice Petit qui nous ont accueillis dans l'amphi nord de l'université de physique, sur le campus de Saint Martin d'Hères. 3 faits et dires marquants ont plus particulièrement retenu mon attention lors de cette introduction.

  • Atos Origin, l'un des sponsors de la manifestation, réalise un projet stratégique sur 7 ans pour le compte d'ERDF (changement des 35 millions de compteurs électriques chez les particuliers) en mettant en oeuvre une démarche agile,
  • Pyxis, l'invité canadien, qui déclare "faire du logiciel et s'éclater", tout cela grâce aux pratiques agiles,
  • Tout le monde se (re)positionne sur l'agilité: c'est visiblement tendance et sans doute vendeur; ce qui est une bonne et une mauvaise chose à mon humble avis.

A suivi une 'performance' appréciée par le public de la part de Jérôme Barrand, "l'invité du 'dîner de con', toute chose égale par ailleurs...", sur l'agilité, un nouveau paradigme managérial. Mr Barrand, professeur à l'Ecole de Management de Grenoble et consultant, ne s'est pas gêné pour accuser l'informatique et les technologies de l'information d'être la cause de tous les maux de notre société actuelle. Provocation qui a réussi à amuser et capter l'attention de l'assistance. J'ai pris pas mal de notes sur le 'show' et tâcherai d'en faire un prochain post. Sans doute la séance la plus marquante de l'après-midi.

J'ai personnellement assisté à la présentation de Ruby on Rails effectuée par Sogilis, dont j'ai fait la connaissance des fondateurs. Il est toujours agréable de constater que d'autres font les mêmes paris que vous (en l'occurrence RoR) et ont une (courte) expérience assez similaire. Au sortir de cette présentation, on a pu sentir la curiosité et l'intérêt suscité par Rails au niveau régional, sans toutefois pouvoir dire si l'adoption augmente significativement.

J'ai aussi assisté à la présentation sur l'Architecture Agile et la maîtrise de la production logicielle en environnement agile. 2 retours d'expérience très intéressants, où l'on a pu vérifier que l'agilité en développement logiciel ne signifiait pas perte de contrôle. De nombreux outils existent ou sont créés par les entreprises qui adoptent ces pratiques pour piloter et accompagner au mieux les équipes agiles.

Il reste que d'outils, de mesure, de maîtrise et de contrôle, il en a été souvent question. Pour ma part, l'agilité reste avant tout une manière d'aborder un projet de développement logiciel, un état d'esprit en quelque sorte. Les principes régissant les pratiques adoptées par l'équipe étant ceux énoncés par le Manifesto. Du moment que vous adhérez et cherchez à appliquer ces principes dans votre environnement, de manière outillée ou non, vous basculez dans l'agilité. A moins que vous n'y soyez déjà...

Enfin, cet après-midi a aussi été l'occasion de retrouvailles ou de rencontres fortuites qui ont donné lieu à des discussions enrichissantes. RDV est donc pris pour l'Agile Tour 2009.

Parsing Accept-Language in Rails

Recently, I had to know which language is preferred by someone visiting the web site I was developing for. Hopefully, HTTP is well designed enough that you should not have to ask your visitors for this preference but can elegantly guess what it is by analyzing the Accept-Language HTTP request header.
This header is seamlessly sent to the HTTP server by your browser. It's clearly defined by the HTTP 1.1 specification if you want all the details. Briefly, the value is a "set of natural languages that are preferred as a response to the request.". Each language "may be given an associated quality value which represents an estimate of the user's preference for the languages specified by that range. The quality value defaults to 1."
To leverage this header, I simply wrote a "helper" method that I made available to all the controllers. I named this method accepted_languages and put it on the ApplicationController class (RAILS_ROOT/app/models/application.rb). It takes no parameter and returns an array where each element is a pair [language tag, quality], sorted by ascending quality.
Here's the code

class ApplicationController < ActionController::Base
  
  # ...
  
  ##
  # Returns the languages accepted by the visitors, sorted by quality
  # (order of preference).
  ##
  def accepted_languages()
    # no language accepted
    return [] if request.env["HTTP_ACCEPT_LANGUAGE"].nil?
    
    # parse Accept-Language
    accepted = request.env["HTTP_ACCEPT_LANGUAGE"].split(",")
    accepted = accepted.map { |l| l.strip.split(";") }
    accepted = accepted.map { |l|
      if (l.size == 2)
        # quality present
        [ l[0].split("-")[0].downcase, l[1].sub(/^q=/, "").to_f ]
      else
        # no quality specified =&gt; quality == 1
        [ l[0].split("-")[0].downcase, 1.0 ]
      end
    }
    
    # sort by quality
    accepted.sort { |l1, l2| l1[1] <=> l2[1] }
  end
end
Look at how you get the request header inside Rails. It's available from the environment attached to the request through a Ruby home-made key (HTTP_ACCEPT_LANGUAGE). This is due to how the HTTP headers are transmitted between the Rails framework and the HTTP server that stands upfront. This is directly derived from how the CGI standard Ruby library does for passing an HTTP request from a web server to a standalone program, here your Rails application.
Most of the time, your controllers only want to retain one language, the one presumably preferred by the visitor. Additionally, your application only supports a few languages. This is the context I had to fit into and I found useful to add a second method to the ApplicationController class.
This method, named preferred_language, takes 2 optional parameters

  1. an array that contains the list of language( tag)s supported by your application; it is used to filter out the set of languages accepted by the visitor.
  2. a default language (tag) that is returned to the caller when the browser does not expose any accepted languages or not a single language that is supported by the application; this second parameter represnts the default language of your application.
By default, the application supports any language but has a preference for english.
class ApplicationController < ActionController::Base
  
  # ...
  
  ##
  # Returns the language preferred by the user.
  ##
  def preferred_language(supported_languages=[], default_language="en")
    
    # only keep supported languages
    preferred_languages = accepted_languages.select {|l|
     (supported_languages || []).include?(l[0]) }
    
    if preferred_languages.empty?
      # the browser does accept any supported languages
      # => default to english
      default_language
    else
      # take the highest quality among accepted (and thus supported) languages
      preferred_languages.last[0]
    end
  end
end
If you want to reuse this piece of code (what it's meant for), please remember that it does not take care of country codes. As explained in the HTTP spec, each language tag starts with an ISO-639 language abbreviation and may be followed by an ISO-3166 country code. The latter is simply ignored by this code.
Finally, the parsing and exposure of all the HTTP headers are worth writing a Rails plug-in or a gem. I might do it one day unless someone points me at a similar contribution.


UPDATE: This morning, I found the HTTP Accept-Language plugin that does exactly what I describe hereby, quite similarly. The methods used for accessing the preferred language(s) are bound to the request object, which I admit is better. I'm quite sure this plugin did not exist when I initiated this development (some months ago).

UPDATE (2013/05/10): Naomi Kyoto suggested a much more concise and elegant code snippet for the accepted_languages method. I recommend to use her gist. Thanks for sharing !



About Argyl

Argyl is the company that I co-founded with Manfred Dardenne. This is a small consulting firm specializing in software develoment for web 2.0 projects.

Located in the French Alps (Grenoble - Isère), Argyl supports companies that have web 2.0-flavoured projects by offering its technological know-how and its capacity of transforming a rough idea into a working system at a fast pace.

Argyl’s offering puts the emphasis on

  • Mashups combining Web 2.0 ingredients like Javascript libraries (Google Maps, YUI, Prototype, ...),
  • Rich User Interfaces, HTML-based, CSS-styled, AJAX-driven,
  • Ruby and Ruby on Rails,
  • The principles and practices of Agile Software Development as described by the Manifesto for Agile Software Development and implemented by XP: customer-driven development, (talented) people first, just-enough processes, continuous integration, unit testing, iterative development.

Both company founders have a solid background in Java, MySQL, security (TLS, SSL, authentication, access control) and search engine technologies (Sphinx, Ferret).We're now fluent in Ruby and Rails

We can contribute in all the essential phases of your web software lifecycle, from gathering requirements to deploying the application on the appropriate infrastructure.

Our recent engagements include

  • Developing Conversation™, a social media marketing application proposed by eCairn Inc.. Based on Ruby, RoR, MySQL, Sphinx, Webistrano, ...
  • Recruiting and coaching for a billboard management company on a cartographic application. Uses Google Maps and Yahoo UI.

DoS Vulnerability in REXML: Patch the fix !

Last month, a denial of service (DoS) vulnerability was detected in REXML by the Ruby security team. Even though (RE)XML is not at the core of the Rails framework, most RoR applications shall be affected by this flaw.

The details are described on Riding Rails, the official RoR weblog. This is where you can get a fix and the instructions to install it.

Unfortunately, this fix does not always act properly. If, after applying it, your application starts complaining about some unexpected nil object on line 21 of rexml-expansion-fix.rb, you probably hit the glitch.

Here's what the error looks like for me.

You have a nil object when you didn't expect it!
The error occurred while evaluating nil.record_entity_expansion!
/lib/rexml-expansion-fix.rb:21:in `unnormalized'
/usr/local/lib/ruby/1.8/rexml/doctype.rb:135:...

As first explained by Dan Croak in the comments of the Riding Rails original post, the fix does not properly handle the DOCTYPE entity. Dan suggested a fix to the fix that makes the code work for any use. But it leaves a vulnerability on the expansion of doctype entities.

It happens that I got to the very same fix before I found Dan's comment... Just like me, you can get happy again with Rails and REXML by replacing the line 21 of the fix by

document.record_entity_expansion! unless document.nil?

Hope that helps.

Recent Entries

Recent Comments

Ruby and Rails> Recommended