Revert "Yeah, these files are located in their own fhosted repo"

This reverts commit c3c9bf0336.
This commit is contained in:
Eric Christensen 2014-03-24 15:59:40 -04:00
parent c3c9bf0336
commit 87df3083ed
108 changed files with 7838 additions and 184 deletions

View file

@ -0,0 +1 @@
tmp

View file

@ -0,0 +1,17 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE authorgroup PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<authorgroup>
<author>
<firstname>Ján</firstname>
<surname>Rusnačko</surname>
<affiliation>
<orgname>Red Hat, Inc.</orgname>
<orgdiv>Product Security Team</orgdiv>
</affiliation>
<email>jrusnack@redhat.com</email>
</author>
</authorgroup>

View file

@ -0,0 +1,28 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE bookinfo PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<bookinfo id="book-Secure_Ruby_Development_Guide-Secure_Ruby_Development_Guide">
<title>Secure Ruby Development Guide</title>
<subtitle>Guide to secure software development in Ruby</subtitle>
<productname>Fedora Security Team</productname>
<productnumber></productnumber>
<edition>1</edition>
<pubsnumber>1</pubsnumber>
<abstract>
<para>
This guide covers security aspects of software development in Ruby.
</para>
</abstract>
<corpauthor>
<inlinemediaobject>
<imageobject>
<imagedata fileref="images/ruby.png"/>
</imageobject>
</inlinemediaobject>
</corpauthor>
<xi:include href="Common_Content/Legal_Notice.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Author_Group.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</bookinfo>

View file

@ -0,0 +1,380 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<chapter id="chap-Secure_Ruby_Development_Guide-Environment">
<title>Environment</title>
<para>
Development environment can significantly affect quality and security of code
and investing certain effort into proper setup can result in saved development
time, better code coverage, more readable and secure code etc. In general,
automated checks provide a good baseline and are less prone to unintentional
mistakes than developers.
</para>
<section id="sect-Secure_Ruby_Development_Guide-Environment-Code_Quality_Metrics">
<title>Code quality metrics</title>
<para>
Security is just one aspect of code quality along with reliability, correctness
and others. These metrics overlap a lot, for example denial of service can be
seen as both security and reliability issue. Therefore improvement in any of
these areas is likely to affect others.
</para>
<para>
Increasing code quality by reducing complexity, duplication of code and
mainaining good readability is a good first step towards security. All other things
being equal, more complex code will have more weaknesses than simpler one.
</para>
<para>
Several gems can help with improving code quality:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="https://github.com/railsbp/rails_best_practices">Rails Best Practices</ulink>
is a popular gem among rails developers and new checks are implemented based on voting of community.
</para>
</listitem>
<listitem>
<para>
<ulink url="https://github.com/bbatsov/rubocop">rubocop</ulink>
is a style checker and implements vast amount of checks based on
<ulink url="https://github.com/bbatsov/ruby-style-guide">Ruby Style Guide</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="https://github.com/metricfu/metric_fu">metric_fu</ulink>
combines several popular code metric tools like <ulink url="https://github.com/troessner/reek">Reek</ulink>, <ulink url="https://github.com/seattlerb/flog">Flog</ulink>, <ulink url="https://github.com/seattlerb/flay">Flay</ulink>, <ulink url="https://github.com/square/cane">Cane</ulink> etc.
</para>
</listitem>
</itemizedlist>
<para>
These are just few examples and actual setup may vary from project to project.
However, they help developers keep code complexity low in an automated fashion
and can be easily integrated into workflow.
</para>
</section>
<section id="sect-Secure_Ruby_Development_Guide-Environment-Dependency_management">
<title>Dependency management</title>
<para>
Dependencies in form of gems can be another source of vulnerabilities in Ruby applications.
</para>
<section id="sect-Secure_Ruby_Development_Guide-Environment-Outdated_Dependencies">
<title>Outdated Dependencies</title>
<para>
<ulink url="http://bundler.io/">Bundler</ulink> is the de facto standard for managing Ruby application dependencies. Developer can specify required dependencies and their versions in Gemfile and bundler automatically resolves dependencies and prepares environment for application to run in. Bundler freezes exact versions of dependencies in Gemfile.lock and everytime this file is present, depencency resolution step is skipped and exact versions of gems from Gemfile.lock are installed.
</para>
<para>
Freezing versions of dependencies has a security impact. If a dependency is vulnerable and new version contains the fix, Gemfile.lock has to be updated. Detection of outdated versions of dependencies is something that can be automated and several gems help with this using information provided by <ulink url="https://github.com/rubysec/ruby-advisory-db/">rubysec-db</ulink>.
</para>
<para>
<ulink url="http://www.rubysec.com">Rubysec</ulink> project maintains rubysec-db database of all security advisories related to Ruby libraries. This database covers most of the popular gems and provides data to identify vulnerable and patched versions of dependencies.
</para>
<para>
<ulink url="https://github.com/rubysec/bundler-audit">bundler-audit</ulink> is a gem maintainted by rubysec project that automatically scans Gemfile.lock and reports any unpatched dependencies or insecure sources.
</para>
<para>
<ulink url="https://github.com/appfolio/gemsurance">gemsurance</ulink> also works on top of rubysec-db. Unlike bundler-audit it outputs html report and lists outdated gems as well. Another useful feature is possibility to integrate the check with RSpec and make your tests fail whenever vulnerable dependency is detected.
</para>
<important>
<para>
It is highly recommended to set up automated checks for outdated dependencies.
</para>
</important>
</section>
<section>
<title>Vendoring dependencies</title>
<para>
Another way of freezing dependencies is checking their source code into vendor folder in application. With bundler this practice becomes obsolete. Another, still valid, usecase is when dependency needs to be slightly modified to suit needs of application.
</para>
<para>
By checking the dependency into the application`s repository, developer takes responsibility of tracking bugs and vulnerabilities and updating vendored gems. However, backporting commits that fix security issues from upstream version will render automatic tools for checking dependencies useless, as they will rely on gem versions, which will not correspond with the vendored code.
</para>
</section>
<section>
<title>Gem signing</title>
<para>
Gem signing is already implemented in rubygems and is based on x509 certificates, even though discussion about future implementation is <ulink url="https://github.com/rubygems-trust/rubygems.org/wiki">ongoing</ulink>. There is no PKI, so user who wants to verify gem`s integrity must explicitly download and trust certificate that was used to sign the gem. Establishing trust in certificate of party user has no prior relationship with over internet can be diffucult and unscalable.
<important>
<para>
Assuming user verified the certificate belongs to the developer it says, signature protects integrity of gem as it is distributed and gives user a mechanism to detect modifications of gem after it was signed.
</para>
<para>
However, signatures do not guarantee trustworthiness of gem author.
</para>
</important>
</para>
<para>
Developer can generate his private key and self signed certificate with:
<programlisting>
$ gem cert --build &lt;email address&gt;
...
$ chmod 600 gem-private_key.pem gem-public_cert.pem
</programlisting>
</para>
<para>
This command will generate self-signed 2048 bit RSA with SHA1 certificate (this configuration is currently hardcoded) stored in PEM format.
</para>
<important>
<para>
Generated private key will not be passphrase protected, and it has to be encrypted manually:
<programlisting language="Bash">
$ openssl rsa -des3 -in &lt;private key&gt; -out &lt;encrypted private key&gt;
</programlisting>
</para>
</important>
<para>
To sign the gem, following needs to be added to gemspec:
<programlisting language="Ruby">
s.cert_chain = &lt;path to public certificate&gt;
s.signing_key = &lt;path to private key&gt; if $0 =~ /gem\z/
</programlisting>
</para>
<para>
After building the gem, one can verify it has been signed with:
<programlisting>
$ gem spec testgem-1.0.0.gem cert_chain
...
$ tar tf testgem-1.0.0.gem
data.tar.gz
metadata.gz
data.tar.gz.sig
metadata.gz.sig
</programlisting>
</para>
<section>
<title>Installation and policies</title>
<para>
To make use of signatures in gems, user has to specify security policy during gem installation (it is turned off by default):
<programlisting>
$ gem install -P HighSecurity testgem
</programlisting>
</para>
<para>
There are 4 available security policies:
<variablelist>
<varlistentry>
<term>No policy</term>
<listitem>
<para>Signed packages are treated as unsigned.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>LowSecurity</term>
<listitem>
<para>Still pretty much no security. Rubygems will make sure signature matches certificate and certificate hasn`t expired.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>MediumSecurity</term>
<listitem>
<para>For signed gems, signature is verified against certificate, certificate validity is checked and certificate chain is checked too. Packages from untrusted sources won`t be installed (user has to explicitly trust the cerficate, see below). Unsigned gems are installed normally.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>HighSecurity</term>
<listitem>
<para>Same as medium, but unsigned gems are not installed.</para>
</listitem>
</varlistentry>
</variablelist>
<warning>
<para>Since signatures protect integrity of gem as it`s being distributed from developer to users, the only policy with security impact is <constant>HighSecurity</constant>. With <constant>MediumSecurity</constant>, attacker can always intercept gem, strip signatures, modify it and serve users that accept unsigned gems.</para>
</warning>
</para>
<para>
To install signed gem under medium or high security policy, user has to download certificate from external source, verify it`s authenticity and explicitly add it to his local database of trusted certificates:
<programlisting>
$ gem cert --add &lt;certificate&gt;
</programlisting>
</para>
<para>
This command will store public certificate to <filename>~/.gem/trust</filename> directory. Name of the certificate will contain hexdigest of the subject of certificate, so if users adds another certificate with the same subject as one of the already trusted ones, original one will be overwritten without notice.
</para>
<para>
To avoid overwriting existing certificate, make sure subject of certificate being added is different from certificates that are already trusted:
<programlisting>
$ openssl x509 -text -in &lt;certificate&gt; | grep Subject:
Subject: CN=test, DC=example, DC=com
$ gem cert --list
...
</programlisting>
</para>
<para>
Bundler supports gem signing and trust policies since version 1.3 and user can specify security policy during installation:
<programlisting>
$ bundle install --trust-policy=HighSecurity
</programlisting>
<warning>
<para>Gems that are installed by bundler from repository like
<programlisting>
gem 'jquery-datatables-rails', git: 'git://github.com/rweng/jquery-datatables-rails.git'
</programlisting>
bypass security policy, as they are not installed using <command>gem</command> command, but cloned into bundler folder.</para>
</warning>
</para>
<para>
A small gem <command>bundler_signature_check</command> can be used to check <filename>Gemfile</filename> and determine which gems are signed, with suggestion which security policy can be currently safely used (note that <command>bundler_signature_check</command> is signed and it`s dependencies <command>bundler</command> and <command>rake</command> are likely already installed, so <constant>HighSecurity</constant> can be used):
<programlisting>
$ gem install -P HighSecurity bundler_signature_check
$ bundler_signature_check
...
</programlisting>
</para>
</section>
<section>
<title>References:</title>
<para>
<itemizedlist>
<listitem>
<para>
Rubygems Security page <ulink url="http://guides.rubygems.org/security/">http://guides.rubygems.org/security/</ulink></para>
</listitem>
<listitem>
<para>
Documentation of <classname>Gem::Security</classname> module <ulink url="http://rubygems.rubyforge.org/rubygems-update/Gem/Security.html">http://rubygems.rubyforge.org/rubygems-update/Gem/Security.html</ulink></para>
</listitem>
<listitem>
<para>
Ben Smith`s Hacking with gems presentation <ulink url="http://www.youtube.com/watch?v=z-5bO0Q1J9s">http://www.youtube.com/watch?v=z-5bO0Q1J9s</ulink></para>
</listitem>
</itemizedlist>
</para>
</section>
</section>
</section>
<section>
<title>Static code analysis with Brakeman</title>
<para>
<ulink url="http://brakemanscanner.org">Brakeman</ulink> is a static code scanner for Ruby on Rails applications. It does not require any configuration and can be run out-of-the-box on source of rails application. It performs static code analysis, so it does not require rails application to be set up, but rather parses the source code and looks for common vulnerable patterns.
</para>
<para>
Brakeman gem is signed, but some of its dependencies are not, so to install run:
<programlisting>
$ gem install -P MediumSecurity brakeman
</programlisting>
</para>
<para>
To execute scan on application, run brakeman from rails application repository:
<programlisting>
$ brakeman -o report.html --path &lt;path to rails app&gt;
</programlisting>
</para>
<para>
The format of the output is determined by file extension or by <command>-f</command> flag. Currently supported formats are <constant>html</constant>,<constant>json</constant>,<constant>tabs</constant>, <constant>csv</constant> and <constant>text</constant>.
</para>
<para>
Brakeman output contains warnings in format
<programlisting>
+------------+-----------+---------+--------------------+------------------------------------------+
| Confidence | Class | Method | Warning Type | Message |
+------------+-----------+---------+--------------------+------------------------------------------+
| High | Foo | bar | Denial of Service | Symbol conversion from unsafe String ... |
</programlisting>
</para>
<para>
As static code scanner Brakeman does not analyze the behaviour of code when run
and lacks execution context (e.g. it does not know about dead code that`s never
executed). Therefore Brakeman output usually contains also false warnings.
There are 3 confidence levels to help developers determine possible false
warnings and prioritize when reviewing the output: <constant>High</constant>,
<constant>Medium</constant> and <constant>Weak</constant>.
</para>
<section>
<title>Continuous integration</title>
<para>
Good way to use Brakeman is to integrate it into workflow of a project
and fix the reported problems before they are committed into repository.
</para>
<para>
Creating a rake task is easy with
<programlisting>
$ brakeman --rake
</programlisting>
which creates file <filename>lib/tasks/brakeman.rake</filename>
</para>
<para>
Another useful options is to create a configuration file from a command
line options:
<programlisting>
$ brakeman -C &lt;config file&gt; &lt;options&gt;
</programlisting>
which can be later used:
<programlisting>
$ brakeman -c &lt;config file&gt;
</programlisting>
</para>
<para>
Very useful feature is comparison with older scan result and outputting
only difference between reports - developers can then easily identify
warnings that were just added or fixed:
<programlisting>
$ brakeman --compare &lt;old result in json&gt; -o &lt;output in json&gt;
</programlisting>
The output is always in json (<command>-f</command> is ignored).
</para>
</section>
<section>
<title>Reducing number of false warnings</title>
<para>
There are several ways to reduce number of false warnings, most of which
can be dangerous. Reducing number of false warnings might be meaningful
when Brakeman is adopted by an existing project - in such cases initial
report can be overwhelming and ignoring warnings that are likely to be false
can be crucial. However, this shall be considered only temporary solution.
<important>
<para>
Reduction of false warnings by skipping certain checks or ignoring
certain files is dangerous. Even if all currently reported warnings
are false, future commits might introduce flaws that would otherwise
be reported. This greatly reduces effectiveness of Brakeman and its
value for project.
</para>
</important>
</para>
<para>
One way to reduce number of warnings is to set minimum confidence level:
<programlisting>
$ brakeman -w &lt;level&gt;
</programlisting>
where level <constant>1</constant> indicates Weak confidence, level
<constant>2</constant> Medium and <constant>3</constant> High confidence.
</para>
<para>
Another option is to specify list of safe methods:
<programlisting>
$ brakeman -s &lt;comma separated list of methods&gt;
</programlisting>
</para>
<para>
This will add methods to the set of known safe methods and certain checks
will skip them without producing a warning. For example, Cross site scripting
checker maintains a set of methods which produce safe output (it contains
methods like <command>escapeHTML</command>) and safe methods specified
as command line argument are added to the list.
</para>
<para>
You can skip processing <filename>lib</filename> directory and/or specify
files to be skipped:
<programlisting>
$ brakeman --skip-libs
$ brakeman --skip-files &lt;comma separated list of files&gt;
</programlisting>
</para>
</section>
</section>
</chapter>

View file

@ -0,0 +1,580 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<chapter id="chap-Secure_Ruby_Development_Guide-Language_features">
<title>Language features</title>
<para>
</para>
<section>
<title>Tainting and restricted code execution</title>
<para>
Ruby language includes a security mechanism to handle untrusted objects
and restrict arbitrary code execution. This mechanism consists of two parts: first is an automated way of marking objects in Ruby as coming from untrusted source, called tainting. The second part is mechanism for restricting code execution and prevents certain potentially dangerous functions being executed on tainted data. Ruby interpreter can run in several safe levels, each of which defines different restrictions.
</para>
<para>
This mechanism (especially restricted code execution) is implementation
specific and is not part of Ruby specification. Other Ruby implementations such as Rubinius and JRuby do not implement safe levels. However, taint flag is part of the rubyspec.
</para>
<section>
<title>Object.tainted?</title>
<para>
Each object in Ruby carries a taint flag which marks it as originating from unsafe source. Additionally, any object derived from tainted object is also tainted. Objects that come from external environment are automatically marked as tainted, which includes command line arguments (<constant>ARGV</constant>), environment variables (<constant>ENV</constant>), data read from files, sockets or other streams. Environment variable <constant>PATH</constant> is exception: it is tainted only if it contains a world-writable directory.
</para>
<para>
To check whether object is tainted and change taintedness of object, use methods <command>Object.tainted?</command>, <command>Object.taint</command> and <command>Object.untaint</command>:
<programlisting>
>> input = gets
exploitable
=> "exploitable\n"
>> input.tainted?
=> true
>> input.untaint
=> "exploitable\n"
>> input.tainted?
=> false
</programlisting>
<note>
<para>
Literals (such as numbers or symbols) are exception: they do not carry taint flag and are always untainted.
</para>
</note>
</para>
</section>
<section>
<title>Object.untrusted?</title>
<para>
At higher safe levels (see safe level 4 below) any code is automatically untrusted and interpreter prevents execution of untrusted code on trusted objects. In Ruby 1.8, taint flag is also used to mark objects as untrusted, so untrusted code is not allowed to modify untainted objects. In addition, any object created by untrusted code is tainted. This effectively allows to sandbox an untrusted code, which will not be allowed to modify "trusted" objects.
</para>
<para>
Mixing taint and trust of object has serious drawback - untrusted code is allowed to modify all tainted objects (even if they come from trusted code).
</para>
<para>
Ruby 1.9 adds another flag to each object to mark it as untrusted. Untrusted code is now allowed only to modify untrusted objects (ignoring taint flag), and objects created by untrusted code are automatically marked as untrusted and tainted. To check and modify trust flag use methods <command>Object.untrusted?</command>, <command>Object.untrust</command> and <command>Object.trust</command>.
</para>
<para>
However, Ruby 2.1 deprecates trust flag and the behaviour of above methods is the same as <command>Object.tainted?</command>, <command>Object.taint</command> and <command>Object.untaint</command>. This change comes together with removal of safe level 4, which makes trust flag useless (see <ulink url="https://bugs.ruby-lang.org/issues/8468">issue on ruby-lang</ulink> or read below).
</para>
</section>
<section>
<title>$SAFE</title>
<para>
Ruby interpreter can run in restricted execution mode with several levels of checking, controlled by global variable <constant>$SAFE</constant>. There are 5 possible levels: 0,1,2,3,4 with 0 being default safe level. <constant>$SAFE</constant> is thread-local and its value can only be increased (at least in theory - in practice there are well known ways how to work around restricted code execution or decrease a safe level. See <xref linkend='SAFE-security'/>). Safe level can be changed by assigning to <constant>$SAFE</constant> or with <command>-T&lt;level&gt;</command> argument.
</para>
<para>Safe levels have following restrictions:
<variablelist>
<varlistentry>
<term>
level 0
</term>
<listitem>
<para>strings from streams/environment/ARGV are tainted (default)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
level 1
</term>
<listitem>
<para>dangerous operations on tainted values are forbidden (such as <command>eval</command>, <command>require</command> etc.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
level 2
</term>
<listitem>
<para>adds to the level 1 also restrictions on directory, file and process operations</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
level 3
</term>
<listitem>
<para>in addition all created objects are tainted and untrusted</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
level 4
</term>
<listitem>
<para>code running in this level cannot change trusted objects, direct output is also restricted. This safe level <ulink url="https://bugs.ruby-lang.org/issues/8468">is deprecated</ulink> since Ruby 2.1</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
There is a lack of documentation of what is restricted in each safe level. For more exhausting description refer to <ulink url="http://ruby-doc.com/docs/ProgrammingRuby/">Programming Ruby: Pragmatic programmer`s guide</ulink>.
</para>
<section id='SAFE-security'>
<title id='SAFE-security.title'>Security considerations of $SAFE</title>
<para>
Design of restricted code execution based on <constant>$SAFE</constant> is inherently flawed. Blacklist approach is used to restrict operation on each level, which means any missed function creates a vulnerability. In past several security updates were related to restricted code execution and taint flag (see <ulink url="https://www.ruby-lang.org/en/news/2005/10/03/ruby-vulnerability-in-the-safe-level-settings/">CVE-2005-2337</ulink>, CVE-2006-3694, <ulink url="https://www.ruby-lang.org/en/news/2008/08/08/multiple-vulnerabilities-in-ruby/">CVE-2008-3655</ulink>, <ulink url="https://www.ruby-lang.org/en/news/2008/08/08/multiple-vulnerabilities-in-ruby/">CVE-2008-3657</ulink>, <ulink url="https://www.ruby-lang.org/en/news/2011/02/18/exception-methods-can-bypass-safe/">CVE-2011-1005</ulink>, <ulink url="https://www.ruby-lang.org/en/news/2012/10/12/cve-2012-4464-cve-2012-4466/">CVE-2012-4464</ulink>,<ulink url="https://www.ruby-lang.org/en/news/2012/10/12/cve-2012-4464-cve-2012-4466/">CVE-2012-4466</ulink> and <ulink url="https://www.ruby-lang.org/en/news/2013/05/14/taint-bypass-dl-fiddle-cve-2013-2065/">CVE-2013-2065</ulink>).
</para>
<warning>
<para>
Design of restricted code execution based on <constant>$SAFE</constant> is inherently flawed and cannot be used to run untrusted code even at the highest safe level. It must not be used as mechanism to create a secure sandbox, as attacker will be able to work around the restrictions or decrease safe level.
</para>
</warning>
<para>
One example of how exploitable the design is comes from <ulink url="https://www.ruby-lang.org/en/news/2013/05/14/taint-bypass-dl-fiddle-cve-2013-2065/">CVE-2013-2065</ulink>:
<programlisting language="Ruby">
require 'fiddle'
$SAFE = 1
input = "uname -rs".taint
handle = DL.dlopen(nil)
sys = Fiddle::Function.new(handle['system'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
sys.call DL::CPtr[input].to_i
</programlisting>
Even though safe level 1 should restrict execution of system commands, this can be bypassed using Fiddle library, which is an extension to translate a foreign function interface with Ruby. Exploit above bypasses safe level by passing input to system call as numeric memory offset. Since numbers as literals cannot be tainted, code cannot check taintedness of input.
</para>
<note>
<para>
However, running application with higher safe level is still useful for catching unintended programming errors, such as executing <command>eval</command> on tainted string.
</para>
</note>
</section>
</section>
</section>
<section>
<title>Dangerous methods</title>
<para>
Ruby contains number of methods and modules that should be used with caution, since calling them with input potentially controlled by attacker might be abused into arbitrary code execution. These include:
<itemizedlist>
<listitem>
<para><command>Kernel#exec</command>, <command>Kernel#system</command>, backticks and <command>%x{...}</command></para>
</listitem>
<listitem>
<para><command>Kernel#fork</command>, <command>Kernel#spawn</command></para>
</listitem>
<listitem>
<para><command>Kernel#load</command>, <command>Kernel#autoload</command></para>
</listitem>
<listitem>
<para><command>Kernel#require</command>, <command> Kernel#require_relative</command></para>
</listitem>
<listitem>
<para><command>DL</command> and <command>Fiddle</command> module</para>
</listitem>
<listitem>
<para><command>Object#send</command>, <command>Object#__send__</command> and <command>Object#public_send</command></para>
</listitem>
<listitem>
<para><command>BasicObject#instance_eval</command>, <command>BasicObject#instance_exec</command></para>
</listitem>
<listitem>
<para><command>Module#class_eval</command>, <command>Module#class_exec</command>, <command>Module#module_eval</command>, <command>Module#module_exec</command></para>
</listitem>
<listitem>
<para><command>Module#alias_method</command></para>
</listitem>
</itemizedlist>
</para>
</section>
<section id="RubySymbols">
<title>Symbols</title>
<para>Symbols in MRI Ruby are used for method, variable and constant lookup. They are implemented as integers so that they are faster to look up in hastables. Once symbol is created, memory allocated for it is never freed. This creates opportunity for attacker: if he is able to create arbitrary symbols, he could flood the application with unique symbols that will never be garbage collected. Memory consumption of Ruby process would only grow until it runs out of memory, resulting in Denial of Service attack.</para>
<para>
Application developers should be careful when calling <command>to_sym</command> or <command>intern</command> on user-supplied strings. Additionally, other methods may convert supplied arguments to symbols internally, for example <command>Object.send</command>, <command>Object.instance_variable_set</command>, <command>Object.instance_variable_get</command>, <command>Module.const_get</command> or <command>Module.const_set</command>:
<programlisting language="Ruby">
>> Symbol.all_symbols.size
=> 2956
>> Module.const_get('MY_SYMBOL')
NameError: uninitialized constant Module::MY_SYMBOL
>> Symbol.all_symbols.size
=> 2957
</programlisting>
</para>
<para>
Array of all currently defined symbols is available through <command>Symbol.all_symbols</command> class method.
</para>
</section>
<section>
<title>Serialization in Ruby</title>
<para>
Deserialization of untrusted data has been on the top of critical vulnerabilities in 2013 (prominent examples are deserialization issues found in Ruby on Rails, see <ulink url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0156">CVE-2013-0156</ulink>, <ulink url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0277">CVE-2013-0277</ulink> or <ulink url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0333">CVE-2013-0333</ulink>). There are several ways how to serialize objects in Ruby:
</para>
<section>
<title>Marshal.load</title>
<para>
<command>Marshal.dump</command> and <command>Marshal.load</command> can serialize and deserialize most of the classes in Ruby. If application deserializes data from untrusted source, attacker can abuse this to execute arbitrary code. Therefore, this method is not suitable most of the time and should never be be used on data from unstrusted source.
</para>
</section>
<section>
<title>YAML.load</title>
<para>
YAML is a popular serialization format among Ruby developers. Just like <command>Marshal.load</command> it can be used to deserialize most of the Ruby classes and also should never be used on untrusted data.
</para>
<section>
<title>SafeYAML</title>
<para>
Alternative approach is taken by <ulink url="http://danieltao.com/safe_yaml/">SafeYAML</ulink> gem - by default it allows deserialization of only few types of objects that can be considered safe, such as <constant>Hash</constant>, <constant>Array</constant>, <constant>String</constant> etc. When application requires serialization of certain types, developer can explicitly whitelist trusted types of objects:
<programlisting>
SafeYAML.whitelist!(FrobDispenser, GobbleFactory)
</programlisting>
This approach is more versatile, since it disables serialization of unsafe classes, yet allows developer to serialize know benign object. Requiring <constant>safe_yaml</constant> will patch method <command>YAML.load</command>.
</para>
</section>
</section>
<section>
<title>JSON.parse and JSON.load</title>
<para>
JSON format supports only several primitive data types such as strings, arrays, hashes, numbers etc. This certainly limits the attack surface, but it should not give developer false sense of security - one example is <ulink url="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0333">CVE-2013-0333</ulink> vulnerability in Ruby on Rails, when parser used for deserialization of JSON data actually converted data to a subset of <constant>YAML</constant> and used <command>YAML.load</command> to deserialize.
</para>
<para>
However, it is possible to extend Ruby classes to be JSON-dumpable:
<programlisting language="Ruby">
class Range
def to_json(*a)
{
'json_class' => self.class.name,
'data' => [ first, last, exclude_end? ]
}.to_json(*a)
end
def self.json_create(o)
new(*o['data'])
end
end
</programlisting>
This will allow instances of Range class to be serialized with JSON:
<programlisting language="Ruby">
>> (1..10).to_json
=> "{\"json_class\":\"Range\",\"data\":[1,10,false]}"
</programlisting>
During deserialization, JSON gem will try to look up class referenced by "json_class", which might create new Symbol if the class does not exist, possibly allowing Denial of Service (see <xref linkend='RubySymbols'/>):
<programlisting>
>> Symbol.all_symbols.size
=> 3179
>> JSON.parse('{"json_class":"NonexistentClass"}')
ArgumentError: can't get const NonexistentClass: uninitialized constant NonexistentClass
>> Symbol.all_symbols.size
=> 3180
</programlisting>
To disable this, <constant>:create_additions => false</constant> option can be passed as second argument:
<programlisting>
>> JSON.parse('{"json_class":"NonexistentClass"}',:create_additions => false)
=> {"json_class"=>"NonexistentClass"}
</programlisting>
This behaviour has changed in response to <ulink url="https://www.ruby-lang.org/en/news/2013/02/22/json-dos-cve-2013-0269/">CVE-2013-0269</ulink> and <command>JSON.parse</command> now defaults to <constant>:create_additions => false</constant>. However, default behaviour has not changed for <command>JSON.load</command>, which is dangerous to call on untrusted input.
</para>
</section>
<section>
<title>Exploiting deserialization vulnerabilities</title>
<para>
To exploit deserialization vulnerability, there must already be a dangerous class loaded in the current namespace. In particular, it contains unsafe <command>init_with()</command> or <command>[]=</command> methods, that get called during deserialization. This might seem like an unlikely event, however, its very likely in case of big projects like Ruby on Rails.
</para>
<para>
<ulink url="https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-security/61bkgvnSGTQ">CVE-2013-0156</ulink> vulnerability in Ruby on Rails can be used as an example. A vulnerable class in this case was <constant>ActionDispatch::Routing::RouteSet::NamedRouteCollection</constant>, which contained code like this:
<programlisting language="Ruby">
class NamedRouteCollection
alias []= add
def add(name, route)
routes[name.to_sym] = route
define_named_route_methods(name, route)
end
def define_named_route_methods(name, route)
define_url_helper route, :"#{name}_path",
route.defaults.merge(:use_route => name, :only_path => true)
define_url_helper route, :"#{name}_url",
route.defaults.merge(:use_route => name, :only_path => false)
end
def define_url_helper(route, name, options)@module.module_eval &lt;&lt;-END_EVAL
def #{name}(*args)
# ... code
end
END_EVAL
end
...
</programlisting>
Even though <command>module_eval</command> is hidden under several layers of method calls, calling <command>[]=</command> effectively passes first argument to the <command>define_url_helper</command>, where it gets evaluated.
</para>
<para>
To exploit vulnerable class, it is enough to deserialize YAML payload below:
<programlisting>
--- !ruby/hash:NamedRouteCollection
foo; end; system 'rm /etc/passwd'; def bar: baz
</programlisting>
Before deserialization, Ruby's YAML parser Psych first looks at the declared type, which says this object is an instance of <constant>NamedRouteCollection</constant> and subclass of Ruby's <constant>Kernel::Hash</constant> class.
</para>
<para>
Deserialization of hashes from YAML to Ruby makes use of <command>[]=</command> method. Given YAML like
<programlisting>
--- !ruby/hash:MyHash
key1: value1
key2: value2
</programlisting>
deserialization process is equivalent to calling
<programlisting language="Ruby">
newobj = MyHash.new
newobj['key1'] = 'value1'
newobj['key2'] = 'value2'
newobj
</programlisting>
</para>
<para>
In the case of YAML payload, key and value pair is
<programlisting language="Ruby">
['foo; end; system 'rm /etc/passwd'; def bar','baz']
</programlisting>
so deserialization process will call <command>[]=</command> method on <constant>NamedRouteCollection</constant> with key <code>'foo; end; system 'rm /etc/passwd'; def bar'</code>.
</para>
<para>
This gets passed to <command>define_url_helper</command> as an argument and following code gets evaluated:
<programlisting language="Ruby">
def foo; end; system 'rm /etc/passwd'; def bar(*args)
# ... code
end
</programlisting>
Reordering the code above to be more readable, this is equivalent to
<programlisting language="Ruby">
def foo
end
system 'rm /etc/passwd'
def bar(*args)
# ... code
end
</programlisting>
</para>
<section>
<title>References</title>
<itemizedlist>
<listitem>
<para>
Aaron Patterson's blog <ulink url="http://tenderlovemaking.com/2013/02/06/yaml-f7u12.html">http://tenderlovemaking.com/2013/02/06/yaml-f7u12.html</ulink>
</para>
</listitem>
<listitem>
<para>
Charlie Sommerville's blog <ulink url="https://charlie.bz/blog/rails-3.2.10-remote-code-execution">https://charlie.bz/blog/rails-3.2.10-remote-code-execution</ulink>
</para>
</listitem>
<listitem>
<para>
Metasploit's blog <ulink url="https://community.rapid7.com/community/metasploit/blog/2013/01/09/serialization-mischief-in-ruby-land-cve-2013-0156">https://community.rapid7.com/community/metasploit/blog/2013/01/09/serialization-mischief-in-ruby-land-cve-2013-0156</ulink>
</para>
</listitem>
<listitem>
<para>
Extending Hash <ulink url="http://www.yaml.org/YAML_for_ruby.html#extending_kernel::hash">http://www.yaml.org/YAML_for_ruby.html#extending_kernel::hash</ulink>
</para>
</listitem>
</itemizedlist>
</section>
</section>
</section>
<section>
<title>Regular expressions</title>
<para>
A common gotcha in Ruby regular expressions relates to anchors marking the begninning and the end of a string. Specifically, <constant>^</constant> and <constant>$</constant> refer to the beginning and the end of a line, rather then a string. If regular expression like <command>/^[a-z]+$</command> is used to whitelist user input, attacker can bypass it by including newline. To match the beginning and the end of a string use anchors <command>\A</command> and <command>\z</command>.
<programlisting language="Ruby">
>> puts 'Exploited!' if /^benign$/ =~ "benign\n with exploit"
Exploited!
=> nil
>> puts 'Exploited!' if /\Abenign\z/ =~ "benign\n with exploit"
=> nil
</programlisting>
</para>
</section>
<section>
<title>Object.send</title>
<para>
<command>Object.send</command> is a method with serious security impact, since it invokes any method on object, including private methods. Some methods in Ruby like <command>eval</command> or <command>exit!</command> are private methods of <constant>Object</constant> and can be invoked using <command>send</command>:
<programlisting language="Ruby">
>> Object.private_methods.include?(:eval)
=> true
>> Object.private_methods.include?(:exit)
=> true
>> Object.send('eval', "system 'uname'")
Linux
=> true
</programlisting>
</para>
<para>
Alternative is <command>Object.public_send</command>, which by definition only invokes public methods on object. However, this does not prevent attacker from executing only private methods, since <command>Object.send</command> itself is (and has to be) public:
<programlisting language="Ruby">
>> Object.public_send("send","eval","system 'uname'")
Linux
=> true
>> Object.public_send("send","exit!") # exits
</programlisting>
</para>
<para>
Developers should be careful when invoking <command>send</command> and <command>public_send</command> with user controlled arguments.
</para>
</section>
<section>
<title>SSL in Ruby</title>
<para>
Ruby uses OpenSSL implementation of common cryptographic primitives, which are accessible through <constant>OpenSSL</constant> module included in standard library. This module is then used by other parts of standard library to manage SSL, including <constant>Net::HTTP</constant>, <constant>Net::POP</constant>, <constant>Net::IMAP</constant>, <constant>Net::SMTP</constant> and others.
</para>
<para>
There are four valid verification modes <constant>VERIFY_NONE</constant>, <constant>VERIFY_PEER</constant>, <constant>VERIFY_FAIL_IF_NO_PEER_CERT</constant> and <constant>VERIFY_CLIENT_ONCE</constant>. These correspond to underlying <ulink url="https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html#NOTES">OpenSSL modes</ulink>.
</para>
<para>
SSL connection can be created using OpenSSL module directly:
<programlisting language="Ruby">
>> require 'openssl'
=> true
>> require 'socket'
=> true
>> tcp_client = TCPSocket.new 'redhat.com', 443
=> #&lt;TCPSocket:fd 5&gt;
>> ssl_context = OpenSSL::SSL::SSLContext.new
=> #&lt;OpenSSL::SSL::SSLContext:0x00000000fcf918&gt;
>> ssl_context.set_params
=> {:ssl_version=>"SSLv23", :verify_mode=>1, :ciphers=>"ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW", :options=>-2147480585}
>> ssl_client = OpenSSL::SSL::SSLSocket.new tcp_client, ssl_context
=> #&lt;OpenSSL::SSL::SSLSocket:0x0000000106a418&gt;
>> ssl_client.connect
=> #&lt;OpenSSL::SSL::SSLSocket:0x0000000106a418&gt;
</programlisting>
Note the call to <command>ssl_context.set_params</command>: by default, when context is created, all its instance variables are nil. Before using the context, <command>set_params</command> should be called to initialize them (when called without argument, default parameters are chosen). In case this call is omitted and variables are left uninitialized, certificate verification is not performed (effectively the same as <constant>VERIFY_NONE</constant> mode). Default parameters are stored in the constant:
<programlisting language="Ruby">
>> OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
=> {:ssl_version=>"SSLv23", :verify_mode=>1, :ciphers=>"ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW", :options=>-2147480585}
</programlisting>
One of the side effects of <command>set_params</command> is that it also sets up certificate store with certificates from default certificate area (see <xref linkend='CertificateStore'/> below):
<programlisting language="Ruby">
>> ssl_context.cert_store
=> nil
>> ssl_context.set_params
=> {:ssl_version=>"SSLv23", :verify_mode=>1, :ciphers=>"ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW", :options=>-2147480585}
>> ssl_context.cert_store
=> #&lt;OpenSSL::X509::Store:0x00000000fea740&gt;
</programlisting>
</para>
<section id="CertificateStore">
<title>Certificate store</title>
<para>
Class <command>OpenSSL::X509::Store</command> implements certificate store in Ruby. Certificate store is similar to store in web browsers - it contains trusted certificates that can be used to verify certificate chain. When new certificate store is created, it contains no trusted certificates by default.
</para>
<para>
To populate certificate store with certificates, use one of methods:
<itemizedlist>
<listitem>
<para>
<command>Store#add_file</command> takes a path to DER/PEM encoded certificate
</para>
</listitem>
<listitem>
<para>
<command>Store#add_cert</command> takes instance of <constant>X509::Certificate</constant>
</para>
</listitem>
<listitem>
<para>
<command>Store#add_path</command> takes a path to a directory with trusted certificates
</para>
</listitem>
<listitem>
<para>
<command>Store#set_default_path</command> adds certificates stored in default certificate area
</para>
</listitem>
</itemizedlist>
</para>
<para>
OpenSSL installation usually creates a directory, which stores several trusted certificates (approach similar to web browsers, that also come with predefined certificate store). To populate certificate store with certificates that come with OpenSSL use <command>Store#set_default_path</command>. The path to default certificate area is defined as:
<programlisting language="Ruby">
>> OpenSSL::X509::DEFAULT_CERT_AREA
=> "/etc/pki/tls"
</programlisting>
</para>
</section>
<section>
<title>Ruby libraries using OpenSSL</title>
<para>
There are several libraries that build on top of OpenSSL. Depending on how a library uses <constant>SSLContext</constant>, users may encounter exception from OpenSSL code saying the certificate verification failed:
<programlisting language="Ruby">
>> ssl_client.connect
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
from (irb):7:in `connect'
from (irb):7
</programlisting>
This usually happens when <constant>verify_mode</constant> is set to check the certificate, but the certificate store used does not contain trusted certificate required to verify the SSL sent by the server.
</para>
<note>
<para>
The worst advice that can be found on internet on how to fix SSL is to set
<programlisting>
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
</programlisting>
This redefines constant <constant>OpenSSL::SSL::VERIFY_PEER</constant> to have the same effect as <constant>OpenSSL::SSL::VERIFY_PEER</constant>, effectively globally disabling certificate checking.
</para>
</note>
<para>
Take <constant>Net::IMAP</constant> as example (the code below refers to Ruby 1.9.3): initialize method for creating a new IMAP connection has takes the following arguments:
<programlisting language="Ruby">
def initialize(host, port_or_options = {},
usessl = false, certs = nil, verify = true)
...
</programlisting>
When SSL connection is used but <constant>certs</constant> and <constant>verify</constant> arguments are left to be assigned defaults values, SSLError may be thrown when certificate sent by server cannot be verified.
</para>
<important>
<para>
The correct solution is to always make sure certificate store used by <constant>SSLContext</constant> contains a trusted certificate that can be used to verify the certificate sent by the server.
</para>
</important>
<section>
<title>Behaviour in different Ruby versions</title>
<para>
Default behaviour differs across Ruby versions: in Ruby 1.8, SSL enabled libraries usually falled back to <constant>VERIFY_NONE</constant> mode. The above mentioned <constant>Net::IMAP#initialize</constant> looks like this:
<programlisting language="Ruby">
def initialize(host, port = PORT, usessl = false, certs = nil, verify = false)
...
</programlisting>
Starting from Ruby 1.9, standard library defaults to <constant>VERIFY_PEER</constant> mode.
</para>
</section>
</section>
</section>
</chapter>

View file

@ -0,0 +1,507 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<chapter>
<title>Web Application Security</title>
<para>
Web application development is one of the most popular usages of Ruby language thanks to the popularity of Ruby on Rails. Following chapter is dedicated to security of web applications with most of the content being framework-independent, while examples and implmentation specific problems are targeted to Ruby on Rails.
</para>
<para>
Ruby on Rails as a popular web framework already helps with a web application security by providing secure defaults, useful helper methods, automatic html escaping etc.
</para>
<section>
<title>Authentication and session management</title>
<para>
</para>
</section>
<section>
<title>Authorization and user management</title>
<para>
</para>
</section>
<section>
<title>Common attacks and mitigations</title>
<section>
<title>Cross site scripting (XSS)</title>
<para></para>
</section>
<section>
<title>Cross site request forgery (CSRF)</title>
<para></para>
</section>
<section>
<title>SQL injection attack</title>
<para></para>
</section>
<section>
<title>LDAP injection attack</title>
<para></para>
</section>
<section>
<title>Cross site tracing (XST)</title>
<para></para>
</section>
<section>
<title>Guidelines and principles</title>
<para></para>
</section>
</section>
<section>
<title>Client-side security</title>
<section>
<title>Same origin policy</title>
<para>
One of the most important concepts of web applications is same origin policy. It is a protection mechanism implemented by modern web browsers that isolates web applications from each other on the client side. This isolation is performed on domain names under the assumption that content from different domains comes from different entities. In theory, this means every domain has its own trust domain and interaction across domains is restricted. In practice, there are multiple ways of bypassing this mechanism, malicious ones often creating confused deputy problem where client`s browser is tricked into submitting attacker-specified request under his authority.
</para>
<para>
Same origin policy prevents Javascript and other scripting languages to access DOM across domains. In addition it also applies to XMLHttpRequest Javascript API provided by browsers and prohibits page of sending XMLHttpRequest requests against different domains. On the downside, actual implementation by different browsers may vary in important details. Since the actual behaviour depends on implementation in each browser, each vendor usually implements some exceptions intended to help web developers, which reduce the reliability of this mechanism.
</para>
<para>
<variablelist>
<varlistentry>
<term>Same origin policy</term>
<listitem>
<para>Two pages share the same origin if the protocol, hostname and port are the same for both.</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Following is a table with outcome of same origin policy check against URL http://web.company.com/~user1
</para>
<para>
<table frame='all'>
<title>Sample CALS Table</title>
<tgroup cols='3' align='left' colsep='1' rowsep='1'>
<thead>
<row><entry>URL</entry><entry>Outcome</entry><entry>Reason</entry></row>
</thead>
<tbody>
<row><entry>http://web.company.com/~user2</entry><entry>Success</entry><entry></entry></row>
<row><entry>https://web.company.com/~user1</entry><entry>Fail</entry><entry>Different protocol</entry></row>
<row><entry>http://store.company.com/~user1</entry><entry>Fail</entry><entry>Different hostname</entry></row>
<row><entry>https://web.company.com:81/~user1</entry><entry>Fail</entry><entry>Different port</entry></row>
</tbody>
</tgroup>
</table>
</para>
<para>
As the example above shows, if a company servers webpages of users from the same domain web.company.com, then pages of individual users are not restricted by same origin policy when accessing each other, as they are coming from the same domain.
</para>
<para>
Browsers treat hostname of server as string literal, which creates another exceptional case: even if IP address of company.com is 10.20.30.40, browser will enforce same origin policy between http://company.com and http://10.20.30.40.
</para>
<section>
<title>Setting document.domain</title>
<para>
A page can also define its origin by setting <code>document.domain</code> property to a fully-qualified suffix of the current hostname. When two pages have defined the same <code>document.domain</code>, same origin policy is not applied. However, <code>document.domain</code> has to be specified mutually - it is not enough for just one page to specify its <code>document.domain</code>. Also, when <code>document.domain</code> property is set, port is set to null, while still being checked. This means company.com:8080 cannot bypass same origin policy and access company.com by setting <code>document.domain = "company.com"</code>, as their ports (null vs 80) differ.
</para>
<para>
However, <code>document.domain</code> has several issues:
<itemizedlist>
<listitem>
<para>When web.company.com and storage.company.com need to share resources and set <code>document.domain = company.com</code>, any subdomain can set its <code>document.domain</code> and access both of them, even though this access was not intended to be permitted.</para>
</listitem>
<listitem>
<para>
When this mechanism cannot be used, cross-domain requests are forbidden even for legitimate use, which creates problem for websites that use multiple (sub)domains.
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section>
<title>Unrestricted operations</title>
<para>Same Origin Policy restricts Javascript access to DOM and XMLHttpRequest across domains. However, there are multiple operations that are not restricted:
<itemizedlist>
<listitem>
<para>
Javascript embedding with <code>&lt;script src=".."&gt;&lt;script&gt;</code>
</para>
</listitem>
<listitem>
<para>
CSS embedding with <code>&lt;link rel="stylesheet" href="..."&gt;</code>
</para>
</listitem>
<listitem>
<para>
Anything with <code>&lt;frame&gt;</code> and <code>&lt;iframe&gt;</code>
</para>
</listitem>
<listitem>
<para>
.. and others
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section>
<title>Additional resources</title>
<itemizedlist>
<listitem>
<para>
Browser Security Handbook <ulink url="http://code.google.com/p/browsersec/wiki/Part2">http://code.google.com/p/browsersec/wiki/Part2</ulink>
</para>
</listitem>
<listitem>
<para>
Same Origin Policy article on Mozilla Developer Network <ulink url="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript</ulink>
</para>
</listitem>
</itemizedlist>
</section>
</section>
<section>
<title>Bypassing same origin policy</title>
<para>
Same Origin Policy as security mechanism leaves a lot to be desired: on one hand, it is not flexible enough to allow web developers use cross-domain resources in several legitimate usecases without exceptions to the rule and workarounds, on the other hand, such exceptions create opportunities for attacker.
</para>
<para>
There are several other mechanisms except <code>document.domain</code> that provide a way to relax Same Origin Policy.
</para>
<section id='CORS'>
<title>Cross-origin resource sharing (CORS)</title>
<para>
Cross-origin resource sharing is a mechanism that allows web application to inform browser, whether cross domain requests against the requested resource are expected.
</para>
<para>
Web browsers that conform to the CORS alter their behaviour of handling XMLHttpRequests: instead of denying the cross-domain request immediately, HTTP request is sent with <code>Origin</code> header. Let's assume http://example.com/testpage is making a XMLHttpRequest against http://content.com/wanted_image. Request would contain:
<programlisting>
GET /wanted_image HTTP/1.1
Referrer: http://example.com/testpage
Origin: http://example.com
</programlisting>
If the server allows sharing of the resource with domain that originated the request, the response would include:
<programlisting>
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
..
</programlisting>
By sending <constant>Access-Control-Allow-Origin</constant> header, server explicitly tells browser that this cross domain request shall be allowed. Allowed values of <constant>Access-Control-Allow-Origin</constant> are: * (denoting any domain, effectively marking the resource public) or space separated list of allowed origins (in practice, this usually contains just a single domain - one that was specified in Origin header in request).
</para>
<para>
If the resource should not be accessible by the originating domain, server ought not include Access-Control-Allow-Origin header in the response. By default, upon receiving such response from server browser will not pass the response back to the page that originated the request.
</para>
<para>
Several additional considerations:
<itemizedlist>
<listitem>
<para>
If the browser is outdated and does not conform to CORS, cross domain request will be denied immediately without sending the request to the server. This means usability of web applications relying on CORS might be restricted on old browsers.
</para>
</listitem>
<listitem>
<para>
If the web server does not conform to CORS, the Access-Control-Allow-Origin header will not be included in the response and the request will be denied on the client side.
</para>
</listitem>
<listitem>
<para>
Cross-domain access to resources is enforced on the side of the client. However, since the request includes Origin header, server may also restrict access to resources from other domains (e.g. by returning nothing).
</para>
</listitem>
<listitem>
<para>
If the origin of page is unknown (for example webpage is running from a file), browsers will send
<programlisting>
Origin: null
</programlisting>
</para>
</listitem>
</itemizedlist>
</para>
<section>
<title>Using CORS in Rack-based applications</title>
<para>
CORS support for Rack-based applications is provided by <ulink url="https://github.com/cyu/rack-cors">rack-cors</ulink> gem. After adding it to the applications Gemfile
<programlisting>
gem 'rack-cors', :require => 'rack/cors'
</programlisting>
and configure Rails by modifying config/application.rb:
<programlisting>
module YourApp
class Application &lt; Rails::Application
# ...
config.middleware.use Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :options]
end
end
end
end
</programlisting>
This configuration permits all origins access to any resource on the server via GET, POST and OPTIONS methods. Customizing the configuration, developer of the application can restrict cross-domain acess to resources by origin, headers and methods.
</para>
</section>
</section>
<section>
<title>JSON with padding (JSONP)</title>
<para>
JSONP is a very common way of hacking around the Same Origin Policy. This mechanism makes use of <code>&lt;script&gt;</code> tag and the fact that embedding Javascript code from other domains is not resctricted by the same origin policy. Since the code references by src attribute of <code>&lt;script&gt;</code> tag is loaded, it can be used as a vehicle to carry data and return them after evaluation.
</para>
<para>
Lets assume webpage needs to access resource at http://example.com/resource/1, which returns JSON data like:
<programlisting>
{"Key1": "Value1", "Key2": "Value2"}
</programlisting>
When webpage requests the resource with
<programlisting>
&lt;source src="http://example.com/resource/1"&gt;&lt;/source&gt;
</programlisting>
after receiving the response, browser will try to evaluate received data. Since data are not executable, interpreter would end with error and data would not be accessible to the code that requested it.
</para>
<para>
To work around this, it would be enough if the returned data were enclosed with function, that would be able to parse them on the client side. Suppose function <code>parseData</code> can accept JSON data as argument, parse it and make it accessible to the rest of the page:
<programlisting>
parseData({"Key1": "Value1", "Key2": "Value2"})
</programlisting>
</para>
<para>
However, web server does not know the name of the function that will parse data. Final piece is to pass the name of data-parsing function to server as parameter in request:
<programlisting>
&lt;script src="http://example.com/resource/1?jsonp=parseData"&gt;&lt;/script&gt;
</programlisting>
</para>
<para>
This technique of sharing resources across domains carries bigger security risks than CORS. Since <command>source</command> tag does not fall under Same Origin Policy on the client side, browser sends normal HTTP GET request without <constant>Origin</constant> header. Server that receives request has no means to know that the request was generated on behalf of page from other domain. Since neither the browser nor the server checks this kind of cross-domain requests, last obstacle that prevents exploitation is the fact that returned response is evaluated as Javascript code.
</para>
<para>
Example of this type of vulnerability is <ulink url="https://access.redhat.com/security/cve/CVE-2013-6443">CVE-2013-6443</ulink>. Cloud Forms Manage IQ application has been found vulnerable to cross-domain requests issued using JSONP. UI of application makes heavy use of Javascript and in this particular case changing the tab to "Authentication" would generate this HTTP request through XMLHttpRequest API:
<programlisting>
GET /ops/change_tab/?tab_id=settings_authentication&amp;callback=...
Referrer: ...
Cookie: ...
</programlisting>
Response returned by the server would look like this:
<programlisting>
HTTP/1.1 200 OK
....
miqButtons('hide');
Element.replace("ops_tabs", "&lt;div id=\"ops_tabs\" ...");
</programlisting>
where ops_tabs div contained html code of the Authentication tab including form with hidden CSRF token. To exploit this vulnerability, attacker would patch <command>Element.replace</command> function on his page and issue a JSONP request against CFME server.
<programlisting>
&lt;script src='http://code.jquery.com/jquery-1.10.2.min.js'&gt;&lt;/script&gt;
&lt;script&gt;
function test() {
$.ajax({
url: $( "input[name=url]" ).val() + '/ops/change_tab/?tab_id=settings_authentication',
dataType: 'jsonp'
});
};
var Element = { replace: function (a,text) {
...
}
&gt;/script&gt;
</programlisting>
This way attacker can run arbitrary code on returned response from the server: since the request also contains CSRF token, it is easy for attacker to steal it and issue successful CSRF request on behalf of currently logged-in user.
</para>
</section>
<section>
<title>Additional resources</title>
<itemizedlist>
<listitem>
<para>
W3C Recommendation - Cross-Origin Resouce Sharing <ulink url="http://www.w3.org/TR/access-control/">http://www.w3.org/TR/access-control/</ulink>
</para>
</listitem>
<listitem>
<para>
cross-site xmlhttprequest with CORS <ulink url="http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/">http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/</ulink>
</para>
</listitem>
<listitem>
<para>
Ajax and Mashup Security - Open Ajax Alliance <ulink url="http://www.openajax.org/whitepapers/Ajax%20and%20Mashup%20Security.php">http://www.openajax.org/whitepapers/Ajax%20and%20Mashup%20Security.php</ulink>
</para>
</listitem>
<listitem>
<para>
CVE-2013-6443 and reproducer by Martin Povolný <ulink url="https://access.redhat.com/security/cve/CVE-2013-6443">https://access.redhat.com/security/cve/CVE-2013-6443</ulink>
</para>
</listitem>
</itemizedlist>
</section>
</section>
<section>
<title>Content Security Policy</title>
<para></para>
</section>
<section id="HSTS">
<title>HTTP Strict Transport Security</title>
<para>HTTP Strict Transport Security is a mechanism that allows server to inform client that any interactions with the server shall be carried over secure HTTPS connection. </para>
<para>
HTTPS provides a secure tunnel between client and the server, yet there are still ways through which data can leak to the attacker. One of the most practical attacks on SSL is SSL stripping attack introduced by Moxie Marlinspike, in which active network attacker transparently converts HTTPS connection to insecure one. To the client it seems like web application does not support HTTPS and has no means to verify whether this is the case.
</para>
<para>HTTP Strict Transport Security mechanism allows server to inform client's user agent that the web application shall be accessed only through secure HTTPS connection. When client`s UA conformant with HSTS receives such notice from server, it enforces following behaviour:
<itemizedlist>
<listitem>
<para>
all references to HSTS host are converted into secure ones before dereferencing
</para>
</listitem>
<listitem>
<para>
connection is terminated upon any and all secure transport errors or warnings without interaction with user
</para>
</listitem>
</itemizedlist>
</para>
<para>
User agents which receive response with HSTS header need to retain data about host enforcing strict transport security for the timespan declared by the host. User agent builds a list of known HSTS hosts and whenever request is sent to known HSTS host, HTTPS is used.
</para>
<para>
HSTS header sent by the server includes timespan during which UA should enforce strict transport security in seconds:
<programlisting>
Strict-Transport-Security: max-age=631138519
</programlisting>
</para>
<para>
Optionally, server can also specify that HSTS be enforced on all subdomains:
<programlisting>
Strict-Transport-Security: max-age=631138519; includeSubDomains
</programlisting>
</para>
<para>
Setting timespan to zero
<programlisting>
Strict-Transport-Security: max-age=0
</programlisting>
allows the server to indicate that UA should delete HSTS policy associated with the host.
</para>
<section>
<title>Configuring HSTS in Rails</title>
<para>
A single directive in Rail configuration
<programlisting>
config.force_ssl = true
</programlisting>
enables HSTS for the application.
</para>
</section>
<section>
<title>References</title>
<para>
RFC 6797 <ulink url="http://tools.ietf.org/html/rfc6797">http://tools.ietf.org/html/rfc6797</ulink>
</para>
</section>
</section>
<section>
<title>Security related HTTP headers</title>
<para>
There are several HTTP headers that increase security of web application in various ways. Below is non-exhaustive list of most important ones, some of which were already mentioned and explained in previous sections:
</para>
<section>
<title>X-Frame-Options</title>
<para></para>
</section>
<section>
<title>X-XSS-Protection</title>
<para></para>
</section>
<section>
<title>X-Content-Type-Options</title>
<para></para>
</section>
<section>
<title>X-Content-Security-Policy</title>
<para></para>
</section>
<section>
<title>Access-Control-Allow-Origin</title>
<para>This response header is part of the implemetation of Cross Origin Resource Sharing described in <xref linkend='CORS'/>. Via this header server explicitly tells client`s browser whether cross-domain access to resource shall be permitted for the Origin specified.</para>
</section>
<section>
<title>Strict-Transport-Security</title>
<para>This header is part of HTTP Strict Transport Security described in <xref linkend='HSTS'/>. Using it server indicates that any requests from client shall be made through secure SSL connection and such policy be enforced for a declared timespan.</para>
</section>
</section>
</section>
<section>
<title>Application and server configuration and hardening</title>
<section>
<title>Logging</title>
<para></para>
</section>
<section>
<title>User content storage</title>
<para></para>
</section>
<section>
<title>Storing passwords securely</title>
<para></para>
</section>
</section>
</chapter>

View file

@ -0,0 +1,10 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<preface id="pref-Secure_Ruby_Development_Guide-Preface">
<title>Preface</title>
<xi:include href="Common_Content/Conventions.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
</preface>

View file

@ -0,0 +1,27 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE appendix PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<appendix id="appe-Secure_Ruby_Development_Guide-Revision_History">
<title>Revision History</title>
<simpara>
<revhistory>
<revision>
<revnumber>1-1</revnumber>
<date>Tue Feb 18 2014</date>
<author>
<firstname>Ján</firstname>
<surname>Rusnačko</surname>
<email>jrusnack@redhat.com</email>
</author>
<revdescription>
<simplelist>
<member>Initial creation of book</member>
</simplelist>
</revdescription>
</revision>
</revhistory>
</simpara>
</appendix>

View file

@ -0,0 +1,4 @@
<!ENTITY PRODUCT "Documentation">
<!ENTITY BOOKID "Secure_Ruby_Development_Guide">
<!ENTITY YEAR "2014">
<!ENTITY HOLDER "Ján Rusnačko">

View file

@ -0,0 +1,14 @@
<?xml version='1.0' encoding='utf-8' ?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "Secure_Ruby_Development_Guide.ent">
%BOOK_ENTITIES;
]>
<book>
<xi:include href="Book_Info.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Chapter1-Environment.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Chapter2-Language_features.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Chapter3-Ruby_on_Rails_Security.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="Revision_History.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<index />
</book>

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,9 @@
# Config::Simple 4.59
# Tue Feb 18 16:13:39 2014
xml_lang: en-US
type: Book
brand: fedora
chunk_section_depth: 2
version: 1
os_ver: .el6

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:42\n"
"PO-Revision-Date: 2013-09-18T00:49:42\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -0,0 +1,14 @@
#
# AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"
"Content-Type: application/x-publican; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -0,0 +1,14 @@
#
# AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"
"Content-Type: application/x-publican; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

View file

@ -4,8 +4,8 @@
msgid ""
msgstr ""
"Project-Id-Version: 0\n"
"POT-Creation-Date: 2014-02-10 11:03-0500\n"
"PO-Revision-Date: 2014-02-10 11:03-0500\n"
"POT-Creation-Date: 2013-09-18T00:49:43\n"
"PO-Revision-Date: 2013-09-18T00:49:43\n"
"Last-Translator: Automatically generated\n"
"Language-Team: None\n"
"MIME-Version: 1.0\n"

Some files were not shown because too many files have changed in this diff Show more