aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Bigg <radarlistener@gmail.com>2008-10-21 00:04:51 +1030
committerRyan Bigg <radarlistener@gmail.com>2008-10-21 00:04:51 +1030
commite6d5fc1020fe03190cf1c0337f64f7a6c848b12d (patch)
treed617cb741d5d0209fa5362fb0be44c01b3cd4c30
parenta912148151cfb5c8c5fd526a35cc2fbd13d46b3e (diff)
parent6937923d5a039aeaaa15f0a4ea3d688be7d92531 (diff)
downloadrails-e6d5fc1020fe03190cf1c0337f64f7a6c848b12d.tar.gz
rails-e6d5fc1020fe03190cf1c0337f64f7a6c848b12d.tar.bz2
rails-e6d5fc1020fe03190cf1c0337f64f7a6c848b12d.zip
Merge branch 'master' of git@github.com:lifo/docrails
-rw-r--r--railties/doc/guides/activerecord/active_record_basics.txt118
-rw-r--r--railties/doc/guides/debugging/debugging_rails_applications.txt192
-rw-r--r--railties/doc/guides/getting_started_with_rails/getting_started_with_rails.txt38
-rw-r--r--railties/doc/guides/routing/routing_outside_in.txt27
-rw-r--r--railties/doc/guides/securing_rails_applications/security.txt271
5 files changed, 439 insertions, 207 deletions
diff --git a/railties/doc/guides/activerecord/active_record_basics.txt b/railties/doc/guides/activerecord/active_record_basics.txt
index 345b377dfd..60ee1ef7b7 100644
--- a/railties/doc/guides/activerecord/active_record_basics.txt
+++ b/railties/doc/guides/activerecord/active_record_basics.txt
@@ -1,2 +1,120 @@
ActiveRecord Basics
=================================
+This guide will explain in detail how the ActiveRecord design pattern is used inside Ruby on Rails to make communication with the database clear and easy to understand.
+The intent of this guide is to explain the ActiveRecord implementation used by Rails though easy to understand examples, metaphors and detailed explanations of the actual Rails source code.
+After reading this guide readers should have a strong grasp of the ActiveRecord concept and how it can be used with or without Rails. Hopefully, some of the philosophical and theoretical intentions discussed here will also make them a stronger and better developer.
+== ORM The Blueprint of ActiveRecord
+If ActiveRecord is the engine of Rails then ORM is the blueprint of that engine. ORM is short for “Object Relational Mapping” and is a programming concept used to make structures within a system relational. ORM seeks to give semantic meaning to the associations between elements of the system for example tables within a database.
+As a thought experiment imagine the components that make up a typical car. There are doors, seats, windows, engines etc. Viewed independently they are simple parts, yet when bolted together through the aid of a blueprint, the parts become a more complex device. ORM is the blueprint that describes how the individual parts relate to one another and in some cases infers the part’s purpose through the way the associations are described.
+== ActiveRecord The Engine of Rails
+ActiveRecord is a metaphor used to access data within a database. The name “Active Record” was coined by Martin Fowler in his book “Patterns of Enterprise Application Architecture”. ActiveRecord is a conceptual model of the database record and the relationships to other records.
+As a side note, from now when I refer to ActiveRecord I’ll be referring to the specific Rails implementation and not the design pattern in general. I make this distinction because, as Rails has evolved so too has the Rails specific implementation of their version of ActiveRecord.
+Specifically, the Rails ActiveRecord pattern adds inheritance and associations. The associations are created by using a DSL (domain specific language) of macros, and a STI (Single Table Inheritance) to facilitate the inheritance.
+Rails uses ActiveRecord to abstract much of the drudgery or C.R.U.D (explained later) of working with data in databases. Using ActiveRecord Rails automates the mapping between:
+* Classes & Database Tables
+* Class attributes & Database Table Columns
+For example suppose you created a database table called cars:
+[source, sql]
+-------------------------------------------------------
+mysql> CREATE TABLE cars (
+ id INT,
+ color VARCHAR(100),
+ doors INT,
+ horses INT,
+ model VARCHAR(100)
+ );
+-------------------------------------------------------
+Now you created a class named Car, which is to represent an instance of a record from your table.
+[source, ruby]
+-------------------------------------------------------
+class Car
+end
+-------------------------------------------------------
+As you might expect without defining the explicit mappings between your class and the table it is impossible for Rails or any other program to correctly map those relationships.
+[source, ruby]
+-------------------------------------------------------
+>> c = Car.new
+=> #<Class:0x11e1e90>
+>> c.doors
+NoMethodError: undefined method `doors' for #<Class:0x11e1e90>
+ from (irb):2
+-------------------------------------------------------
+Now you could define a door methods to write and read data to and from the database. In a nutshell this is what ActiveRecord does. According to the Rails API:
+“Active Record objects don‘t specify their attributes directly, but rather infer them from the table definition with which they‘re linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.”
+Lets try our Car class again, this time inheriting from ActiveRecord.
+[source, ruby]
+-------------------------------------------------------
+class Car < ActiveRecord::Base
+end
+-------------------------------------------------------
+Now if we try to access an attribute of the table ActiveRecord automatically handles the mappings for us, as you can see in the following example.
+[source, ruby]
+-------------------------------------------------------
+>> c = Car.new
+=> #<Car id: nil, doors: nil, color: nil, horses: nil, model: nil>
+>> c.doors
+=> nil
+-------------------------------------------------------
+
+This wrapper implements attribute accessors, callbacks and validations, which can make the data more powerful.
+- Validations
+ * create!
+ * validates_acceptance_of
+ * validates_associated
+ * validates_confirmation_of
+ * validates_each
+ * validates_exclusion_of
+ * validates_format_of
+ * validates_inclusion_of
+ * validates_length_of
+ * validates_numericality_of
+ * validates_presence_of
+ * validates_size_of
+ * validates_uniqueness_of
+ - Callback
+ * (-) save
+ * (-) valid
+ * (1) before_validation
+ * (2) before_validation_on_create
+ * (-) validate
+ * (-) validate_on_create
+ * (3) after_validation
+ * (4) after_validation_on_create
+ * (5) before_save
+ * (6) before_create
+ * (-) create
+ * (7) after_create
+ * (8) after_save
+
+Rails further extends this model by giving each ActiveRecord a way of describing the variety of ways records are associated with one another. We will touch on some of these associations later in the guide but I encourage readers who are interested to read the guide to ActiveRecord associations for an in-depth explanation of the variety of ways rails can model associations.
+- Associations between objects controlled by meta-programming macros.
+
+== Philosophical Approaches & Common Conventions
+Rails has a reputation of being a zero-config framework which means that it aims to get you off the ground with as little pre-flight checking as possible. This speed benefit is achieved by following “Convention over Configuration”, which is to say that if you agree to live with the defaults then you benefit from a the inherent speed-boost. As Courtneay Gasking put it to me once “You don’t want to off-road on Rails”. ActiveRecord is no different, while it’s possible to override or subvert any of the conventions of AR, unless you have a good reason for doing so you will probably be happy with the defaults. The following is a list of the common conventions of ActiveRecord
+
+
+ActiveRecord is the default model component of the Model-view-controller web-application framework Ruby on Rails, and is also a stand-alone ORM package for other Ruby applications. In both forms, it was conceived of by David Heinemeier Hansson, and has been improved upon by a number of contributors. --wikipedia
+
+ - Naming Conventions
+ - Class Names are Singular
+ - Tables names are the plural name of the class name
+ - Tables contain an identity column named id
+ - ids
+== ActiveRecord Magic
+ - timestamps
+ - updates
+
+== How ActiveRecord Maps your Database.
+- sensible defaults
+- overriding conventions
+
+== Growing Your Database Relationships Naturally
+
+== Attributes
+ - attribute accessor method. How to override them?
+ - attribute?
+ - dirty records
+ -
+== ActiveRecord handling the CRUD of your Rails application - Understanding the life-cycle of an ActiveRecord
+
+== Validations & Callbacks \ No newline at end of file
diff --git a/railties/doc/guides/debugging/debugging_rails_applications.txt b/railties/doc/guides/debugging/debugging_rails_applications.txt
index f7538cd08b..24eb0c0431 100644
--- a/railties/doc/guides/debugging/debugging_rails_applications.txt
+++ b/railties/doc/guides/debugging/debugging_rails_applications.txt
@@ -1,7 +1,7 @@
Debugging Rails Applications
============================
-This guide covers how to debug Ruby on Rails applications. By referring to this guide, you will be able to:
+This guide introduces techniques for debugging Ruby on Rails applications. By referring to this guide, you will be able to:
* Understand the purpose of debugging
* Track down problems and issues in your application that your tests aren't identifying
@@ -18,7 +18,7 @@ One common task is to inspect the contents of a variable. In Rails, you can do t
=== debug
-`debug` will return a <pre>-tag that has object dumped by YAML. Generating readable output to inspect any object.
+The `debug` helper will return a <pre>-tag that renders the object using the YAML format. This will generate human-readable data from any object. For example, if you have this code in a view:
[source, html]
----------------------------------------------------------------------------
@@ -29,8 +29,9 @@ One common task is to inspect the contents of a variable. In Rails, you can do t
</p>
----------------------------------------------------------------------------
-Will render something like this:
+You'll see something like this:
+[source, log]
----------------------------------------------------------------------------
--- !ruby/object:Post
attributes:
@@ -59,10 +60,11 @@ Displaying an instance variable, or any other object or method, in yaml format c
</p>
----------------------------------------------------------------------------
-`to_yaml` converts the method to yaml format leaving it more readable and finally `simple_format` help us to render each line as in the console. This is how `debug` method does its magic.
+The `to_yaml` method converts the method to YAML format leaving it more readable, and then the `simple_format` helper is used to render each line as in the console. This is how `debug` method does its magic.
As a result of this, you will have something like this in your view:
+[source, log]
----------------------------------------------------------------------------
--- !ruby/object:Post
attributes:
@@ -79,7 +81,7 @@ Title: Rails debugging guide
=== inspect
-Another useful method for displaying object values is `inspect`, especially when working with arrays or hashes, it will print the object value as a string, for example:
+Another useful method for displaying object values is `inspect`, especially when working with arrays or hashes. This will print the object value as a string. For example:
[source, html]
----------------------------------------------------------------------------
@@ -92,6 +94,7 @@ Another useful method for displaying object values is `inspect`, especially when
Will be rendered as follows:
+[source, log]
----------------------------------------------------------------------------
[1, 2, 3, 4, 5]
@@ -100,11 +103,13 @@ Title: Rails debugging guide
== The Logger
-=== What is it?
+It can also be useful to save information to log files at runtime. Rails maintains a separate log file for each runtime environment.
-Rails makes use of ruby's standard `logger` to write log information. You can also substitute another logger such as `Log4R` if you wish.
+=== What is The Logger?
-If you want to change the logger you can specify it in your +environment.rb+ or any environment file.
+Rails makes use of Ruby's standard `logger` to write log information. You can also substitute another logger such as `Log4R` if you wish.
+
+You can specify an alternative logger in your +environment.rb+ or any environment file:
[source, ruby]
----------------------------------------------------------------------------
@@ -112,7 +117,7 @@ ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
----------------------------------------------------------------------------
-Or in the `__Initializer__` section, add _any_ of the following
+Or in the +Initializer+ section, add _any_ of the following
[source, ruby]
----------------------------------------------------------------------------
@@ -125,9 +130,9 @@ By default, each log is created under `RAILS_ROOT/log/` and the log file name is
=== Log Levels
-When something is logged it's printed into the corresponding log if the message log level is equal or higher than the configured log level. If you want to know the current log level just call `ActiveRecord::Base.logger.level` method.
+When something is logged it's printed into the corresponding log if the log level of the message is equal or higher than the configured log level. If you want to know the current log level you can call the `ActiveRecord::Base.logger.level` method.
-The available log levels are: +:debug+, +:info+, +:warn+, +:error+, +:fatal+, each level has a log level number from 0 up to 4 respectively. To change the default log level, use
+The available log levels are: +:debug+, +:info+, +:warn+, +:error+, and +:fatal+, corresponding to the log level numbers from 0 up to 4 respectively. To change the default log level, use
[source, ruby]
----------------------------------------------------------------------------
@@ -138,7 +143,7 @@ ActiveRecord::Base.logger.level = 0 # at any time
This is useful when you want to log under development or staging, but you don't want to flood your production log with unnecessary information.
[TIP]
-Rails default log level is +info+ in production mode and +debug+ in development and test mode.
+The default Rails log level is +info+ in production mode and +debug+ in development and test mode.
=== Sending Messages
@@ -151,7 +156,7 @@ logger.info "Processing the request..."
logger.fatal "Terminating application, raised unrecoverable error!!!"
----------------------------------------------------------------------------
-A common example:
+Here's an example of a method instrumented with extra logging:
[source, ruby]
----------------------------------------------------------------------------
@@ -176,45 +181,49 @@ class PostsController < ApplicationController
end
----------------------------------------------------------------------------
-Will be logged like this:
+Here's an example of the log generated by this method:
+[source, log]
----------------------------------------------------------------------------
Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST]
- Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
- Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>"0"}, "authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"posts"}
-New post: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!", "published"=>false, "created_at"=>nil}
+ Session ID: BAh7BzoMY3NyZl9pZCIlMDY5MWU1M2I1ZDRjODBlMzkyMWI1OTg2NWQyNzViZjYiCmZsYXNoSUM6J0FjdGl
+vbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhhc2h7AAY6CkB1c2VkewA=--b18cd92fba90eacf8137e5f6b3b06c4d724596a4
+ Parameters: {"commit"=>"Create", "post"=>{"title"=>"Debugging Rails",
+ "body"=>"I'm learning how to print in logs!!!", "published"=>"0"},
+ "authenticity_token"=>"2059c1286e93402e389127b1153204e0d1e275dd", "action"=>"create", "controller"=>"posts"}
+New post: {"updated_at"=>nil, "title"=>"Debugging Rails", "body"=>"I'm learning how to print in logs!!!",
+ "published"=>false, "created_at"=>nil}
Post should be valid: true
- Post Create (0.000443) INSERT INTO "posts" ("updated_at", "title", "body", "published", "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails', 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
+ Post Create (0.000443) INSERT INTO "posts" ("updated_at", "title", "body", "published",
+ "created_at") VALUES('2008-09-08 14:52:54', 'Debugging Rails',
+ 'I''m learning how to print in logs!!!', 'f', '2008-09-08 14:52:54')
The post was saved and now is the user is going to be redirected...
Redirected to #<Post:0x20af760>
Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts]
----------------------------------------------------------------------------
-Notice the logged lines, now you can search for any unexpected behavior in the output.
-
-By now you should know how to use the logs in any environment. Remember to take advantage of the log levels and use them wisely, mostly in production mode.
+Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels, to avoid filling your production logs with useless trivia.
== Debugging with ruby-debug
-Many times your code may not behave as you expect, sometimes you will try to print in logs, console or view values to make a diagnostic of the problem.
+When your code is behaving in unexpected ways, you can try printing to logs or the console to diagnose the problem. Unfortunately, there are times when this sort of error tracking is not effective in finding the root cause of a problem. When you actually need to journey into your running source code, the debugger is your best companion.
-Unfortunately, you won't find always the answer you are looking for this way. In that case, you will need to know what's happening and adventure into Rails, in this journey the debugger will be your best companion.
-
-If you ever wanted to learn about Rails source code but you didn't know where to start, this may be the best way, just debug any request to your application and use this guide to learn how to move in the code you have written but also go deeper into Rails code.
+The debugger can also help you if you want to learn about the Rails source code but don't know where to start. Just debug any request to your application and use this guide to learn how to move from the code you have written deeper into Rails code.
=== Setup
-Ruby-debug comes as a gem so to install, just run:
+The debugger used by Rails, +ruby-debug+, comes as a gem. To install it, just run:
+[source, shell]
----------------------------------------------------------------------------
-$ sudo gem in ruby-debug
+$ sudo gem install ruby-debug
----------------------------------------------------------------------------
-In case you want to download a particular version or get the source code, refer to link:http://rubyforge.org/projects/ruby-debug/[project's page on rubyforge].
+In case you want to download a particular version or get the source code, refer to the link:http://rubyforge.org/projects/ruby-debug/[project's page on rubyforge].
-Rails has built-in support for ruby-debug since April 28, 2007. Inside any Rails application you can invoke the debugger by calling the `debugger` method.
+Rails has had built-in support for ruby-debug since Rails 2.0. Inside any Rails application you can invoke the debugger by calling the `debugger` method.
-Let's take a look at an example:
+Here's an example:
[source, ruby]
----------------------------------------------------------------------------
@@ -228,26 +237,32 @@ end
If you see the message in the console or logs:
+[source, log]
----------------------------------------------------------------------------
***** Debugger requested, but was not available: Start server with --debugger to enable *****
----------------------------------------------------------------------------
-Make sure you have started your web server with the option --debugger:
+Make sure you have started your web server with the option +--debugger+:
+[source, shell]
----------------------------------------------------------------------------
~/PathTo/rails_project$ script/server --debugger
+=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
+=> Rails 2.2.0 application starting on http://0.0.0.0:3000
+=> Debugger enabled
+...
----------------------------------------------------------------------------
[TIP]
-In development mode, you can dynamically `require \'ruby-debug\'` instead of restarting the server, in case it was started without `--debugger`.
+In development mode, you can dynamically `require \'ruby-debug\'` instead of restarting the server, if it was started without `--debugger`.
In order to use Rails debugging you'll need to be running either *WEBrick* or *Mongrel*. For the moment, no alternative servers are supported.
=== The Shell
-As soon as your application calls the `debugger` method, the debugger will be started in a debugger shell inside the terminal window you've fired up your application server and you will be placed in the ruby-debug's prompt `(rdb:n)`. The _n_ is the thread number.
+As soon as your application calls the `debugger` method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at ruby-debug's prompt `(rdb:n)`. The _n_ is the thread number. The prompt will also show you the next line of code that is waiting to run.
-If you got there by a browser request, the browser will be hanging until the debugger has finished and the trace has completely run as any normal request.
+If you got there by a browser request, the browser tab containing the request will be hung until the debugger has finished and the trace has finished processing the entire request.
For example:
@@ -256,7 +271,7 @@ For example:
(rdb:7)
----------------------------------------------------------------------------
-Now it's time to play and dig into our application. The first we are going to do is ask our debugger for help... so we type: `help` (You didn't see that coming, right?)
+Now it's time to play and dig into your application. A good place to start is by asking the debugger for help... so type: `help` (You didn't see that coming, right?)
----------------------------------------------------------------------------
(rdb:7) help
@@ -272,11 +287,11 @@ continue edit frame method putl set tmate where
----------------------------------------------------------------------------
[TIP]
-To view the help menu for any command use `help <command-name>` in active debug mode. For example: _help var_
+To view the help menu for any command use `help <command-name>` in active debug mode. For example: _+help var+_
-The second command before we move on, is one of the most useful command: `list` (or his shorthand `l`).
+The next command to learn is one of the most useful: `list`. You can also abbreviate ruby-debug commands by supplying just enough letters to distinguish them from other commands, so you can also use +l+ for the +list+ command.
-This command will give us a starting point of where we are by printing 10 lines centered around the current line; the current line here is line 6 and is marked by =>.
+This command shows you where you are in the code by printing 10 lines centered around the current line; the current line in this particular case is line 6 and is marked by +=>+.
----------------------------------------------------------------------------
(rdb:7) list
@@ -293,7 +308,7 @@ This command will give us a starting point of where we are by printing 10 lines
10 format.xml { render :xml => @posts }
----------------------------------------------------------------------------
-If we do it again, this time using just `l`, the next ten lines of the file will be printed out.
+If you repeat the +list+ command, this time using just `l`, the next ten lines of the file will be printed out.
----------------------------------------------------------------------------
(rdb:7) l
@@ -310,14 +325,15 @@ If we do it again, this time using just `l`, the next ten lines of the file will
20 format.html # show.html.erb
----------------------------------------------------------------------------
-And so on until the end of the current file, when the end of file is reached, it will start again from the beginning of the file and continue again up to the end, acting as a circular buffer.
+And so on until the end of the current file. When the end of file is reached, the +list+ command will start again from the beginning of the file and continue again up to the end, treating the file as a circular buffer.
=== The Context
-When we start debugging your application, we will be placed in different contexts as you go through the different parts of the stack.
-A context will be created when a stopping point or an event is reached. It has information about the suspended program which enable a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place the debugged program is stopped.
+When you start debugging your application, you will be placed in different contexts as you go through the different parts of the stack.
-At any time we can call the `backtrace` command (or alias `where`) to print the backtrace of the application, this is very helpful to know how we got where we are. If you ever wondered about how you got somewhere in your code, then `backtrace` is your answer.
+ruby-debug creates a content when a stopping point or an event is reached. The context has information about the suspended program which enables a debugger to inspect the frame stack, evaluate variables from the perspective of the debugged program, and contains information about the place where the debugged program is stopped.
+
+At any time you can call the `backtrace` command (or its alias `where`) to print the backtrace of the application. This can be very helpful to know how you got where you are. If you ever wondered about how you got somewhere in your code, then `backtrace` will supply the answer.
----------------------------------------------------------------------------
(rdb:5) where
@@ -332,7 +348,7 @@ At any time we can call the `backtrace` command (or alias `where`) to print the
...
----------------------------------------------------------------------------
-You move anywhere you want in this trace using the `frame _n_` command, where _n_ is the specified frame number.
+You move anywhere you want in this trace (thus changing the context) by using the `frame _n_` command, where _n_ is the specified frame number.
----------------------------------------------------------------------------
(rdb:5) frame 2
@@ -340,27 +356,27 @@ You move anywhere you want in this trace using the `frame _n_` command, where _n
at line /PathTo/project/vendor/rails/actionpack/lib/action_controller/base.rb:1175
----------------------------------------------------------------------------
-The available variables are the same as if we were running the code line by line, after all, that's what debugging is.
+The available variables are the same as if you were running the code line by line. After all, that's what debugging is.
-Moving up and down the stack frame: You can use `up [n]` (`u` for abbreviated) and `down [n]` commands in order to change the context _n_ frames up or down the stack respectively. _n_ defaults to one.
+Moving up and down the stack frame: You can use `up [n]` (`u` for abbreviated) and `down [n]` commands in order to change the context _n_ frames up or down the stack respectively. _n_ defaults to one. Up in this case is towards higher-numbered stack frames, and down is towards lower-numbered stack frames.
=== Threads
-The debugger can list, stop, resume and switch between running threads, the command `thread` (or the abbreviated `th`) is used an allows the following options:
+The debugger can list, stop, resume and switch between running threads by using the command `thread` (or the abbreviated `th`). This command has a handful of options:
* `thread` shows the current thread.
-* `thread list` command is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution.
+* `thread list` is used to list all threads and their statuses. The plus + character and the number indicates the current thread of execution.
* `thread stop _n_` stop thread _n_.
-* `thread resume _n_` resume thread _n_.
-* `thread switch _n_` switch thread context to _n_.
+* `thread resume _n_` resumes thread _n_.
+* `thread switch _n_` switches the current thread context to _n_.
This command is very helpful, among other occasions, when you are debugging concurrent threads and need to verify that there are no race conditions in your code.
=== Inspecting Variables
-Any expression can be evaluated in the current context, just type it!
+Any expression can be evaluated in the current context. To evaluate an expression, just type it!
-In the following example we will print the instance_variables defined within the current context.
+This example shows how you can print the instance_variables defined within the current context:
----------------------------------------------------------------------------
@posts = Post.find(:all)
@@ -368,7 +384,7 @@ In the following example we will print the instance_variables defined within the
["@_response", "@action_name", "@url", "@_session", "@_cookies", "@performed_render", "@_flash", "@template", "@_params", "@before_filter_chain_aborted", "@request_origin", "@_headers", "@performed_redirect", "@_request"]
----------------------------------------------------------------------------
-As you may have figured out, all variables that you can access from a controller are displayed, lets run the next line, we will use `next` (we will get later into this command).
+As you may have figured out, all of the variables that you can access from a controller are displayed. This list is dynamically updated as you execute code. For example, run the next line using `next` (you'll learn more about this command later in this guide).
----------------------------------------------------------------------------
(rdb:11) next
@@ -379,19 +395,19 @@ Processing PostsController#index (for 127.0.0.1 at 2008-09-04 19:51:34) [GET]
respond_to do |format|
-------------------------------------------------------------------------------
-And we'll ask again for the instance_variables.
+And then ask again for the instance_variables:
----------------------------------------------------------------------------
(rdb:11) instance_variables.include? "@posts"
true
----------------------------------------------------------------------------
-Now +@posts+ is a included in them, because the line defining it was executed.
+Now +@posts+ is a included in the instance variables, because the line defining it was executed.
[TIP]
-You can also step into *irb* mode with the command `irb` (of course!). This way an irb session will be started within the context you invoked it. But you must know that this is an experimental feature.
+You can also step into *irb* mode with the command `irb` (of course!). This way an irb session will be started within the context you invoked it. But be warned: this is an experimental feature.
-To show variables and their values the `var` method is the most convenient way:
+The `var` method is the most convenient way to show variables and their values:
----------------------------------------------------------------------------
var
@@ -401,7 +417,7 @@ var
(rdb:1) v[ar] l[ocal] show local variables
----------------------------------------------------------------------------
-This is a great way for inspecting the values of the current context variables. For example:
+This is a great way to inspect the values of the current context variables. For example:
----------------------------------------------------------------------------
(rdb:9) var local
@@ -418,16 +434,16 @@ You can also inspect for an object method this way:
----------------------------------------------------------------------------
[TIP]
-Commands `p` (print) and `pp` (pretty print) can be used to evaluate Ruby expressions and display the value of variables to the console.
+The commands `p` (print) and `pp` (pretty print) can be used to evaluate Ruby expressions and display the value of variables to the console.
-We can use also `display` to start watching variables, this is a good way of tracking values of a variable while the execution goes on.
+You can use also `display` to start watching variables. This is a good way of tracking the values of a variable while the execution goes on.
----------------------------------------------------------------------------
(rdb:1) display @recent_comments
1: @recent_comments =
----------------------------------------------------------------------------
-The variables inside the displaying list will be printed with their values after we move in the stack. To stop displaying a variable use `undisplay _n_` where _n_ is the variable number (1 in the last example).
+The variables inside the displaying list will be printed with their values after you move in the stack. To stop displaying a variable use `undisplay _n_` where _n_ is the variable number (1 in the last example).
=== Step by Step
@@ -440,9 +456,9 @@ You can also use `step+ _n_` and `step- _n_` to move forward or backward _n_ ste
You may also use `next` which is similar to step, but function or method calls that appear within the line of code are executed without stopping. As with step, you may use plus sign to move _n_ steps.
-The difference between `next` and `step` is that `step` stops at the next line of code executed, doing just single step, while `next` moves to the next line without descending inside methods.
+The difference between `next` and `step` is that `step` stops at the next line of code executed, doing just a single step, while `next` moves to the next line without descending inside methods.
-Lets run the next line in this example:
+For example, consider this block of code with an included +debugger+ statement:
[source, ruby]
----------------------------------------------------------------------------
@@ -462,7 +478,7 @@ end
----------------------------------------------------------------------------
[TIP]
-You can use ruby-debug while using script/console but remember to `require "ruby-debug"` before calling `debugger` method.
+You can use ruby-debug while using script/console. Just remember to `require "ruby-debug"` before calling the `debugger` method.
----------------------------------------------------------------------------
/PathTo/project $ script/console
@@ -476,7 +492,7 @@ Loading development environment (Rails 2.1.0)
)
----------------------------------------------------------------------------
-Now we are where we wanted to be, lets look around.
+With the code stopped, take a look around:
----------------------------------------------------------------------------
(rdb:1) list
@@ -491,7 +507,7 @@ Now we are where we wanted to be, lets look around.
13 end
----------------------------------------------------------------------------
-We are at the end of the line, but... was this line executed? We can inspect the instance variables.
+You are at the end of the line, but... was this line executed? You can inspect the instance variables.
----------------------------------------------------------------------------
(rdb:1) var instance
@@ -499,7 +515,7 @@ We are at the end of the line, but... was this line executed? We can inspect the
@attributes_cache = {}
----------------------------------------------------------------------------
-+@recent_comments+ hasn't been defined yet, so we can assure this line hasn't been executed yet, lets move on this code.
++@recent_comments+ hasn't been defined yet, so it's clear that this line hasn't been executed yet. Use the +next+ command to move on in the code:
----------------------------------------------------------------------------
(rdb:1) next
@@ -512,15 +528,15 @@ We are at the end of the line, but... was this line executed? We can inspect the
@recent_comments = []
----------------------------------------------------------------------------
-Now we can see how +@comments+ relationship was loaded and @recent_comments defined because the line was executed.
+Now you can see that the +@comments+ relationship was loaded and @recent_comments defined because the line was executed.
-In case we want deeper in the stack trace we can move single `steps` and go into Rails code, this is the best way for finding bugs in your code, or maybe in Ruby or Rails.
+If you want to go deeper into the stack trace you can move single `steps`, through your calling methods and into Rails code. This is one of the best ways to find bugs in your code, or perhaps in Ruby or Rails.
=== Breakpoints
-A breakpoint makes your application stop whenever a certain point in the program is reached and the debugger shell is invoked in that line.
+A breakpoint makes your application stop whenever a certain point in the program is reached. The debugger shell is invoked in that line.
-You can add breakpoints dynamically with the command `break` (or just `b`), there are 3 possible ways of adding breakpoints manually:
+You can add breakpoints dynamically with the command `break` (or just `b`). There are 3 possible ways of adding breakpoints manually:
* `break line`: set breakpoint in the _line_ in the current source file.
* `break file:line [if expression]`: set breakpoint in the _line_ number inside the _file_. If an _expression_ is given it must evaluated to _true_ to fire up the debugger.
@@ -531,7 +547,7 @@ You can add breakpoints dynamically with the command `break` (or just `b`), ther
Breakpoint 1 file /PathTo/project/vendor/rails/actionpack/lib/action_controller/filters.rb, line 10
----------------------------------------------------------------------------
-Use `info breakpoints _n_` or `info break _n_` lo list breakpoints, is _n_ is defined it shows that breakpoints, otherwise all breakpoints are listed.
+Use `info breakpoints _n_` or `info break _n_` to list breakpoints. If you supply a number, it lists that breakpoint. Otherwise it lists all breakpoints.
----------------------------------------------------------------------------
(rdb:5) info breakpoints
@@ -539,7 +555,7 @@ Num Enb What
1 y at filters.rb:10
----------------------------------------------------------------------------
-Deleting breakpoints: use the command `delete _n_` to remove the breakpoint number _n_ or all of them if _n_ is not specified.
+To delete breakpoints: use the command `delete _n_` to remove the breakpoint number _n_. If no number is specified, it deletes all breakpoints that are currently active..
----------------------------------------------------------------------------
(rdb:5) delete 1
@@ -547,57 +563,60 @@ Deleting breakpoints: use the command `delete _n_` to remove the breakpoint numb
No breakpoints.
----------------------------------------------------------------------------
-Enabling/Disabling breakpoints:
+You can also enable or disable breakpoints:
-* `enable breakpoints`: allow a list _breakpoints_ or all of them if none specified, to stop your program (this is the default state when you create a breakpoint).
+* `enable breakpoints`: allow a list _breakpoints_ or all of them if no list is specified, to stop your program. This is the default state when you create a breakpoint.
* `disable breakpoints`: the _breakpoints_ will have no effect on your program.
=== Catching Exceptions
The command `catch exception-name` (or just `cat exception-name`) can be used to intercept an exception of type _exception-name_ when there would otherwise be is no handler for it.
-To list existent catchpoints use `catch`.
+To list all active catchpoints use `catch`.
=== Resuming Execution
+There are two ways to resume execution of an application that is stopped in the debugger:
+
* `continue` [line-specification] (or `c`): resume program execution, at the address where your script last stopped; any breakpoints set at that address are bypassed. The optional argument line-specification allows you to specify a line number to set a one-time breakpoint which is deleted when that breakpoint is reached.
-* `finish` [frame-number] (or `fin`): execute until selected stack frame returns. If no frame number is given, we run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been performed. If a frame number is given we run until frame frames returns.
+* `finish` [frame-number] (or `fin`): execute until the selected stack frame returns. If no frame number is given, the application run until the currently selected frame returns. The currently selected frame starts out the most-recent frame or 0 if no frame positioning (e.g up, down or frame) has been performed. If a frame number is given it will run until the specified frame returns.
=== Editing
-At any time, you may use any of this commands to edit the code you are evaluating:
+Two commands allow you to open code from the debugger into an editor:
* `edit [file:line]`: edit _file_ using the editor specified by the EDITOR environment variable. A specific _line_ can also be given.
* `tmate _n_` (abbreviated `tm`): open the current file in TextMate. It uses n-th frame if _n_ is specified.
=== Quitting
-To exit the debugger, use the `quit` command (abbreviated `q`), or alias `exit`.
+
+To exit the debugger, use the `quit` command (abbreviated `q`), or its alias `exit`.
A simple quit tries to terminate all threads in effect. Therefore your server will be stopped and you will have to start it again.
=== Settings
-There are some settings that can be configured in ruby-debug to make it easier to debug your code, being among others useful options:
+There are some settings that can be configured in ruby-debug to make it easier to debug your code. Here are a few of the available options:
* `set reload`: Reload source code when changed.
* `set autolist`: Execute `list` command on every breakpoint.
-* `set listsize _n_`: Set number of source lines to list by default _n_.
-* `set forcestep`: Make sure `next` and `step` commands always move to a new line
+* `set listsize _n_`: Set number of source lines to list by default to _n_.
+* `set forcestep`: Make sure the `next` and `step` commands always move to a new line
-You can see the full list by using `help set` or `help set subcommand` to inspect any of them.
+You can see the full list by using `help set`. Use `help set _subcommand_` to learn about a particular +set+ command.
[TIP]
-You can include any number of this configuration lines inside a `.rdebugrc` file in your HOME directory, and ruby-debug will read it every time it is loaded
+You can include any number of these configuration lines inside a `.rdebugrc` file in your HOME directory. ruby-debug will read this file every time it is loaded. and configure itself accordingly.
-The following lines are recommended to be included in `.rdebugrc`:
+Here's a good start for an `.rdebugrc`:
+[source, log]
----------------------------------------------------------------------------
set autolist
set forcestep
set listsize 25
----------------------------------------------------------------------------
-
== References
* link:http://www.datanoise.com/ruby-debug[ruby-debug Homepage]
@@ -614,4 +633,5 @@ set listsize 25
http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/5[Lighthouse ticket]
+* October 19, 2008: Copy editing pass by link:../authors.html#mgunderloy[Mike Gunderloy]
* September 16, 2008: initial version by link:../authors.html#miloops[Emilio Tagua]
diff --git a/railties/doc/guides/getting_started_with_rails/getting_started_with_rails.txt b/railties/doc/guides/getting_started_with_rails/getting_started_with_rails.txt
index b6d8203c8b..a3493abfe8 100644
--- a/railties/doc/guides/getting_started_with_rails/getting_started_with_rails.txt
+++ b/railties/doc/guides/getting_started_with_rails/getting_started_with_rails.txt
@@ -417,14 +417,13 @@ Rails includes methods to help you validate the data that you send to models. Op
[source, ruby]
-------------------------------------------------------
-class Comment < ActiveRecord::Base
- belongs_to :post
- validates_presence_of :commenter, :body
- validates_length_of :commenter, :minimum => 5
+class Post < ActiveRecord::Base
+ validates_presence_of :name, :title
+ validates_length_of :title, :minimum => 5
end
-------------------------------------------------------
-These changes will ensure that all comments have a body and a commenter, and that the commenter is at least five characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects.
+These changes will ensure that all posts have a name and a title, and that the title is at least five characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects.
=== Using the Console
@@ -439,19 +438,19 @@ After the console loads, you can use it to work with your application's models:
[source, shell]
-------------------------------------------------------
->> c = Comment.create(:body => "A new comment")
-=> #<Comment id: nil, commenter: nil, body: "A new comment",
-post_id: nil, created_at: nil, updated_at: nil>
->> c.save
+>> p = Post.create(:content => "A new post")
+=> #<Post id: nil, name: nil, title: nil, content: "A new post",
+created_at: nil, updated_at: nil>
+>> p.save
=> false
->> c.errors
-=> #<ActiveRecord::Errors:0x227be7c @base=#<Comment id: nil,
-commenter: nil, body: "A new comment", post_id: nil, created_at: nil,
-updated_at: nil>, @errors={"commenter"=>["can't be blank",
+>> p.errors
+=> #<ActiveRecord::Errors:0x23bcf0c @base=#<Post id: nil, name: nil,
+title: nil, content: "A new post", created_at: nil, updated_at: nil>,
+@errors={"name"=>["can't be blank"], "title"=>["can't be blank",
"is too short (minimum is 5 characters)"]}>
-------------------------------------------------------
-This code shows creating a new +Comment+ instance, attempting to save it and getting +false+ for a return value (indicating that the save failed), and inspecting the +errors+ of the comment.
+This code shows creating a new +Post+ instance, attempting to save it and getting +false+ for a return value (indicating that the save failed), and inspecting the +errors+ of the post.
TIP: Unlike the development web server, the console does not automatically load your code afresh for each line. If you make changes, type +reload!+ at the console prompt to load them.
@@ -818,6 +817,8 @@ You'll need to edit the +post.rb+ file to add the other side of the association:
[source, ruby]
-------------------------------------------------------
class Post < ActiveRecord::Base
+ validates_presence_of :name, :title
+ validates_length_of :title, :minimum => 5
has_many :comments
end
-------------------------------------------------------
@@ -930,6 +931,15 @@ end
You'll see a bit more complexity here than you did in the controller for posts. That's a side-effect of the nesting that you've set up; each request for a comment has to keep track of the post to which the comment is attached.
+In addition, the code takes advantage of some of the methods available for an association. For example, in the +new+ method, it calls
+
+[source, ruby]
+-------------------------------------------------------
+@comment = @post.comments.build
+-------------------------------------------------------
+
+This creates a new +Comment+ object _and_ sets up the +post_id+ field to have the +id+ from the specified +Post+ object in a single operation.
+
=== Building Views
Because you skipped scaffolding, you'll need to build views for comments "by hand." Invoking +script/generate controller+ will give you skeleton views, but they'll be devoid of actual content. Here's a first pass at fleshing out the comment views.
diff --git a/railties/doc/guides/routing/routing_outside_in.txt b/railties/doc/guides/routing/routing_outside_in.txt
index ff41bc0257..716c362c76 100644
--- a/railties/doc/guides/routing/routing_outside_in.txt
+++ b/railties/doc/guides/routing/routing_outside_in.txt
@@ -551,6 +551,33 @@ If you like, you can combine shallow nesting with the +:has_one+ and +:has_many+
-------------------------------------------------------
map.resources :publishers, :has_many => { :magazines => :photos }, :shallow => true
-------------------------------------------------------
+
+=== Route Generation from Arrays
+
+In addition to using the generated routing helpers, Rails can also generate RESTful routes from an array of parameters. For example, suppose you have a set of routes generated with these entries in routes.rb:
+
+[source, ruby]
+-------------------------------------------------------
+map.resources :magazines do |magazine|
+ magazine.resources :ads
+end
+-------------------------------------------------------
+
+Rails will generate helpers such as magazine_ad_path that you can use in building links:
+
+[source, ruby]
+-------------------------------------------------------
+<%= link_to "Ad details", magazine_ad_path(@magazine, @ad) %>
+-------------------------------------------------------
+
+Another way to refer to the same route is with an array of objects:
+
+[source, ruby]
+-------------------------------------------------------
+<%= link_to "Ad details", [@magazine, @ad] %>
+-------------------------------------------------------
+
+This format is especially useful when you might not know until runtime which of several types of object will be used in a particular link.
=== Namespaced Resources
diff --git a/railties/doc/guides/securing_rails_applications/security.txt b/railties/doc/guides/securing_rails_applications/security.txt
index aa1fcf4171..d068a22491 100644
--- a/railties/doc/guides/securing_rails_applications/security.txt
+++ b/railties/doc/guides/securing_rails_applications/security.txt
@@ -14,62 +14,64 @@ mail me at 42 {_et_} rorsecurity.info. After reading it, you should be familiar
== Introduction
-Web application frameworks are made to help developers building web applications. Some of them also help you securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem. It‘s nice to see all Rails applications I audited, had a good level of security.
+Web application frameworks are made to help developers building web applications. Some of them also help you with securing the web application. In fact one framework is not more secure than another: If you use it correctly, you will be able to build secure apps with many frameworks. Ruby on Rails has some clever helper methods, for example against SQL injection, so that this is hardly a problem. It‘s nice to see that all of the Rails applications I audited had a good level of security.
-In general there is no such thing as plug-n-play security. It depends on the people using it, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications).
+In general there is no such thing as plug-n-play security. Security depends on the people using the framework, and sometimes on the development method. And it depends on all layers of a web application environment: The back-end storage, the web server and the web application itself (and possibly other layers or applications).
The Gartner Group however estimates that 75% of attacks are at the web application layer, and found out "that out of 300 audited sites, 97% are vulnerable to attack". This is because web applications are relatively easy to attack, as they are simple to understand and manipulate, even by the lay person.
-The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, we have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
+The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the Additional Resources chapter). I do it manually because that‘s how you find the nasty logical security problems.
== Sessions
+A good place to start looking at security is with sessions, which can be vulnerable to particular attacks.
+
=== What are sessions?
--- _HTTP is a stateless protocol, sessions make it stateful._
+-- _HTTP is a stateless protocol Sessions make it stateful._
Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request.
-Rails will create a new session automatically if a new user accesses the application. It will load an existing one, if the user has already used the application.
+Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application.
-A session usually consists of a hash of values and a session id, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session id. And the other way round, the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method:
+A session usually consists of a hash of values and a session id, usually a 32-character string, to identify the hash. Every cookie sent to the client's browser includes the session id. And the other way round: the browser will send it to the server on every request from the client. In Rails you can save and retrieve values using the session method:
-....................................
+[source, ruby]
+----------------------------------------------------------------------------
session[:user_id] = @current_user.id
User.find(session[:user_id])
-....................................
+----------------------------------------------------------------------------
=== Session id
--- _The session id is a 32 bytes long MD5 hash value._
-
-It consists of the hash value of a random string. The random string is the current time, a random number between 0 and 1, the process id number of the Ruby interpreter (also basically a random number) and a constant string. Currently it is not feasible to brute-force Rails' session ids. To date MD5 is uncompromised, but there have been collisions, so it is theoretically possible to create another input text with the same hash value. But this has no security impact to date.
+-- _The session id is a 32 byte long MD5 hash value._
+A session id consists of the hash value of a random string. The random string is the current time, a random number between 0 and 1, the process id number of the Ruby interpreter (also basically a random number) and a constant string. Currently it is not feasible to brute-force Rails' session ids. To date MD5 is uncompromised, but there have been collisions, so it is theoretically possible to create another input text with the same hash value. But this has had no security impact to date.
=== Session hijacking
-- _Stealing a user's session id lets an attacker use the web application in the victim's name._
-Many web applications have an authentication system: A user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session id in the cookie identifies the session.
+Many web applications have an authentication system: a user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session id in the cookie identifies the session.
Hence, the cookie serves as temporary authentication for the web application. Everyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures:
-- Sniff the cookie in an insecure network. Wireless LAN is an example for such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. One more reason not to work from a coffee store. For the web application builder this means to [,#fffcdb]#provide a secure connection over SSL#.
+- Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to [,#fffcdb]#provide a secure connection over SSL#.
- Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a [,#fffcdb]#log-out button# in the web application, and [,#fffcdb]#make it prominent#.
-- Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. Read more about XSS later.
+- Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read more about XSS later.
- Instead of stealing a cookie unknown to the attacker, he fixes a user's session identifier (in the cookie) known to him. Read more about this so-called session fixation later.
-The main objective of attackers is to make money. The underground prices for stolen bank login accounts range from $10-$1000 (depending on the available amount of funds), $0.40-$20 for credit card numbers, $1-$8 for online auction site accounts and $4-$30 for email passwords, according to the http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf[Symantec Global Internet Security Threat Report].
+The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10-$1000 (depending on the available amount of funds), $0.40-$20 for credit card numbers, $1-$8 for online auction site accounts and $4-$30 for email passwords, according to the http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf[Symantec Global Internet Security Threat Report].
=== Session guidelines
-- _Here are some general guidelines on sessions._
-- [,#fffcdb]#Do not store large objects in a session#. Instead you should store them in the database and save its id in the session. This will eliminate synchronization headaches and it won't fill up your session storage space (depending on what session storage you chose, see below).
+- [,#fffcdb]#Do not store large objects in a session#. Instead you should store them in the database and save their id in the session. This will eliminate synchronization headaches and it won't fill up your session storage space (depending on what session storage you chose, see below).
This will also be a good idea, if you modify the structure of an object and old versions of it are still in some user's cookies. With server-side session storages you can clear out the sessions, but with client-side storages, this is hard to mitigate.
- [,#fffcdb]#Critical data should not be stored in session#. If the user clears his cookies or closes the browser, they will be lost. And with a client-side session storage, the user can read the data.
@@ -77,7 +79,7 @@ This will also be a good idea, if you modify the structure of an object and old
=== Session storage
--- _Rails provides several storage mechanisms for the session hashes, the most important are ActiveRecordStore and CookieStore._
+-- _Rails provides several storage mechanisms for the session hashes. The most important are ActiveRecordStore and CookieStore._
There are a number of session storages, i.e. where Rails saves the session hash and session id. Most real-live applications choose ActiveRecordStore (or one of its derivatives) over file storage due to performance and maintenance reasons. ActiveRecordStore keeps the session id and hash in a database table and saves and retrieves the hash on every request.
@@ -87,7 +89,7 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves
- The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, [,#fffcdb]#you don't want to store any secrets here#. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie.
-That means the security of this storage depends on this secret (and of the digest algorithm, which defaults to SHA512, and has not been compromised, yet). So [,#fffcdb]#don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters#. Put the secret in your environment.rb:
+That means the security of this storage depends on this secret (and of the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So [,#fffcdb]#don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters#. Put the secret in your environment.rb:
....................................
config.action_controller.session = {
@@ -98,10 +100,9 @@ config.action_controller.session = {
There are, however, derivatives of CookieStore which encrypt the session hash, so the client cannot see it.
-
=== Replay attacks for CookieStore sessions
--- _Another sort of attacks you have to be aware of when using CookieStore, are replay attacks._
+-- _Another sort of attack you have to be aware of when using CookieStore is the replay attack._
It works like this:
@@ -111,20 +112,19 @@ It works like this:
- The dark side of the user forces him to take the cookie from the first step (which he copied) and replace the current cookie in the browser.
- The user has his credit back.
-Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore avoiding accessing the database.
+Including a nonce (a random value) in the session solves replay attacks. A nonce is valid only once, and the server has to keep track of all the valid nonces. It gets even more complicated if you have several application servers (mongrels). Storing nonces in a database table would defeat the entire purpose of CookieStore (avoiding accessing the database).
The best [,#fffcdb]#solution against it is not to store this kind of data in a session, but in the database#. In this case store the credit in the database and the logged_in_user_id in the session.
-
=== Session fixation
--- _Apart from stealing a user's session id, the attacker may fixate a session id known to him. This is called session fixation._
+-- _Apart from stealing a user's session id, the attacker may fix a session id known to him. This is called session fixation._
image::images/session_fixation.png[Session fixation]
This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works:
-. The attacker creates a valid session id: He loads the login page of the web application where he wants to fixate the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image).
+. The attacker creates a valid session id: He loads the login page of the web application where he wants to fix the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image).
. He possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore he accesses the web application from time to time in order to keep the session alive.
@@ -137,16 +137,16 @@ Read more about XSS and injection later on.
. From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn't notice the attack.
-
=== Session fixation – Countermeasures
-- _One line of code will protect you from session fixation._
The most effective countermeasure is to [,#fffcdb]#issue a new session identifier# and declare the old one invalid after a successful login. That way, an attacker cannot use the fixed session identifier. This is a good countermeasure against session hijacking, as well. Here is how to create a new session in Rails:
-.............
+[source, ruby]
+----------------------------------------------------------------------------
reset_session
-.............
+----------------------------------------------------------------------------
If you use the popular RestfulAuthentication plugin for user management, add reset_session to the SessionsController#create action. Note that this removes any value from the session, [,#fffcdb]#you have to transfer them to the new session#.
@@ -154,12 +154,12 @@ Another countermeasure is to [,#fffcdb]#save user-specific properties in the ses
=== Session expiry
--- _Never expiring sessions extend the time-frame for attacks such as cross-site reference forgery (CSRF), session hijacking and session fixation._
+-- _Sessions that never expire extend the time-frame for attacks such as cross-site reference forgery (CSRF), session hijacking and session fixation._
One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to [,#fffcdb]#expire sessions in a database table#. Call Session.sweep("20m") to expire sessions that were used longer than 20 minutes ago.
-
-..................................
+[source, ruby]
+----------------------------------------------------------------------------
class Session < ActiveRecord::Base
def self.sweep(time_ago = nil)

 time = case time_ago
@@ -171,12 +171,14 @@ class Session < ActiveRecord::Base

 self.delete_all "updated_at < '#{time.to_s(:db)}'"

 end

end
-..................................
-
-The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions created a long time ago. Use this line in the sweep method above:
+----------------------------------------------------------------------------
-+self.delete_all "updated_at < '#{time.to_s(:db)}' OR created_at < '#{2.days.ago.to_s(:db)}'"+
+The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above:
+[source, ruby]
+----------------------------------------------------------------------------
+self.delete_all "updated_at < '#{time.to_s(:db)}' OR created_at < '#{2.days.ago.to_s(:db)}'"
+----------------------------------------------------------------------------
== Cross-Site Reference Forgery (CSRF)
-- _This attack method works by including malicious code or a link in a page that accesses a web application that the user is believed to have authenticated. If the session for that web application has not timed out, an attacker may execute unauthorized commands._
@@ -190,11 +192,11 @@ In the session chapter you have learned that most Rails applications use cookie-
- Bob's session at www.webapp.com is still alive, because he didn't log out a few minutes ago.
- By viewing the post, the browser finds an image tag. It tries to load the suspected image from www.webapp.com. As explained before, it will also send along the cookie with the valid session id.
- The web application at www.webapp.com verifies the user information in the corresponding session hash and destroys the project with the ID 1. It then returns a result page which is an unexpected result for the browser, so it will not display the image.
-- Bob doesn't notice the attack, only a few days later he finds out that project number one is gone.
+- Bob doesn't notice the attack -- but a few days later he finds out that project number one is gone.
It is important to notice that the actual crafted image or link doesn't necessarily have to be situated in the web application's domain, it can be anywhere – in a forum, blog post or email.
-CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures), less than 0.1% in 2006, but it really is a 'sleeping giant' [Grossman]. This is in stark contrast to the results in my (and others) security contract work – [,#fffcdb]#CSRF is an important security issue#.
+CSRF appears very rarely in CVE (Common Vulnerabilities and Exposures) -- less than 0.1% in 2006 -- but it really is a 'sleeping giant' [Grossman]. This is in stark contrast to the results in my (and others) security contract work – [,#fffcdb]#CSRF is an important security issue#.
=== CSRF Countermeasures
@@ -212,9 +214,9 @@ The HTTP protocol basically provides two main types of requests - GET and POST (
- The interaction [,#fffcdb]#changes the state# of the resource in a way that the user would perceive (e.g., a subscription to a service), or
- The user is [,#fffcdb]#held accountable for the results# of the interaction.
-If your web application is RESTful, you might be used to additional HTTP verbs, such as PUT or DELETE. Most of today‘s web browsers, however do not support them - only GET and POST. Rails uses a hidden _method field to handle this barrier.
+If your web application is RESTful, you might be used to additional HTTP verbs, such as PUT or DELETE. Most of today‘s web browsers, however do not support them - only GET and POST. Rails uses a hidden +_method+ field to handle this barrier.
-[,#fffcdb]#The verify method in a controller can make sure that specific actions may not be used over GET#. Here is an example to verify use of the transfer action over POST, otherwise it redirects to the list action.
+[,#fffcdb]#The verify method in a controller can make sure that specific actions may not be used over GET#. Here is an example to verify the use of the transfer action over POST. If the action comes in using any other verb, it redirects to the list action.
.................................................................................
verify :method => :post, :only => [:transfer], :redirect_to => {:action => :list}
@@ -224,7 +226,8 @@ With this precaution, the attack from above will not work, because the browser s
But this was only the first step, because [,#fffcdb]#POST requests can be send automatically, too#. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request.
-............................................
+[source, html]
+----------------------------------------------------------------------------
<a href="http://www.harmless.com/" onclick="
var f = document.createElement('form');
f.style.display = 'none';
@@ -233,7 +236,7 @@ But this was only the first step, because [,#fffcdb]#POST requests can be send a
f.action = 'http://www.example.com/account/destroy';
f.submit();
return false;">To the harmless survey</a>
-............................................
+----------------------------------------------------------------------------
Or the attacker places the code into the onmouseover event handler of an image:
@@ -243,22 +246,26 @@ There are many other possibilities, including Ajax to attack the victim in the b
+protect_from_forgery :secret => "123456789012345678901234567890..."+
-This will automatically include a security token, calculated of the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. It will raise an ActionController::InvalidAuthenticityToken error, if the security token doesn't match what was expected.
+This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. It will raise an ActionController::InvalidAuthenticityToken error, if the security token doesn't match what was expected.
Note that [,#fffcdb]#cross-site scripting (XSS) vulnerabilities bypass all CSRF protections#. XSS gives the attacker access to all elements on a page, so he can read the CSRF security token from a form or directly submit the form. Read more about XSS later.
== Redirection and Files
+
+Another class of security vulnerabilities surrounds the use of redirection and files in web applications.
+
=== Redirection
-- _Redirection in a web application is an underestimated cracker tool: Not only can the attacker forward the user to a trap web site, he may also create a self-contained attack._
Whenever the user is allowed to pass (parts of) the URL for redirection, it is possibly vulnerable. The most obvious attack would be to redirect users to a fake web application which looks and feels exactly as the original one. This so-called phishing attack works by sending an unsuspicious link in an email to the users, injecting the link by XSS in the web application or putting the link into an external site. It is unsuspicious, because the link starts with the URL to the web application and the URL to the malicious site is hidden in the redirection parameter: http://www.example.com/site/redirect?to= www.attacker.com. Here is an example of a legacy action:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
def legacy
redirect_to(params.update(:action=>'main'))
end
-..........
+----------------------------------------------------------------------------
This will redirect the user to the main action if he tried to access a legacy action. The intention was to preserve the URL parameters to the legacy action and pass them to the main action. However, it can exploited by an attacker if he includes a host key in the URL:
@@ -267,20 +274,23 @@ This will redirect the user to the main action if he tried to access a legacy ac
If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to [,#fffcdb]#include only the expected parameters in a legacy action# (again a whitelist approach, as opposed to removing unexpected parameters). [,#fffcdb]#And if you redirect to an URL, check it with a whitelist or a regular expression#.
==== Self-contained XSS
-Another redirection and self-contained XSS attack works in Firefox and Opera by the use of the data protocol. This protocol displays its contents directly in the browser and can be anything from HTML, JavaScript to entire images:
+
+Another redirection and self-contained XSS attack works in Firefox and Opera by the use of the data protocol. This protocol displays its contents directly in the browser and can be anything from HTML or JavaScript to entire images:
+data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K+
This example is a Base64 encoded JavaScript which displays a simple message box. In a redirection URL, an attacker could redirect to this URL with the malicious code in it. As a countermeasure, [,#fffcdb]#do not allow the user to supply (parts of) the URL to be redirected to#.
=== File uploads
--- _Make sure file uploads don't overwrite important files and process media files asynchronously._
-Many web applications allow users to upload files. [,#fffcdb]#File names, which the user may choose (partly), should always be filtered# as an attacker could use a malicious file name to overwrite any file on the server. If you store file uploads at /var/www/uploads, and the user enters a file name like “../../../etc/passwd”, it will overwrite an important file. Of course, the Ruby interpreter would need the appropriate permissions to do so – one more reason to run web servers, database servers and other programs as a less privileged Unix user.
+-- _Make sure file uploads don't overwrite important files, and process media files asynchronously._
+
+Many web applications allow users to upload files. [,#fffcdb]#File names, which the user may choose (partly), should always be filtered# as an attacker could use a malicious file name to overwrite any file on the server. If you store file uploads at /var/www/uploads, and the user enters a file name like “../../../etc/passwd”, it may overwrite an important file. Of course, the Ruby interpreter would need the appropriate permissions to do so – one more reason to run web servers, database servers and other programs as a less privileged Unix user.
-When filtering user input file names, [,#fffcdb]#don't try to remove malicious parts#. Think of a situation where the web application removes all “../” in a file name and an attacker uses a string such as “….//” - the result will be “../”. It is best to use a whitelist approach, which [,#fffcdb]#checks for the validity of a file name with a set of accepted characters#. This is opposed to a blacklist approach which removes not allowed characters. In case it isn't a valid file name, reject it (or replace not accepted characters), but don't remove them. Here is the file name sanitizer from the http://github.com/technoweenie/attachment_fu/tree/master[attachment_fu plugin]:
+When filtering user input file names, [,#fffcdb]#don't try to remove malicious parts#. Think of a situation where the web application removes all “../” in a file name and an attacker uses a string such as “....//” - the result will be “../”. It is best to use a whitelist approach, which [,#fffcdb]#checks for the validity of a file name with a set of accepted characters#. This is opposed to a blacklist approach which attempts to remove not allowed characters. In case it isn't a valid file name, reject it (or replace not accepted characters), but don't remove them. Here is the file name sanitizer from the http://github.com/technoweenie/attachment_fu/tree/master[attachment_fu plugin]:
-...............................
+[source, ruby]
+----------------------------------------------------------------------------
def sanitize_filename(filename)
returning filename.strip do |name|
# NOTE: File.basename doesn't work right with Windows paths on Unix
@@ -291,13 +301,14 @@ def sanitize_filename(filename)
name.gsub! /[^\w\.\-]/, '_'
end
end
-...............................
+----------------------------------------------------------------------------
A significant disadvantage of synchronous processing of file uploads (as the attachment_fu plugin may do with images), is its [,#fffcdb]#vulnerability to denial-of-service attacks#. An attacker can synchronously start image file uploads from many computers which increases the server load and may eventually crash or stall the server.
The solution to this, is best to [,#fffcdb]#process media files asynchronously#: Save the media file and schedule a processing request in the database. A second process will handle the processing of the file in the background.
=== Executable code in file uploads
+
-- _Source code in uploaded files may be executed when placed in specific directories. Do not place file uploads in Rails /public directory if it is Apache's home directory._
The popular Apache web server has an option called DocumentRoot. This is the home directory of the web site, everything in this directory tree will be served by the web server. If there are files with a certain file name extension, the code in it will be executed when requested (might require some options to be set). Examples for this are PHP and CGI files. Now think of a situation where an attacker uploads a file “file.cgi” with code in it, which will be executed when someone downloads the file.
@@ -305,40 +316,42 @@ The popular Apache web server has an option called DocumentRoot. This is the hom
[,#fffcdb]#If your Apache DocumentRoot points to Rails' /public directory, do not put file uploads in it#, store files at least one level downwards.
=== File downloads
+
-- _Make sure users cannot download arbitrary files._
Just as you have to filter file names for uploads, you have to do so for downloads. The send_file() method sends files from the server to the client. If you use a file name, that the user entered, without filtering, any file can be downloaded:
-...........................
+[source, ruby]
+----------------------------------------------------------------------------
send_file('/var/www/uploads/' + params[:filename])
-...........................
+----------------------------------------------------------------------------
Simply pass a file name like “../../../etc/passwd” to download the server's login information. A simple solution against this, is to [,#fffcdb]#check that the requested file is in the expected directory#:
-...........................
+[source, ruby]
+----------------------------------------------------------------------------
basename = File.expand_path(File.join(File.dirname(__FILE__), '../../files'))
filename = File.expand_path(File.join(basename, @file.public_filename))
raise if basename =!
File.expand_path(File.join(File.dirname(filename), '../../../'))
send_file filename, :disposition => 'inline'
-...........................
+----------------------------------------------------------------------------
Another (additional) approach is to store the file names in the database and name the files on the disk after the ids in the database. This is also a good approach to avoid possible code in an uploaded file to be executed. The attachment_fu plugin does this in a similar way.
-
-
== Intranet and Admin security
+
-- _Intranet and administration interfaces are popular attack targets, because they allow privileged access. Although this would require several extra-security measures, the opposite is the case in the real world._
In 2007 there was the first tailor-made http://www.symantec.com/enterprise/security_response/weblog/2007/08/a_monster_trojan.html[Trojan] which stole information from an Intranet, namely the "Monster for employers" web site of Monster.com, an online recruitment web application. Tailor-made Trojans are very rare, so far, and the risk is quite low, but it is certainly a possibility and an example of how the security of the client host is important, too. However, the highest threat to Intranet and Admin applications are XSS and CSRF.

*XSS* If your application re-displays malicious user input from the extranet, the application will be vulnerable to XSS. User names, comments, spam reports, order addresses are just a few uncommon examples, where there can be XSS.
-Already one single place in the admin interface or Intranet, where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.
+Having one single place in the admin interface or Intranet where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.
Refer to the Injection section for countermeasures against XSS. It is [,#fffcdb]#recommended to use the SafeErb plugin# also in an Intranet or administration interface.
-*CSRF* Cross-Site Reference Forgery (CSRF) is a giant attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above, how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.
+*CSRF* Cross-Site Reference Forgery (CSRF) is a giant attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.
A real-world example is a http://www.symantec.com/enterprise/security_response/weblog/2008/01/driveby_pharming_in_the_
wild.html[router reconfiguration by CSRF]. The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had his credentials stolen.
@@ -349,7 +362,8 @@ Another popular attack is to spam your web application, your blog or forum to pr
For [,#fffcdb]#countermeasures against CSRF in administration interfaces and Intranet applications, refer to the countermeasures in the CSRF section#.
=== Additional precautions
-The common admin interface is like this: It's located at www.example.com/admin, may be accessed only if the admin flag is set in the User model, re-displays user input and allows the admin to delete/add/edit whatever data desired. Here are some thoughts about this:
+
+The common admin interface works like this: it's located at www.example.com/admin, may be accessed only if the admin flag is set in the User model, re-displays user input and allows the admin to delete/add/edit whatever data desired. Here are some thoughts about this:
- It is very important to [,#fffcdb]#think about the worst case#: What if someone really got hold of my cookie or user credentials. You could [,#fffcdb]#introduce roles# for the admin interface to limit the possibilities of the attacker. Or how about [,#fffcdb]#special login credentials# for the admin interface, other than the ones used for the public part of the application. Or a [,#fffcdb]#special password for very serious actions#?
@@ -357,18 +371,19 @@ The common admin interface is like this: It's located at www.example.com/admin,
- [,#fffcdb]#Put the admin interface to a special sub-domain# such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa.
-
== Mass assignment
+
-- _Without any precautions Model.new(params[:model]) allows attackers to set any database column's value._
The mass-assignment feature may become a problem, as it allows an attacker to set any model's attribute by manipulating the hash passed to a model's new() method:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
def signup
params[:user] #=> {:name => “ow3ned”, :admin => true}
@user = User.new(params[:user])
end
-..........
+----------------------------------------------------------------------------
Mass-assignment saves you much work, because you don't have to set each value individually. Simply pass a hash to the new() method, or assign attributes=(attributes) a hash value, to set the model's attributes to the values in the hash. The problem is that it is often used in conjunction with the parameters (params) hash available in the controller, which may be manipulated by an attacker. He may do so by changing the URL like this:
@@ -378,37 +393,42 @@ http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1
This will set the following parameters in the controller:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
params[:user] #=> {:name => “ow3ned”, :admin => true}
-..........
+----------------------------------------------------------------------------
So if you create a new user using mass-assignment, it may be too easy to become an administrator.
-
=== Countermeasures
+
To avoid this, Rails provides two class methods in your ActiveRecord class to control access to your attributes. The attr_protected method takes a list of attributes that will not be accessible for mass-assignment. For example:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
attr_protected :admin
-..........
+----------------------------------------------------------------------------
A much better way, because it follows the whitelist-principle, is the [,#fffcdb]#attr_accessible method#. It is the exact opposite of attr_protected, because [,#fffcdb]#it takes a list of attributes that will be accessible#. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
attr_accessible :name
-..........
+----------------------------------------------------------------------------
If you want to set a protected attribute, you will to have to assign it individually:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
params[:user] #=> {:name => "ow3ned", :admin => true}
@user = User.new(params[:user])
@user.admin #=> false # not mass-assigned
@user.admin = true
@user.admin #=> true
-..........
+----------------------------------------------------------------------------
== User management
+
-- _Almost every web application has to deal with authorization and authentication. Instead of rolling your own, it is advisable to use common plug-ins. But keep them up-to-date, too. A few additional precautions can make your application even more secure._
There are some authorization and authentication plug-ins for Rails available. A good one saves only encrypted passwords, not plain-text passwords. The most popular plug-in is [,#fffcdb]#restful_authentication# which protects from session fixation, too. However, earlier versions allowed you to login without user name and password in certain circumstances.
@@ -422,9 +442,10 @@ http://localhost:3006/user/activate?id=
This is possible because on some servers, this way the parameter id, as in params[:id], would be nil. However, here is the finder from the activation action:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
User.find_by_activation_code(params[:id])
-..........
+----------------------------------------------------------------------------
If the parameter was nil, the resulting SQL query will be
@@ -434,11 +455,11 @@ SELECT * FROM users WHERE (users.`activation_code` IS NULL) LIMIT 1
And thus it found the first user in the database, returned it and logged him in. You can find out more about it in http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/[my blog post]. [,#fffcdb]#It is advisable to update your plug-ins from time to time#. Moreover, you can review your application to find more flaws like this.
-
=== Brute-forcing accounts
+
-- _Brute-force attacks on accounts are trial and error attacks on the login credentials. Fend them off with more generic error messages and possibly require to enter a CAPTCHA._
-A list of user name's for your web application may be misused to brute-force the corresponding passwords, because most people don't use sophisticated passwords. Most passwords are a combination of dictionary words and possibly numbers. So armed with a list of user name's and a dictionary, an automatic program may find the correct password in a matter of minutes.
+A list of user names for your web application may be misused to brute-force the corresponding passwords, because most people don't use sophisticated passwords. Most passwords are a combination of dictionary words and possibly numbers. So armed with a list of user name's and a dictionary, an automatic program may find the correct password in a matter of minutes.
Because of this, most web applications will display a generic error message “user name or password not correct”, if one of these are not correct. If it said “the user name you entered has not been found”, an attacker could automatically compile a list of user names.
@@ -446,20 +467,24 @@ However, what most web application designers neglect, are the forgot-password pa
In order to mitigate such attacks, [,#fffcdb]#display a generic error message on forgot-password pages, too#. Moreover, you can [,#fffcdb]#require to enter a CAPTCHA after a number of failed logins from a certain IP address#. Note, however, that this is not a bullet-proof solution against automatic programs, because these programs may change their IP address exactly as often. However, it raises the barrier of an attack.
-
=== Account hijacking
+
-- _Many web applications make it easy to hijack user accounts. Why not be different and make it more difficult?_
==== Passwords
+
Think of a situation where an attacker has stolen a user's session cookie and thus may co-use the application. If it is easy to change the password, the attacker will hijack the account with a few clicks. Or if the change-password form is vulnerable to CSRF, the attacker will be able to change the victim's password by luring him to a web page where there is a crafted IMG-tag which does the CSRF. As a countermeasure, [,#fffcdb]#make change-password forms safe against CSRF#, of course. And [,#fffcdb]#require the user to enter the old password when changing it#.
==== E-Mail
+
However, the attacker may also take over the account by changing the e-mail address. After he changed it, he will go to the forgotten-password page and the (possibly new) password will be mailed to the attacker's e-mail address. As a countermeasure [,#fffcdb]#require the user to enter the password when changing the e-mail address, too#.
==== Other
+
Depending on your web application, there may be more ways to hijack the user's account. In many cases CSRF and XSS will help to do so. For example, as in a CSRF vulnerability in http://www.gnucitizen.org/blog/google-gmail-e-mail-hijack-technique/[Google Mail]. In this proof-of-concept attack, the victim would have been lured to a web site controlled by the attacker. On that site is a crafted IMG-tag which results in a HTTP GET request that changes the filter settings of Google Mail. If the victim was logged in to Google Mail, the attacker would change the filters to forward all e-mails to his e-mail address. This is nearly as harmful as hijacking the entire account. As a countermeasure, [,#fffcdb]#review your application logic and eliminate all XSS and CSRF vulnerabilities#.
=== CAPTCHAs
+
-- _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not to ask a user to proof that he is human, but reveal that a robot is a robot._
But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is http://recaptcha.net/[reCAPTCHA] which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. http://ambethia.com/recaptcha/[ReCAPTCHA] is also a Rails plug-in with the same name as the API.
@@ -486,19 +511,21 @@ You can find more sophisticated negative CAPTCHAs in Ned Batchelder's http://ned
Note that this protects you only from automatic bots, targeted tailor-made bots cannot be stopped by this. So negative CAPTCHAs might not be good to protect login forms.
=== Logging
+
-- _Tell Rails not to put passwords in the log files._
By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers etcetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can [,#fffcdb]#filter certain request parameters from your log files# by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log.
-...........
+[source, ruby]
+----------------------------------------------------------------------------
filter_parameter_logging :password
-...........
-
+----------------------------------------------------------------------------
=== Good passwords
+
-- _Do you find it hard to remember all your passwords? Don't write them down, but use the initial letters of each word in an easy to remember sentence._
-Bruce Schneier, a security technologist, http://www.schneier.com/blog/archives/2006/12/realworld_passw.html[has analysed] 34,000 real-world user names and passwords from the MySpace phishing attack mentioned earlier. It turns out, that most of the passwords are quite easy to crack. The 20 most common passwords are:
+Bruce Schneier, a security technologist, http://www.schneier.com/blog/archives/2006/12/realworld_passw.html[has analysed] 34,000 real-world user names and passwords from the MySpace phishing attack mentioned earlier. It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:
password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1 and monkey.
@@ -507,15 +534,17 @@ It is interesting that only 4% of these passwords were dictionary words and the
A good password is a long alphanumeric combination of mixed cases. As this is quite hard to remember, it is advisable to enter only the [,#fffcdb]#first letters of a sentence that you can easily remember#. For example "The quick brown fox jumps over the lazy dog" will be "Tqbfjotld". Note that this is just an example, you should not use well known phrases like these, as they might appear in cracker dictionaries, too.
=== Regular expressions
+
-- _A common pitfall in Ruby's regular expressions is to match the string's beginning and end by ^ and $, instead of \A and \z._
-Ruby uses a slightly different approach to match the end and the beginning of a string. That is why even many Ruby and Rails books make this wrong. So how is this a security threat? Imagine you have a File model and you validate the file name by a regular expression like this:
+Ruby uses a slightly different approach than many other languages to match the end and the beginning of a string. That is why even many Ruby and Rails books make this wrong. So how is this a security threat? Imagine you have a File model and you validate the file name by a regular expression like this:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
class File < ActiveRecord::Base
validates_format_of :name, :with => /^[\w\.\-\+]+$/
end
-..........
+----------------------------------------------------------------------------
This means, upon saving, the model will validate the file name to consist only of alphanumeric characters, dots, + and -. And the programmer added \^ and $ so that file name will contain these characters from the beginning to the end of the string. However, [,#fffcdb]#in Ruby ^ and $ matches the *line* beginning and line end#. And thus a file name like this passes the filter without problems:
@@ -525,37 +554,43 @@ file.txt%0A<script>alert('hello')</script>
Whereas %0A is a line feed in URL encoding, so Rails automatically converts it to "file.txt\n<script>alert('hello')</script>". This file name passes the filter because the regular expression matches – up to the line end, the rest does not matter. The correct expression should read:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
/\A[\w\.\-\+]+\z/
-..........
+[source, ruby]
+----------------------------------------------------------------------------
=== Privilege escalation
+
-- _Changing a single parameter may give the user unauthorized access. Remember that every parameter may be changed, no matter how much you hide or obfuscate it._
The most common parameter that a user might tamper with, is the id parameter, as in +http://www.domain.com/project/1+, whereas 1 is the id. It will be available in params[:id] in the controller. There, you will most likely do something like this:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
@project = Project.find(params[:id])
-..........
+----------------------------------------------------------------------------
This is alright for some web applications, but certainly not if the user is not authorized to view all projects. If the user changes the id to 42, and he is not allowed to see that information, he will have access to it anyway. Instead, [,#fffcdb]#query the user's access rights, too#:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
@project = @current_user.projects.find(params[:id])
-..........
+----------------------------------------------------------------------------
Depending on your web application, there will be many more parameters the user can tamper with. As a rule of thumb, [,#fffcdb]#no user input data is secure, until proven otherwise, and every parameter from the user is potentially manipulated#.
Don‘t be fooled by security by obfuscation and JavaScript security. The Web Developer Toolbar for Mozilla Firefox lets you review and change every form's hidden fields. [,#fffcdb]#JavaScript can be used to validate user input data, but certainly not to prevent attackers from sending malicious requests with unexpected values#. The Live Http Headers plugin for Mozilla Firefox logs every request and may repeat and change them. That is an easy way to bypass any JavaScript validations. And there are even client-side proxies that allow you to intercept any request and response from and to the Internet.
-
== Injection
+
-- _Injection is a class of attacks that introduce malicious code or parameters into a web application in order to run it within its security context. Prominent examples of injection are cross-site scripting (XSS) and SQL injection._
Injection is very tricky, because the same code or parameter can be malicious in one context, but totally harmless in another. A context can be a scripting, query or programming language, the shell or a Ruby/Rails method. The following sections will cover all important contexts where injection attacks may happen. The first section, however, covers an architectural decision in connection with Injection.
=== Whitelists versus Blacklists
--- _When sanitizing, protecting or verifying something, prefer to use whitelists._
+
+-- _When sanitizing, protecting or verifying something, whitelists over blacklists._
A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although, sometimes it is not possible to create a whitelist (in a SPAM filter, for example), [,#fffcdb]#prefer to use whitelist approaches#:
@@ -568,16 +603,18 @@ A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTM
Whitelists are also a good approach against the human factor of forgetting something in the blacklist.
-
=== SQL Injection
+
-- _Thanks to clever methods, this is hardly a problem in most Rails applications. However, this is a very devastating and common attack in web applications, so it is important to understand the problem._
==== Introduction
+
SQL injection attacks aim at influencing database queries by manipulating web application parameters. A popular goal of SQL injection attacks is to bypass authorization. Another goal is to carry out data manipulation or reading arbitrary data. Here is an example of how not to use user input data in a query:
-..........
+[source, ruby]
+----------------------------------------------------------------------------
Project.find(:all, :conditions => "name = '#{params[:name]}'")
-..........
+----------------------------------------------------------------------------
This could be in a search action and the user may enter a project's name that he wants to find. If a malicious user enters ' OR 1=1', the resulting SQL query will be:
@@ -588,11 +625,13 @@ SELECT * FROM projects WHERE name = '' OR 1 --'
The two dashes start a comment ignoring everything after it. So the query returns all records from the projects table including those blind to the user. This is because the condition is true for all records.
==== Bypassing authorization
+
Usually a web application includes access control. The user enters his login credentials, the web applications tries to find the matching record in the users table. The application grants access when it finds a record. However, an attacker may possibly bypass this check with SQL injection. The following shows a typical database query in Rails to find the first record in the users table which matches the login credentials parameters supplied by the user.
-.........
+[source, ruby]
+----------------------------------------------------------------------------
User.find(:first, "login = '#{params[:name]}' AND password = '#{params[:password]}'")
-.........
+----------------------------------------------------------------------------
If an attacker enters ' OR '1'='1 as the name, and ' OR '2'>'1 as the password, the resulting SQL query will be:
@@ -603,11 +642,13 @@ SELECT * FROM users WHERE login = '' OR '1'='1' AND password = '' OR '2'>'1' LIM
This will simply find the first record in the database, and grants access to this user.
==== Unauthorized reading
+
The UNION statement connects two SQL queries and returns the data in one set. An attacker can use it to read arbitrary data from the database. Let's take the example from above:
-............
+[source, ruby]
+----------------------------------------------------------------------------
Project.find(:all, :conditions => "name = '#{params[:name]}'")
-............
+----------------------------------------------------------------------------
And now let's inject another query using the UNION statement:
@@ -627,29 +668,34 @@ The result won't be a list of projects (because there is no project with an empt
Also, the second query renames some columns with the AS statement so that the web application displays the values from the user table. Be sure to update your Rails http://www.rorsecurity.info/2008/09/08/sql-injection-issue-in-limit-and-offset-parameter/[to at least 2.1.1].
==== Countermeasures
+
Ruby on Rails has a built in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. [,#fffcdb]#Using Model.find(id) or Model.find_by_some thing(something) automatically applies this countermeasure[,#fffcdb]#. But in SQL fragments, especially [,#fffcdb]#in conditions fragments (:conditions => "..."), the connection.execute() or Model.find_by_sql() methods, it has to be applied manually#.
Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this:
-.........
+[source, ruby]
+----------------------------------------------------------------------------
Model.find(:first, :conditions => ["login = ? AND password = ?", entered_user_name, entered_password])
-.........
+----------------------------------------------------------------------------
As you can see, the first part of the array is an SQL fragment with question marks. The sanitized versions of the variables in the second part of the array replace the question marks. Or you can pass a hash for the same result:
-.........
+[source, ruby]
+----------------------------------------------------------------------------
Model.find(:first, :conditions => {:login => entered_user_name, :password => entered_password})
-.........
+----------------------------------------------------------------------------
-The array or hash form is only available in model instances. You can try sanitize_sql() elsewhere. [,#fffcdb]#Make it a habit to think about the security consequences when using an external string in SQL#.
+The array or hash form is only available in model instances. You can try +sanitize_sql()+ elsewhere. [,#fffcdb]#Make it a habit to think about the security consequences when using an external string in SQL#.
=== Cross-Site Scripting (XSS)
+
-- _The most widespread, and one of the most devastating security vulnerabilities in web applications is XSS. This malicious attack injects client-side executable code. Rails provides helper methods to fend these attacks off._
==== Entry points
+
An entry point is a vulnerable URL and its parameters where an attacker can start an attack.
-The most common entry points are message posts, user comments, guest books, but also project titles, document names and search result pages - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter – obvious, hidden or internal. Remember that the user may intercept any traffic. Applications, such as the http://livehttpheaders.mozdev.org/[Live HTTP Headers Firefox plugin], or client-site proxies make it easy to change requests.
+The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter – obvious, hidden or internal. Remember that the user may intercept any traffic. Applications, such as the http://livehttpheaders.mozdev.org/[Live HTTP Headers Firefox plugin], or client-site proxies make it easy to change requests.
XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session; redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser.
@@ -658,6 +704,7 @@ During the second half of 2007, there were 88 vulnerabilities reported in Mozill
A relatively new, and unusual, form of entry points are banner advertisements. In earlier 2008, malicious code appeared in banner ads on popular sites, such as MySpace and Excite, according to http://blog.trendmicro.com/myspace-excite-and-blick-serve-up-malicious-banner-ads/[Trend Micro].
==== HTML/JavaScript Injection
+
The most common XSS language is of course the most popular client-side scripting language JavaScript, often in combination with HTML. [,#fffcdb]#Escaping user input is essential#.
Here is the most straightforward test to check for XSS:
@@ -674,6 +721,7 @@ This JavaScript code will simply display an alert box. The next examples do exac
..........
===== Cookie theft
+
These examples don't do any harm so far, so let's see how an attacker can steal the user's cookie (and thus hijack the user's session). In JavaScript you can use the document.cookie property to read and write the document's cookie. JavaScript enforces the same origin policy, that means a script from one domain cannot access cookies of another domain. The document.cookie property holds the cookie of the originating web server. However, you can read and write this property, if you embed the code directly in the HTML document (as it happens with XSS). Inject this anywhere in your web application to see your own cookie on the result page:
..........
@@ -695,6 +743,7 @@ GET http://www.attacker.com/_app_session=836c1c25278e5b321d6bea4f19cb57e2
You can mitigate these attacks (in the obvious way) by adding the http://dev.rubyonrails.org/ticket/8895[httpOnly] flag to cookies, so that document.cookie may not be read by JavaScript. Http only cookies can be used from IE v6.SP1, Firefox v2.0.0.5 and Opera 9.5. Safari is still considering, it ignores the option. But other, older browsers (such as WebTV and IE 5.5 on Mac) can actually cause the page to fail to load. Be warned that cookies http://ha.ckers.org/blog/20070719/firefox-implements-httponly-and-is-vulnerable-to-xmlhttprequest/[will still be visible using Ajax], though.
===== Defacement
+
With web page defacement an attacker can do a lot of things, for example, present false information or lure the victim on the attackers web site to steal the cookie, login credentials or other sensitive data. The most popular way is to include code from external sources by iframes:
..........
@@ -705,7 +754,7 @@ This loads arbitrary HTML and/or JavaScript from an external source and embeds i
A more specialized attack could overlap the entire web site or display a login form, which looks the same as the site's original, but transmits the user name and password to the attackers site. Or it could use CSS and/or JavaScript to hide a legitimate link in the web application, and display another one at its place which redirects to a fake web site.
-Reflected injection attacks are those, where the payload is not stored to present it to the victim later on, but included in the URL. Especially search forms fail to escape the search string. The following link presented a page which stated that "George Bush appointed a 9 year old boy to be the chairperson...":
+Reflected injection attacks are those where the payload is not stored to present it to the victim later on, but included in the URL. Especially search forms fail to escape the search string. The following link presented a page which stated that "George Bush appointed a 9 year old boy to be the chairperson...":
..........
http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode=1-->
@@ -713,6 +762,7 @@ http://www.cbsnews.com/stories/2002/02/15/weather_local/main501644.shtml?zipcode
..........
===== Countermeasures
+
[,#fffcdb]#It is very important to filter malicious input, but it is also important to escape the output of the web application#.
Especially for XSS, it is important to do [,#fffcdb]#whitelist input filtering instead of blacklist#. Whitelist filtering states the values allowed as opposed to the values not allowed. Blacklists are never complete.
@@ -735,6 +785,7 @@ This allows only the given tags and does a good job, even against all kinds of t
As a second step, [,#fffcdb]#it is good practice to escape all output of the application#, especially when re-displaying user input, which hasn't been input filtered (as in the search form example earlier on). [,#fffcdb]#Use escapeHTML() (or its alias h()) method# to replace the HTML input characters &,",<,> by its uninterpreted representations in HTML (&amp;, &quot;, &lt; and &gt;). However, it can easily happen that the programmer forgets to use it, so [,#fffcdb]#it is recommended to use the http://safe-erb.rubyforge.org/svn/plugins/safe_erb/[SafeErb] plugin#. SafeErb reminds you to escape strings from external sources.
===== Obfuscation and Encoding Injection
+
Network traffic is mostly based on the limited Western alphabet, so new character encodings, such as Unicode, emerged, to transmit characters in other languages. But, this is also a threat to web applications, as malicious code can be hidden in different encodings that the web browser might be able to process, but the web application might not. Here is an attack vector in UTF-8 encoding:
............
@@ -744,8 +795,8 @@ Network traffic is mostly based on the limited Western alphabet, so new characte
This example pops up a message box. It will be recognized by the above sanitize() filter, though. A great tool to obfuscate and encode strings, and thus “get to know your enemy”, is the http://www.businessinfo.co.uk/labs/hackvertor/hackvertor.php[Hackvertor]. Rails‘ sanitize() method does a good job to fend off encoding attacks.
-
==== Examples from the underground
+
-- _In order to understand today's attacks on web applications, it's best to take a look at some real-world attack vectors._
The following is an excerpt from the http://www.symantec.com/security_response/writeup.jsp?docid=2006-061211-4111-99&tabid=1[Js.Yamanner@m] Yahoo! Mail http://groovin.net/stuff/yammer.txt[worm]. It appeared on June 11, 2006 and was the first webmail interface worm:
@@ -765,6 +816,7 @@ In December 2006, 34,000 actual user names and passwords were stolen in a http:/
The MySpace Samy worm will be discussed in the CSS Injection section.
=== CSS Injection
+
-- _CSS Injection is actually JavaScript injection, because some browsers (IE, some versions of Safari and others) allow JavaScript in CSS. Think twice about allowing custom CSS in your web application._
CSS Injection is explained best by a well-known worm, the http://namb.la/popular/tech.html[MySpace Samy worm]. This worm automatically sent a friend request to Samy (the attacker) simply by visiting his profile. Within several hours he had over 1 million friend requests, but it creates too much traffic on MySpace, so that the site goes offline. The following is a technical explanation of the worm.
@@ -800,9 +852,10 @@ In the end, he got a 4 KB worm, which he injected into his profile page.
The http://www.securiteam.com/securitynews/5LP051FHPE.html[moz-binding] CSS property proved to be another way to introduce JavaScript in CSS in Gecko-based browsers (Firefox, for example).
==== Countermeasures
-This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature, I am not aware of a whitelist CSS filter. [,#fffcdb]#If you want to allow custom colours or images, you can allow the user to choose them and build the CSS in the web application#. Use Rails' sanitize() method as a model for a whitelist CSS filter, if you really need one.
+This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature, I am not aware of a whitelist CSS filter. [,#fffcdb]#If you want to allow custom colours or images, you can allow the user to choose them and build the CSS in the web application#. Use Rails' +sanitize()+ method as a model for a whitelist CSS filter, if you really need one.
=== Textile Injection
+
-- _If you want to provide text formatting other than HTML (due to security), use a mark-up language which is converted to HTML on the server-side. http://whytheluckystiff.net/ruby/redcloth/[RedCloth] is such a language for Ruby, but without precautions, it is also vulnerable to XSS._
For example, RedCloth translates _test_ to <em>test<em>, which makes the text italic. However, up to the current version 3.0.4, it is still vulnerable to XSS:
@@ -827,24 +880,28 @@ However, this does not filter all HTML, a few tags will be left (by design), for
...........
==== Countermeasures
+
It is recommended to [,#fffcdb]#use RedCloth in combination with a whitelist input filter#, as described in the countermeasures against XSS.
=== Ajax Injection
+
-- _The same security precautions have to be taken for Ajax actions as for “normal” ones. There is at least one exception, however: The output has to be escaped in the controller already, if the action doesn't render a view._
If you use the http://dev.rubyonrails.org/browser/plugins/in_place_editing[in_place_editor plugin], or actions that return a string, rather than rendering a view, [,#fffcdb]#you have to escape the return value in the action#. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method.
=== RJS Injection
+
-- _Don't forget to escape in JavaScript (RJS) templates, too._
The RJS API generates blocks of JavaScript code based on Ruby code, thus allowing you to manipulate a view or parts of a view from the server side. [,#fffcdb]#If you allow user input in RJS templates, do escape it using escape_javascript() within JavaScript functions, and in HTML parts using h()#. Otherwise an attacker could execute arbitrary JavaScript.
=== Command Line Injection
+
-- _Use user-supplied command line parameters with caution._
If your application has to execute commands in the underlying operating system, there are several methods in Ruby: exec(command), syscall(command), system(command) and \`command`. You will have to be especially careful with these functions if the user may enter the whole command, or a part of it. This is because in most shells, you can execute another command at the end of the first one, concatenating them with a semicolon (;) or a vertical bar (|).
-A countermeasure is to [,#fffcdb]#use the system(command, parameters) method which passes command line parameters safely#.
+A countermeasure is to [,#fffcdb]#use the +system(command, parameters)+ method which passes command line parameters safely#.
..........
system("/bin/echo","hello; rm *")