Merged master into branch/clean it up/green test run

This commit is contained in:
Al Snow
2014-04-03 21:00:58 -04:00
88 changed files with 2528 additions and 857 deletions
+1
View File
@@ -2,4 +2,5 @@ language: ruby
rvm:
- "2.0.0"
before_script: rake db:setup
before_script: bundle exec rake db:setup
env: RAILSGOAT_MAINTAINER=true
+9 -4
View File
@@ -1,6 +1,10 @@
source 'https://rubygems.org'
gem 'rails', '3.2.15'
#don't upgrade
gem 'rails', '3.2.11'
gem 'rack', '1.4.0'
gem 'rack-ssl', '1.3.4'
ruby '2.0.0'
@@ -27,12 +31,12 @@ end
gem 'gauntlt'
gem 'simplecov', '0.8.0.pre2', :require => false, :group => :test
gem 'simplecov', :require => false, :group => :test
group :development, :test do
gem 'launchy'
gem 'capybara'
gem 'database_cleaner', '< 1.1.0'
gem 'database_cleaner'
gem 'poltergeist'
gem 'rspec-rails'
end
@@ -49,7 +53,6 @@ group :assets do
gem 'uglifier'
end
gem 'jquery-rails'
# To use ActiveModel has_secure_password
@@ -78,3 +81,5 @@ gem 'aruba'
gem 'execjs'
gem 'therubyracer'
# Add SMTP server support using MailCatcher
gem 'mailcatcher'
+156 -115
View File
@@ -1,60 +1,62 @@
GEM
remote: https://rubygems.org/
specs:
actionmailer (3.2.15)
actionpack (= 3.2.15)
mail (~> 2.5.4)
actionpack (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
actionmailer (3.2.11)
actionpack (= 3.2.11)
mail (~> 2.4.4)
actionpack (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.4)
rack (~> 1.4.5)
rack (~> 1.4.0)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.2.1)
activemodel (3.2.15)
activesupport (= 3.2.15)
activemodel (3.2.11)
activesupport (= 3.2.11)
builder (~> 3.0.0)
activerecord (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
activerecord (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activeresource (3.2.15)
activemodel (= 3.2.15)
activesupport (= 3.2.15)
activesupport (3.2.15)
i18n (~> 0.6, >= 0.6.4)
activeresource (3.2.11)
activemodel (= 3.2.11)
activesupport (= 3.2.11)
activesupport (3.2.11)
i18n (~> 0.6)
multi_json (~> 1.0)
addressable (2.3.5)
arel (3.0.2)
aruba (0.5.3)
addressable (2.3.6)
arel (3.0.3)
aruba (0.5.4)
childprocess (>= 0.3.6)
cucumber (>= 1.1.1)
rspec-expectations (>= 2.7.0)
bcrypt-ruby (3.1.2)
better_errors (1.0.1)
bcrypt (3.1.7)
bcrypt-ruby (3.1.5)
bcrypt (>= 3.1.3)
better_errors (1.1.0)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
brakeman (2.2.0)
brakeman (2.4.3)
erubis (~> 2.6)
fastercsv (~> 1.5)
haml (>= 3.0, < 5.0)
highline (~> 1.6.20)
multi_json (~> 1.2)
ruby2ruby (~> 2.0.5)
ruby_parser (~> 3.2.2)
ruby_parser (~> 3.4.0)
sass (~> 3.0)
slim (>= 1.3.6, < 3.0)
terminal-table (~> 1.4)
builder (3.0.4)
bundler-audit (0.2.0)
bundler-audit (0.3.0)
bundler (~> 1.2)
capybara (2.1.0)
capybara (2.2.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
@@ -62,9 +64,12 @@ GEM
xpath (~> 2.0)
celluloid (0.15.2)
timers (~> 1.1.0)
childprocess (0.3.9)
celluloid-io (0.15.0)
celluloid (>= 0.15.0)
nio4r (>= 0.5.0)
childprocess (0.5.2)
ffi (~> 1.0, >= 1.0.11)
cliver (0.2.2)
cliver (0.3.2)
coderay (1.1.0)
coffee-rails (3.2.2)
coffee-script (>= 2.2.0)
@@ -72,18 +77,19 @@ GEM
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.6.3)
cucumber (1.3.8)
coffee-script-source (1.7.0)
cucumber (1.3.11)
builder (>= 2.1.2)
diff-lcs (>= 1.1.3)
gherkin (~> 2.12.1)
gherkin (~> 2.12)
multi_json (>= 1.7.5, < 2.0)
multi_test (>= 0.0.2)
database_cleaner (1.0.1)
daemons (1.1.9)
database_cleaner (1.2.0)
debug_inspector (0.0.2)
diff-lcs (1.2.4)
docile (1.1.0)
dotenv (0.9.0)
diff-lcs (1.2.5)
docile (1.1.3)
dotenv (0.10.0)
em-websocket (0.5.0)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.5.3)
@@ -91,105 +97,124 @@ GEM
eventmachine (1.0.3)
execjs (2.0.2)
fastercsv (1.5.5)
ffi (1.9.0)
ffi (1.9.3)
foreman (0.63.0)
dotenv (>= 0.7)
thor (>= 0.13.6)
gauntlt (1.0.6)
aruba
cucumber
nokogiri (~> 1.5.0)
trollop
formatador (0.2.4)
gauntlt (1.0.8)
aruba (= 0.5.4)
cucumber (= 1.3.11)
nokogiri (= 1.6.1)
trollop (~> 2.0)
gherkin (2.12.2)
multi_json (~> 1.3)
guard (1.4.0)
listen (>= 0.4.2)
thor (>= 0.14.6)
guard (2.6.0)
formatador (>= 0.2.4)
listen (~> 2.7)
lumberjack (~> 1.0)
pry (>= 0.9.12)
thor (>= 0.18.1)
guard-brakeman (0.8.1)
brakeman (>= 2.1.1)
guard (>= 1.1.0)
guard-livereload (1.0.3)
em-websocket (>= 0.2.0)
guard-livereload (2.1.2)
em-websocket (~> 0.5)
guard (~> 2.0)
multi_json (~> 1.8)
guard-rspec (4.2.8)
guard (~> 2.1)
rspec (>= 2.14, < 4.0)
guard-shell (0.6.1)
guard (>= 1.1.0)
multi_json (~> 1.0)
guard-rspec (2.5.4)
guard (>= 1.1)
rspec (~> 2.11)
guard-shell (0.5.1)
guard (>= 1.1.0)
haml (4.0.3)
haml (4.0.5)
tilt
hashr (0.0.22)
highline (1.6.20)
highline (1.6.21)
hike (1.2.3)
http_parser.rb (0.5.3)
i18n (0.6.5)
i18n (0.6.9)
journey (1.0.4)
jquery-fileupload-rails (0.4.1)
actionpack (>= 3.1)
railties (>= 3.1)
jquery-rails (3.0.4)
jquery-rails (3.1.0)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
json (1.8.1)
kgio (2.8.1)
launchy (2.3.0)
kgio (2.9.2)
launchy (2.4.2)
addressable (~> 2.3)
libv8 (3.16.14.3)
listen (2.1.2)
listen (2.7.1)
celluloid (>= 0.15.2)
celluloid-io (>= 0.15.0)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
lockfile (2.1.0)
mail (2.5.4)
lumberjack (1.0.5)
mail (2.4.4)
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
mailcatcher (0.5.12)
activesupport (~> 3.0)
eventmachine (~> 1.0.0)
haml (>= 3.1, < 5)
mail (~> 2.3)
sinatra (~> 1.2)
skinny (~> 0.2.3)
sqlite3 (~> 1.3)
thin (~> 1.5.0)
method_source (0.8.2)
mime-types (1.25)
multi_json (1.8.2)
multi_test (0.0.2)
nokogiri (1.5.10)
poltergeist (1.4.1)
capybara (~> 2.1.0)
cliver (~> 0.2.1)
mime-types (1.25.1)
mini_portile (0.5.3)
multi_json (1.9.2)
multi_test (0.1.1)
nio4r (1.0.0)
nokogiri (1.6.1)
mini_portile (~> 0.5.0)
poltergeist (1.5.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
polyglot (0.3.3)
powder (0.2.0)
polyglot (0.3.4)
powder (0.2.1)
thor (>= 0.11.5)
pry (0.9.6)
coderay (>= 0.9.8)
method_source (>= 0.6.5)
ruby_parser (>= 2.0.5)
slop (~> 2.1.0)
rack (1.4.5)
pry (0.9.12.6)
coderay (~> 1.0)
method_source (~> 0.8)
slop (~> 3.4)
rack (1.4.0)
rack-cache (1.2)
rack (>= 0.4)
rack-livereload (0.3.15)
rack
rack-ssl (1.3.3)
rack-protection (1.5.2)
rack
rack-ssl (1.3.4)
rack
rack-test (0.6.2)
rack (>= 1.0)
rails (3.2.15)
actionmailer (= 3.2.15)
actionpack (= 3.2.15)
activerecord (= 3.2.15)
activeresource (= 3.2.15)
activesupport (= 3.2.15)
rails (3.2.11)
actionmailer (= 3.2.11)
actionpack (= 3.2.11)
activerecord (= 3.2.11)
activeresource (= 3.2.11)
activesupport (= 3.2.11)
bundler (~> 1.0)
railties (= 3.2.15)
railties (3.2.15)
actionpack (= 3.2.15)
activesupport (= 3.2.15)
railties (= 3.2.11)
railties (3.2.11)
actionpack (= 3.2.11)
activesupport (= 3.2.11)
rack-ssl (~> 1.3.2)
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
raindrops (0.12.0)
rake (10.1.0)
rb-fsevent (0.9.3)
rb-inotify (0.9.2)
raindrops (0.13.0)
rake (10.2.2)
rb-fsevent (0.9.4)
rb-inotify (0.9.3)
ffi (>= 0.5.0)
rdoc (3.12.2)
json (~> 1.4)
@@ -198,67 +223,80 @@ GEM
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.6)
rspec-expectations (2.14.3)
rspec-core (2.14.8)
rspec-expectations (2.14.5)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.4)
rspec-rails (2.14.0)
rspec-mocks (2.14.6)
rspec-rails (2.14.2)
actionpack (>= 3.0)
activemodel (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
ruby2ruby (2.0.6)
ruby2ruby (2.0.8)
ruby_parser (~> 3.1)
sexp_processor (~> 4.0)
ruby_parser (3.2.2)
ruby_parser (3.4.1)
sexp_processor (~> 4.1)
sass (3.2.12)
safe_yaml (0.9.7)
sass (3.3.4)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
sexp_processor (4.4.0)
simplecov (0.8.0.pre2)
sexp_processor (4.4.3)
simplecov (0.8.2)
docile (~> 1.1.0)
lockfile (>= 2.1.0)
multi_json
simplecov-html (~> 0.7.1)
simplecov-html (0.7.1)
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
sinatra (1.4.4)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
skinny (0.2.3)
eventmachine (~> 1.0.0)
thin (~> 1.5.0)
slim (2.0.2)
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
slop (2.1.0)
slop (3.5.0)
sprockets (2.2.2)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.8)
sqlite3 (1.3.9)
temple (0.6.7)
terminal-table (1.4.5)
therubyracer (0.12.0)
therubyracer (0.12.1)
libv8 (~> 3.16.14.0)
ref
thor (0.18.1)
thin (1.5.1)
daemons (>= 1.0.9)
eventmachine (>= 0.12.6)
rack (>= 1.0.0)
thor (0.19.1)
tilt (1.4.1)
timers (1.1.0)
travis-lint (1.7.0)
travis-lint (1.8.0)
hashr (~> 0.0.22)
safe_yaml (~> 0.9.0)
treetop (1.4.15)
polyglot
polyglot (>= 0.3.1)
trollop (2.0)
tzinfo (0.3.38)
uglifier (2.3.0)
tzinfo (0.3.39)
uglifier (2.5.0)
execjs (>= 0.3.0)
json (>= 1.8.0)
unicorn (4.6.3)
unicorn (4.8.2)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
websocket-driver (0.3.0)
websocket-driver (0.3.2)
xpath (2.0.0)
nokogiri (~> 1.3)
@@ -274,7 +312,7 @@ DEPENDENCIES
bundler-audit
capybara
coffee-rails
database_cleaner (< 1.1.0)
database_cleaner
execjs
foreman
gauntlt
@@ -285,15 +323,18 @@ DEPENDENCIES
jquery-fileupload-rails
jquery-rails
launchy
mailcatcher
poltergeist
powder
pry
rack (= 1.4.0)
rack-livereload
rails (= 3.2.15)
rack-ssl (= 1.3.4)
rails (= 3.2.11)
rb-fsevent
rspec-rails
sass-rails
simplecov (= 0.8.0.pre2)
simplecov
sqlite3
therubyracer
travis-lint
+81 -49
View File
@@ -1,78 +1,110 @@
## Getting Started ##
#### With Ruby, Rubygems, Git, and Bundler installed ####
# RailsGoat [![Build Status](https://api.travis-ci.org/OWASP/railsgoat.png?branch=master)](https://travis-ci.org/OWASP/railsgoat) [![Code Climate](https://codeclimate.com/github/OWASP/railsgoat.png)](https://codeclimate.com/github/OWASP/railsgoat)
git clone https://github.com/OWASP/railsgoat.git
RailsGoat is a vulnerable version of the Ruby on Rails Framework. It includes vulnerabilities from the OWASP Top 10, as well as some "extras" that the initial project contributors felt worthwhile to share. This project is designed to educate both developers, as well as security professionals.
cd railsgoat
## Getting Started
rvm use 2.0.0@railsgoat --create # https://rvm.io/
bundle
rake db:setup
To begin, install the Ruby Version Manager (RVM):
rails s
```
$ curl -L https://get.rvm.io | bash -s stable --autolibs=3 --ruby=1.9.3
$ rvm use 2.0.0@railsgoat --create # https://rvm.io/
```
open http://0.0.0.0:3000
After installing the package, clone this repo:
Start hacking!!!
```
$ git clone git@github.com:OWASP/railsgoat.git
```
### Running Capybara Tests ###
Navigate into the directory and accept the notice by typing `yes`:
```
****************************************************************************************************
* NOTICE *
****************************************************************************************************
* RVM has encountered a new or modified .rvmrc file in the current directory, this is a shell *
* script and therefore may contain any shell commands. *
* *
* Examine the contents of this file carefully to be sure the contents are safe before trusting it! *
* Do you wish to trust '/path/to/railsgoat/.rvmrc'? *
* Choose v[view] below to view the contents *
****************************************************************************************************
y[es], n[o], v[iew], c[cancel]>
```
RailsGoat now includes a set of _failing_ Capybara RSpecs, each one indicating a separate vulnerability exists
in the application.
Install the project dependencies:
To run them, though, you'll first need to [install PhantomJS](https://github.com/jonleighton/poltergeist#installing-phantomjs),
which is required by the Poltergeist Capybara driver. Then just rake:
```
$ bundle install
```
rake training
If you receive an error, make sure you have `bundler` installed:
NOTE: As vulnerabilities are fixed in the application, these specs won't change from to passing but to _pending_.
```
$ gem install bundler
```
### Developer Note ###
Initialize the database:
As changes are made to the application, the Capybara RSpecs can be used to verify the vulnerabilities
in the application are still intact. To use them in this way, and have them _pass_ instead of fail,
set the `RAILSGOAT_MAINTAINER` environment variable.
```
$ rake db:setup
```
<p/>
Conversion to the OWASP Top 10, 2013 is under way.
Start the WEBrick HTTP Server:
You can view progress within the top-10-2013 branch.
```
$ rails server
```
git fetch origin
git checkout top-10-2013
Then proceed with browsing the site as normal :thumbsup:
<hr/>
Open your favorite browser, navigate to `http://localhost:3000` and start hacking!
### Build Info ###
## Capybara Tests
[![Code Climate](https://codeclimate.com/github/OWASP/railsgoat.png)](https://codeclimate.com/github/OWASP/railsgoat)
RailsGoat now includes a set of failing Capybara RSpecs, each one indicating that a separate vulnerability exists in the application. To run them, you first need to install [PhantomJS](https://github.com/jonleighton/poltergeist#installing-phantomjs), which is required by the Poltergeist Capybara driver. Upon installation, simply run the following rake task:
[![Build Status](https://travis-ci.org/OWASP/railsgoat.png?branch=master)](https://travis-ci.org/OWASP/railsgoat)
```
$ rake training
```
### License Stuff ###
NOTE: As vulnerabilities are fixed in the application, these specs will not change to `passing`, but to `pending`.
## Processing Email
In order for RailsGoat to effectively process email, you will first need to run MailCatcher, an SMTP server that will intercept email messages and display them in a web interface.
To start an instance of MailCatcher, simply run:
```
$ mailcatcher
```
If successful, you should see the following output:
```
Starting MailCatcher
==> smtp://127.0.0.1:1025
==> http://127.0.0.1:1080
*** MailCatcher runs as a daemon by default. Go to the web interface to quit.
```
Alternatively, you can run MailCatcher in the foreground by running `mailcatcher -f` in your terminal.
## Contributing
As changes are made to the application, the Capybara RSpecs can be used to verify that the vulnerabilities in the application are still intact. To use them in this way, and have them `pass` instead of `fail`, set the `RAILSGOAT_MAINTAINER` environment variable.
Conversion to the OWASP Top Ten 2013 completed in November, 2013.
# License
The MIT License (MIT)
Copyright (c) 2013 The Open Web Application Security Project
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
<hr/>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-261
View File
@@ -1,261 +0,0 @@
== Welcome to Rails
Rails is a web-application framework that includes everything needed to create
database-backed web applications according to the Model-View-Control pattern.
This pattern splits the view (also called the presentation) into "dumb"
templates that are primarily responsible for inserting pre-built data in between
HTML tags. The model contains the "smart" domain objects (such as Account,
Product, Person, Post) that holds all the business logic and knows how to
persist themselves to a database. The controller handles the incoming requests
(such as Save New Account, Update Product, Show Post) by manipulating the model
and directing data to the view.
In Rails, the model is handled by what's called an object-relational mapping
layer entitled Active Record. This layer allows you to present the data from
database rows as objects and embellish these data objects with business logic
methods. You can read more about Active Record in
link:files/vendor/rails/activerecord/README.html.
The controller and view are handled by the Action Pack, which handles both
layers by its two parts: Action View and Action Controller. These two layers
are bundled in a single package due to their heavy interdependence. This is
unlike the relationship between the Active Record and Action Pack that is much
more separate. Each of these packages can be used independently outside of
Rails. You can read more about Action Pack in
link:files/vendor/rails/actionpack/README.html.
== Getting Started
1. At the command prompt, create a new Rails application:
<tt>rails new myapp</tt> (where <tt>myapp</tt> is the application name)
2. Change directory to <tt>myapp</tt> and start the web server:
<tt>cd myapp; rails server</tt> (run with --help for options)
3. Go to http://localhost:3000/ and you'll see:
"Welcome aboard: You're riding Ruby on Rails!"
4. Follow the guidelines to start developing your application. You can find
the following resources handy:
* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html
* Ruby on Rails Tutorial Book: http://www.railstutorial.org/
== Debugging Rails
Sometimes your application goes wrong. Fortunately there are a lot of tools that
will help you debug it and get it back on the rails.
First area to check is the application log files. Have "tail -f" commands
running on the server.log and development.log. Rails will automatically display
debugging and runtime information to these files. Debugging info will also be
shown in the browser on requests from 127.0.0.1.
You can also log your own messages directly into the log file from your code
using the Ruby logger class from inside your controllers. Example:
class WeblogController < ActionController::Base
def destroy
@weblog = Weblog.find(params[:id])
@weblog.destroy
logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!")
end
end
The result will be a message in your log file along the lines of:
Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1!
More information on how to use the logger is at http://www.ruby-doc.org/core/
Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are
several books available online as well:
* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe)
* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide)
These two books will bring you up to speed on the Ruby language and also on
programming in general.
== Debugger
Debugger support is available through the debugger command when you start your
Mongrel or WEBrick server with --debugger. This means that you can break out of
execution at any point in the code, investigate and change the model, and then,
resume execution! You need to install ruby-debug to run the server in debugging
mode. With gems, use <tt>sudo gem install ruby-debug</tt>. Example:
class WeblogController < ActionController::Base
def index
@posts = Post.all
debugger
end
end
So the controller will accept the action, run the first line, then present you
with a IRB prompt in the server window. Here you can do things like:
>> @posts.inspect
=> "[#<Post:0x14a6be8
@attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>,
#<Post:0x14a6620
@attributes={"title"=>"Rails", "body"=>"Only ten..", "id"=>"2"}>]"
>> @posts.first.title = "hello from a debugger"
=> "hello from a debugger"
...and even better, you can examine how your runtime objects actually work:
>> f = @posts.first
=> #<Post:0x13630c4 @attributes={"title"=>nil, "body"=>nil, "id"=>"1"}>
>> f.
Display all 152 possibilities? (y or n)
Finally, when you're ready to resume execution, you can enter "cont".
== Console
The console is a Ruby shell, which allows you to interact with your
application's domain model. Here you'll have all parts of the application
configured, just like it is when the application is running. You can inspect
domain models, change values, and save to the database. Starting the script
without arguments will launch it in the development environment.
To start the console, run <tt>rails console</tt> from the application
directory.
Options:
* Passing the <tt>-s, --sandbox</tt> argument will rollback any modifications
made to the database.
* Passing an environment name as an argument will load the corresponding
environment. Example: <tt>rails console production</tt>.
To reload your controllers and models after launching the console run
<tt>reload!</tt>
More information about irb can be found at:
link:http://www.rubycentral.org/pickaxe/irb.html
== dbconsole
You can go to the command line of your database directly through <tt>rails
dbconsole</tt>. You would be connected to the database with the credentials
defined in database.yml. Starting the script without arguments will connect you
to the development database. Passing an argument will connect you to a different
database, like <tt>rails dbconsole production</tt>. Currently works for MySQL,
PostgreSQL and SQLite 3.
== Description of Contents
The default directory structure of a generated Ruby on Rails application:
|-- app
| |-- assets
| | |-- images
| | |-- javascripts
| | `-- stylesheets
| |-- controllers
| |-- helpers
| |-- mailers
| |-- models
| `-- views
| `-- layouts
|-- config
| |-- environments
| |-- initializers
| `-- locales
|-- db
|-- doc
|-- lib
| |-- assets
| `-- tasks
|-- log
|-- public
|-- script
|-- test
| |-- fixtures
| |-- functional
| |-- integration
| |-- performance
| `-- unit
|-- tmp
| `-- cache
| `-- assets
`-- vendor
|-- assets
| |-- javascripts
| `-- stylesheets
`-- plugins
app
Holds all the code that's specific to this particular application.
app/assets
Contains subdirectories for images, stylesheets, and JavaScript files.
app/controllers
Holds controllers that should be named like weblogs_controller.rb for
automated URL mapping. All controllers should descend from
ApplicationController which itself descends from ActionController::Base.
app/models
Holds models that should be named like post.rb. Models descend from
ActiveRecord::Base by default.
app/views
Holds the template files for the view that should be named like
weblogs/index.html.erb for the WeblogsController#index action. All views use
eRuby syntax by default.
app/views/layouts
Holds the template files for layouts to be used with views. This models the
common header/footer method of wrapping views. In your views, define a layout
using the <tt>layout :default</tt> and create a file named default.html.erb.
Inside default.html.erb, call <% yield %> to render the view using this
layout.
app/helpers
Holds view helpers that should be named like weblogs_helper.rb. These are
generated for you automatically when using generators for controllers.
Helpers can be used to wrap functionality for your views into methods.
config
Configuration files for the Rails environment, the routing map, the database,
and other dependencies.
db
Contains the database schema in schema.rb. db/migrate contains all the
sequence of Migrations for your schema.
doc
This directory is where your application documentation will be stored when
generated using <tt>rake doc:app</tt>
lib
Application specific libraries. Basically, any kind of custom code that
doesn't belong under controllers, models, or helpers. This directory is in
the load path.
public
The directory available for the web server. Also contains the dispatchers and the
default HTML files. This should be set as the DOCUMENT_ROOT of your web
server.
script
Helper scripts for automation and generation.
test
Unit and functional tests along with fixtures. When using the rails generate
command, template test files will be generated for you and placed in this
directory.
vendor
External libraries that the application depends on. Also includes the plugins
subdirectory. If the app has frozen rails, those gems also go here, under
vendor/rails/. This directory is in the load path.
@@ -0,0 +1,3 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
@@ -0,0 +1,3 @@
// Place all the styles related to the password_resets controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
+7 -1
View File
@@ -1,6 +1,6 @@
class AdminController < ApplicationController
# before_filter :administrative
before_filter :administrative, :if => :admin_param
skip_before_filter :has_info
def dashboard
@@ -45,4 +45,10 @@ class AdminController < ApplicationController
end
end
private
def admin_param
params[:admin_id] != '1'
end
end
@@ -0,0 +1,57 @@
class Api::V1::UsersController < ApplicationController
skip_before_filter :authenticated
before_filter :valid_api_token
before_filter :extrapolate_user
respond_to :json
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
def show
respond_with @user.as_json
end
private
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /(.*?)-(.*)/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
def check_hash(id, hash)
digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
@clean_token = CGI::unescape(token)
end
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
@user = User.find_by_id(@clean_token.split("-").first)
end
end
+5 -2
View File
@@ -9,7 +9,10 @@ class ApplicationController < ActionController::Base
private
def current_user
@current_user ||= User.find_by_user_id(session[:user_id].to_s)
@current_user ||= (
User.find_by_auth_token(cookies[:auth_token].to_s) ||
User.find_by_user_id(session[:user_id].to_s)
)
end
def authenticated
@@ -23,7 +26,7 @@ class ApplicationController < ActionController::Base
def administrative
if not is_admin?
reset_session
#reset_session
redirect_to root_url
end
end
@@ -0,0 +1,63 @@
class PasswordResetsController < ApplicationController
skip_before_filter :authenticated
def reset_password
user = Marshal.load(Base64.decode64(params[:user])) unless params[:user].nil?
if user && params[:password] && params[:confirm_password] && params[:password] == params[:confirm_password]
user.password = params[:password]
user.save!
flash[:success] = "Your password has been reset please login"
redirect_to :login
else
flash[:error] = "Error resetting your password. Please try again."
redirect_to :login
end
end
def confirm_token
if !params[:token].nil? && is_valid?(params[:token])
flash[:success] = "Password reset token confirmed! Please create a new password."
render :reset_password
else
flash[:error] = "Invalid password reset token. Please try again."
redirect_to :login
end
end
def forgot_password
@user = User.find_by_email(params[:email]) unless params[:email].nil?
if @user && password_reset_mailer(@user)
flash[:success] = "Password reset email sent to #{params[:email]}"
redirect_to :login
else
flash[:error] = "There was an issue sending password reset email to #{params[:email]}".html_safe unless params[:email].nil?
end
end
private
def password_reset_mailer(user)
token = generate_token(user.id, user.email)
UserMailer.forgot_password(user.email, token).deliver
end
def generate_token(id, email)
hash = Digest::MD5.hexdigest(email)
"#{id}-#{hash}"
end
def is_valid?(token)
if token =~ /(?<user_id>\d+)-(?<email_hash>[A-Z0-9]{32})/i
# Fetch the user by their id, and hash their email address
@user = User.find_by_id($~[:user_id])
email = Digest::MD5.hexdigest(@user.email)
# Compare and validate our hashes
return true if email == $~[:email_hash]
end
end
end
+43
View File
@@ -0,0 +1,43 @@
class PayController < ApplicationController
def index
end
def update_dd_info
msg = false
pay = Pay.new(
:bank_account_num => params[:bank_account_num],
:bank_routing_num => params[:bank_routing_num],
:percent_of_deposit => params[:dd_percent]
)
pay.user_id = current_user.user_id
msg = true if pay.save!
respond_to do |format|
format.json {render :json => {:msg => msg } }
end
end
def show
respond_to do |format|
format.json { render :json => {:user => current_user.pay.as_json} }
end
end
def destroy
pay = Pay.find_by_id(params[:id])
if pay.present? and pay.destroy
flash[:success] = "Successfully Deleted Entry"
else
flash[:error] = "Unable to process that request at this time"
end
redirect_to user_pay_index_path
end
def decrypted_bank_acct_num
decrypted = Encryption.decrypt_sensitive_value(params[:value_to_decrypt])
respond_to do |format|
format.json {render :json => {:account_num => decrypted || "No Data" }}
end
end
end
+5 -1
View File
@@ -8,7 +8,6 @@ class SessionsController < ApplicationController
redirect_to home_dashboard_index_path if current_user
end
def create
path = params[:url].present? ? params[:url] : home_dashboard_index_path
begin
@@ -19,7 +18,11 @@ class SessionsController < ApplicationController
end
if user
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token if User.where(:user_id => user.user_id).exists?
else
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
end
redirect_to path
else
# Removed this code, just doesn't seem specific enough!
@@ -30,6 +33,7 @@ class SessionsController < ApplicationController
end
def destroy
cookies.delete(:auth_token)
reset_session
redirect_to root_path
end
+11 -45
View File
@@ -15,7 +15,6 @@ class TutorialsController < ApplicationController
end
def injection
end
def xss
@@ -63,6 +62,12 @@ class TutorialsController < ApplicationController
def misconfig
end
def insecure_components
end
def access_control
end
def crypto
end
@@ -78,55 +83,16 @@ class TutorialsController < ApplicationController
def guard
end
def info_disclosure
@bad_code_1 =
%q{
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width:16%">Full Name</th>
<th style="width:16%">Income</th>
<th style="width:16%">Bonuses</th>
<th style="width:16%">Years w/ MetaCorp</th>
<th style="width:16%">SSN</th>
<th style="width:16%">DoB</th>
</tr>
</thead>
<tbody>
<tr>
<td><%= "#{@user.first_name} #{@user.last_name}" %></td>
<td><%= @user.work_info.income %></td>
<td><%= @user.work_info.bonuses %></td>
<td><%= @user.work_info.years_worked %></td>
<td class="ssn"><%= @user.work_info.SSN %></td>
<td><%= @user.work_info.DoB %></td>
</tr>
</tbody>
</table>
}
@good_code_1 = %q{
class WorkInfo < ActiveRecord::Base
attr_accessible :DoB, :SSN, :bonuses, :income, :years_worked
belongs_to :user
# We should probably use this
def last_four
"***-**-" << self.SSN[-4,4]
end
end
}
@bad_code_2 = %q{<td class="ssn"><%= @user.work_info.SSN %></td>}
@good_code_2 = %q{<td class="ssn"><%= @user.work_info.last_four %></td>}
def logic_flaws
end
def mass_assignment
end
def guantlt
end
def constantize
end
+2 -1
View File
@@ -3,6 +3,7 @@ class UsersController < ApplicationController
skip_before_filter :has_info
skip_before_filter :authenticated, :only => [:new, :create]
def new
@user = User.new
end
@@ -16,7 +17,7 @@ class UsersController < ApplicationController
else
@user = user
flash[:error] = user.errors.full_messages.to_sentence
redirect_to :sign_up
redirect_to :signup
end
end
+2
View File
@@ -0,0 +1,2 @@
module Api::V1::UsersHelper
end
+2
View File
@@ -0,0 +1,2 @@
module PasswordResetsHelper
end
+2
View File
@@ -0,0 +1,2 @@
module PayHelper
end
+10
View File
@@ -0,0 +1,10 @@
class UserMailer < ActionMailer::Base
default from: "noreply@railsgoat.dev"
def forgot_password(email, token)
@token = token
@url = url_for(controller: "password_resets", action: "reset_password", only_path: false) + "?token=#{token}"
mail(to: "#{email}", subject: "Reset your MetaCorp password")
end
end
+6
View File
@@ -0,0 +1,6 @@
class KeyManagement < ActiveRecord::Base
attr_accessible :iv, :user_id
belongs_to :work_info
belongs_to :user
end
+25
View File
@@ -0,0 +1,25 @@
class Pay < ActiveRecord::Base
# mass-assignable attributes
attr_accessible :bank_account_num, :bank_routing_num, :percent_of_deposit
# Associations
belongs_to :user
# Validations
validates :bank_account_num, presence: true
validates :bank_routing_num, presence: true
validates :percent_of_deposit, presence: true
# callbacks
before_save :encrypt_bank_account_num
def as_json
super(only: [:bank_account_num, :bank_routing_num, :percent_of_deposit, :id])
end
def encrypt_bank_account_num
self.bank_account_num = Encryption.encrypt_sensitive_value(self.bank_account_num)
end
end
+40 -7
View File
@@ -1,11 +1,19 @@
require 'encryption'
class User < ActiveRecord::Base
attr_accessible :email, :admin, :first_name, :last_name, :user_id, :password, :password_confirmation
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create,
:if => :password#,
#:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
:if => :password
=begin
validates :password, :presence => true,
:confirmation => true,
:if => :password,
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
=end
validates_presence_of :email
validates_uniqueness_of :email
validates_format_of :email, :with => /.+@.+\..+/i
@@ -18,12 +26,15 @@ class User < ActiveRecord::Base
has_one :work_info, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
has_many :performance, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
has_many :messages, :foreign_key => :receiver_id, :primary_key => :user_id, :dependent => :destroy
has_many :pay, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
before_create { generate_token(:auth_token) }
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
#work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
@@ -31,23 +42,39 @@ class User < ActiveRecord::Base
"#{self.first_name} #{self.last_name}"
end
=begin
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
=end
private
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
if user
raise "#{email} doesn't exist!" if !(user)
if user.password == Digest::MD5.hexdigest(password)
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
=begin
# More secure version, still lacking a decent hashing routine, this is for timing attack prevention
def self.authenticate(email, password)
user = find_by_email(email) || User.new(:password => "")
if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))
return user
else
raise "Incorrect username or password"
end
end
=end
def assign_user_id
unless @skip_user_id_assign.present? || self.user_id.present?
user = User.order("user_id").last
@@ -64,4 +91,10 @@ private
end
end
def generate_token(column)
begin
self[column] = Encryption.encrypt_sensitive_value(self.user_id)
end while User.exists?(column => self[column])
end
end
+28 -1
View File
@@ -1,17 +1,44 @@
class WorkInfo < ActiveRecord::Base
attr_accessible :DoB, :SSN, :bonuses, :income, :years_worked
belongs_to :user
has_one :key_management, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
#before_save :encrypt_ssn
# We should probably use this
def last_four
"***-**-" << self.SSN[-4,4]
"***-**-" << self.decrypt_ssn[-4,4]
end
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def key
raise "Key Missing" if !(KEY)
KEY
end
def iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def cipher_type
'aes-256-cbc'
end
end
+1 -1
View File
@@ -59,7 +59,7 @@ function makeActive(){
};
function loadTable(){
$("#userDataTable").load("/admin/"+ <%=current_user.user_id %> + "/get_all_users")
$("#userDataTable").load("/admin/"+ <%= params[:admin_id] %> + "/get_all_users")
};
$(document).ready(
+1 -1
View File
@@ -83,7 +83,7 @@ $('#delete_button').click(function() {
$("#editAcct").modal('hide');
$.ajax({
url: "/admin/" + <%= @user.user_id %> + "/delete_user.json",
url: "/admin/" + <%= params[:admin_id] %> + "/delete_user.json",
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
@@ -65,6 +65,14 @@
</div>
Messages
<% end %>
</li>
<li id="pay">
<%= link_to user_pay_index_path(:user_id => current_user.user_id) do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe038;"></span>
</div>
Pay
<% end %>
</li>
</ul>
</div>
+34 -30
View File
@@ -16,22 +16,23 @@
A1 Injection
<% end %>
</li>
<li id="xss">
<%= link_to xss_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A2 XSS
<% end %>
</li>
<li id="broken_auth">
<%= link_to broken_auth_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A3 Broken Auth
A2 Broken Auth
<% end %>
</li>
<li id="xss">
<%= link_to xss_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A3 XSS
<% end %>
</li>
<li id="insecure_dor">
<%= link_to insecure_dor_tutorials_path do %>
<div class="icon">
@@ -40,44 +41,44 @@
A4 Insecure DOR
<% end %>
</li>
<li id="csrf">
<%= link_to csrf_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A5 CSRF
<% end %>
</li>
<li id="misconfig">
<%= link_to misconfig_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A6 Misconfig
A5 Misconfig
<% end %>
</li>
<li id="crypto">
<%= link_to crypto_tutorials_path do %>
<li id="exposure">
<%= link_to exposure_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A7 Crypto
A6 Exposure
<% end %>
</li>
<li id="url_access">
<%= link_to url_access_tutorials_path do %>
<li id="access_control">
<%= link_to access_control_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A8 URL Access
A7 Access Control
<% end %>
</li>
<li id="ssl_tls">
<%= link_to ssl_tls_tutorials_path do %>
<li id="csrf">
<%= link_to csrf_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A9 SSL/TLS
A8 CSRF
<% end %>
</li>
<li id="insecure_components">
<%= link_to insecure_components_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
</div>
A9 Components
<% end %>
</li>
<li id="redirects">
@@ -96,21 +97,24 @@
Extras
</a>
<ul>
<li id="gauntlt">
<%= link_to "gauntlt", gauntlt_tutorials_path %>
</li>
<li id="guard">
<%= link_to "Guard", guard_tutorials_path %>
</li>
<!--<li>
<a href="#">Session Secret</a>
</li>-->
<li id="info_dislosure">
<%= link_to "Info Dislosure", info_disclosure_tutorials_path %>
</li>
<li id="mass_assignment">
<%= link_to "Mass Assignment", mass_assignment_tutorials_path %>
</li>
<li id="constantize">
<%= link_to "Constantize", constantize_tutorials_path %>
</li>
<li id="logic_flaws">
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
</li>
<!--
<li>
<a href="#">DB Sessions</a>
@@ -0,0 +1,98 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A7 - Missing Function Level Access Control
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
Many web applications check URL access rights before rendering protected links and buttons. However, applications need to perform similar access control checks each time these pages are accessed, or attackers will be able to forge URLs to access these hidden pages anyway.
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Rails provides the ability to apply before_filter(s) which run prior to rendering content to the user. This is helpful when restricting access to content based on the user's role. These filters can be skipped on certain actions or controllers and entirely if certain conditions are met. In this case, the before_filter is being skipped if the admin_id param is equal to 1.
</p>
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
before_filter :administrative, :if => :admin_param
...
def admin_param
params[:id] == '1'
end
} %>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Failure to Restrict URL Access - ATTACK</b></p>
<p class="desc">
Request the following URL: /admin/1/dashboard and have fun :-)
</p>
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
<p class="desc">
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb. The additional condition that if the admin_id param equals 1 means the filter can be circumvented by an attacker. The way to fix this issue is to remove the conditional and enforce the filter on all access requests to the admin dashboard as follows:
</p>
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
before_filter :administrative
} %>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
I bet there is some admin functionality in here :-)
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,111 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A2 - Broken Authentication and Session Management - Insecure Compare and Timing Attacks
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseCompOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseCompOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
A timing attack can exist in several forms. This specific case relates to username (email address) enumeration. By leveraging an automated tool, an attacker can review any subtle variation in response times after submitting a login request to determine if the application is performing a computationally intense function. Meaning, if a function is run once a user is discovered, even if the password is incorrect, this information provides the user with valid or invalid usernames.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseCompTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseCompTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within app/models/user.rb
</p>
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
<span style="background-color: yellow"> user = find_by_email(email)</span>
raise "#{email} doesn't exist!" if !(user)
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
return auth
end
</pre>
<p class="desc">
Ignore for a moment that the application actually tells you whether or not an email address exists :-). Instead, let's look at what would happen if this error message wasn't so specific. Even if the error message vulnerability was mitigated (because it indicates whether or not a user exists), there will be some variations in the application's response between a user that exists and one that does not (however so slight, considering MD5 is in use).
</p>
<p class="desc">
To understand why, let's follow the flow of this code example. Firstly, the application look for a user by email. If not found, nothing else really happens. No further processing, password comparison, etc. If a user <i>is</i> found, we will perform a password comparison and process as normal.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseCompThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseCompThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Lack of Password Complexity - SOLUTION</b></p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
def self.authenticate(email, password)
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
return user
else
raise "Incorrect username or password"
end
end
</pre>
<p class="desc">
To mitigate this attack and shore up our weakness, we do two things. The first is to find a user by email, if they don't exist, create a new user object in memory (not in the database) and assign it a blank password value. This means, regardless of whether or not a user exists, we will have a user to perform some processing on. The next is, we take the input from the user and match it against the user object's password leveraging secure_compare. This is a function (secure_compare) used to ensure that when a comparison happens, it will always take the same amount of time.
</p>
<p class="desc">
In summary, we have ensured that regardless of whether or not a user exists, a password comparison will always occur and it will take the same amount of time to complete.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseCompFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
<div class="accordion-body collapse" id="collapseCompFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Timing is everything. Authenticating is important too.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A3 - Broken Authentication and Session Management - Lack of Password Complexity
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A2 - Broken Authentication and Session Management - Lack of Password Complexity
</div>
</div>
<div class="widget-body">
@@ -72,9 +72,9 @@
<pre class="ruby">
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create,
<span style="background-color: yellow">:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}</span>
:if => :password,
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
</pre>
</div>
</div>
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A3 - Broken Authentication and Session Management - Username/Pass Enumeration
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A2 - Broken Authentication and Session Management - Username/Pass Enumeration
</div>
</div>
<div class="widget-body">
@@ -55,7 +55,7 @@
mike@metacorp.com
</td>
<td>
motorcross1445
motocross1445
</td>
</tr>
<tr>
@@ -1,101 +0,0 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A7 - Insecure Cryptographic Storage - Clear-text storage of SSN(s)
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSSNOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseSSNOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores Social Security Numbers in plain-text and because of this, it fails to adequately protect these numbers from theft.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSSNTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseSSNTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
The WorkInfo model (app/models/work_info.rb) is where the code to encrypt this data should be. However, as seen, is missing any routine to do so.
</p>
<pre class="ruby">
class WorkInfo < ActiveRecord::Base
attr_accessible :DoB, :SSN, :bonuses, :income, :years_worked
belongs_to :user
# We should probably use this
def last_four
"***-**-" << self.SSN[-4,4]
end
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSSNThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseSSNThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Password Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
One additional item to note with rails specifically, the framework makes it easy to determine the type of environment running, example:
<pre class="ruby">
Rails.env.production?
</pre>
...or
<pre class="ruby">
Rails.env.development?
</pre>
This allows developers to easily create different keys for development and production and should be considered an asset to utilize. While development keys are usually stored within the source code of most rails applications, and developers with access to the repo can download those keys, the same should NOT hold true for production keys.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseSSNFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
<div class="accordion-body collapse" id="collapseSSNFour" style="height: 0px;">
<div class="accordion-inner">
How protected are those passwords in the database against cracking?
</div>
</div>
</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A5 - Cross Site Request Forgery (CSRF)
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A8 - Cross Site Request Forgery (CSRF)
</div>
</div>
<div class="widget-body">
@@ -60,7 +60,7 @@
<div class="accordion-inner">
<p><b> Cross-Site Request Forgery ATTACK:</b></p>
<p class="desc">
The application allows users to update their calendar and schedule PTO events (PTO section). Due to the fact CSRF protections are disabled, the AJAX request will send the authenticity token but the application will <b>not</b> validate either it's presence or validity. Create an html page using the code shown below, authenticate as another user, click on it, review the new calendar (change the dates under date_range1). You should see this HTML code will work, even if you hadn't navigated to the PTO section prior to sending it.
The application allows users to update their calendar and schedule PTO events (PTO section). Due to the fact CSRF protections are disabled, the AJAX request will send the authenticity token but the application will <b>not</b> validate either its presence or validity. Create an html page using the code shown below, authenticate as another user, click on it, review the new calendar (change the dates under date_range1). You should see this HTML code will work, even if you hadn't navigated to the PTO section prior to sending it.
</p>
<p>
<pre class="ruby">
@@ -84,7 +84,7 @@
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
<p>
By Default, the protect_from_forgery directive is added under the application_controller.rb at project creation. However, occasionally developers turn it off (comment out) because of issues with JS. There are two separate solutions around the JS problem.
By default, the protect_from_forgery directive is added under the application_controller.rb at project creation. However, occasionally developers turn it off (comment out) because of issues with JS. There are two separate solutions around the JS problem.
</p>
<p>
Once protect_from_forgery is added back...
@@ -0,0 +1,144 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A6 - Sensitive Data Exposure - Model Attributes Exposure
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseModelOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseModelOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseModelTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseModelTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
def show
respond_with @user.as_json
end
</pre>
<p class="desc">
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
</p>
<pre class="ruby">
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
</pre>
<p class="desc">
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
X-Runtime: 0.051734
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
Note that all attributes associated with this user are returned via the API.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseModelThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseModelThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Model Attributes Exposure - ATTACK</b></p>
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
<p><b>Model Attributes Exposure - SOLUTION</b></p>
<p class="desc">
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
</p>
<pre class="ruby">
respond_with @user.admin ? User.all.as_json : @user.as_json
</pre>
<p class="desc">
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
</p>
<pre class="ruby">
def as_json
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
end
</pre>
<p class="desc">
The response from the API should look like:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "2333488e856669ac637e37cb4cf09cb6"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: baa6a1c90004838793614e4c61633767
X-Runtime: 0.092768
Connection: close
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseModelFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
<div class="accordion-body collapse" id="collapseModelFour" style="height: 0px;">
<div class="accordion-inner">
We have an API available... what does it return?
</div>
</div>
</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A7 - Insecure Cryptographic Storage - Password Storage
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A6 - Sensitive Data Exposure - Insecure Password Storage
</div>
</div>
<div class="widget-body">
@@ -0,0 +1,170 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A6 - Sensitive Data Exposure - Clear-text storage of SSN(s)
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSSNOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseSSNOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores and transmits Social Security Numbers insecurely.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSSNTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseSSNTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
</p>
<p class="desc">
The WorkInfo model (app/models/work_info.rb) is missing code to encrypt this data prior to storage. Additionally, while code exists to render only the last 4 numbers of an SSN (shown below), at no time is it used.
</p>
<pre class="ruby">
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSSNThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseSSNThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>SSN Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
</p>
<p class="desc">
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
</p>
<pre class="ruby">
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def key
raise "Key Missing" if !(KEY)
KEY
end
def iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def cipher_type
'aes-256-cbc'
end
</pre>
<p class="desc">
Also within the WorkInfo model, we add the following line of code...
</p>
<pre class="ruby">
before_save :encrypt_ssn
</pre>
<p class="desc">
The remaining pieces are:
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
<li> For new user's who are registering, we create an initialization specific to their account</li>
</p>
<pre class="ruby">
# SEED DATA
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
</pre>
<pre class="ruby">
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
elsif Rails.env.development?
KEY = "123456789101112123456789101112123456789101112"
end
</pre>
<pre class="ruby">
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
<%= CGI.unescapeHTML("&lt;td class=&quot;ssn&quot;&gt;&lt;%= @user.work_info.last_four %&gt;&lt;/td&gt;") %>
</pre>
<pre class="ruby">
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseSSNFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
<div class="accordion-body collapse" id="collapseSSNFour" style="height: 0px;">
<div class="accordion-inner">
My SSN seems pretty important, hope it's kept safe!
</div>
</div>
</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Information Disclosure (Sensitive)
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A9 - Using Components with Known Vulnerabilities
</div>
</div>
<div class="widget-body">
@@ -17,7 +17,7 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The application stores and returns full social security numbers. The clear-text storage of this value within the database falls under <%= link_to "Insecure Cryptographic Storage", crypto_tutorials_path, {:style => "color: rgb(181, 121, 158)"}%>. However, the other failure here is that the application returns this full SSN value within the response for the user's Work Info page. Although a portion of the SSN value is obfuscated using JavaScript (when rendered in the browser), any attacker who has positioned themselves to sniff this traffic or read the user's browser cache can extract the full value from the source.
Virtually every application has these issues because most development teams dont focus on ensuring their components/libraries are up to date. In many cases, the developers dont even know all the components they are using, never mind their versions. Component dependencies make things even worse.
</p>
</div>
</div>
@@ -32,15 +32,16 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
The bug is introduced within app/views/work_info/index.html.erb, seen on line 20:
<p class="desc">
Within the Gemfile the following gem versions are set. These versions of Rails and Rack are both vulnerable to multiple attacks.
</p>
<p>
<pre class="ruby">
<%= @bad_code_1 %>
<%= %q{
gem 'rails', '3.2.11'
gem 'rack', '1.4.3'
} %>
</pre>
The value, stored unencrypted, is called directly from the database. (line 20)
<p class="desc">
</p>
</div>
</div>
@@ -55,25 +56,9 @@
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p>
A model method to return only the last four digits already exists. The following code was taken from the WorkInfo model - app/models/work_info.rb:
</p>
<p class="desc">
<pre class="ruby">
<%= @good_code_1%>
</pre>
To fix this issue, simply update your gems after unpinning the gem versions. You should always run the most up to date version possible and run Bundler-Audit Regularly.
</p>
<p class="desc">
Essentially, this takes the SSN string from the DB, retrieves only the last four characters in the string, and concatenates the last four characters with asterisks. Because this occurs at the model level, the view page never calls the full SSN value and therefore the user's browser never receives the full SSN. The view code would need to change from...
<pre class="ruby">
<%= @bad_code_2 %>
</pre>
to...
<pre class="ruby">
<%= @good_code_2 %>
</pre>
</p>
</div>
</div>
</div>
@@ -87,9 +72,7 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p>
Inspect your work information closely
</p>
Remeber to keep your gems up to date!
</div>
</div>
</div>
@@ -17,7 +17,7 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
A direct object reference occurs when a developer exposes a reference to an internal implementation object, such as a file, directory, or database key. Without an access control check or other protection, attackers can manipulate these references to access unauthorized data.
Applications frequently use the actual name or key of an object when generating web pages. Applications dont always verify the user is authorized for the target object. This results in an insecure direct object reference flaw. Testers can easily manipulate parameter values to detect such flaws. Code analysis quickly shows whether authorization is properly verified.
</p>
</div>
</div>
@@ -0,0 +1,219 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Logic Flaws - Broken Regular Expression
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
Regular expressions are a common way to extract the data you want from the data you do <b><u>not</u></b> want. It is common for Ruby developers to forget that in Ruby regexp anchors are \A and \z. This allows strict enforcement so that potentially dangerous characters such as the newline character aren't able to bypass security-based regular expression checks.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within the file app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
before_filter :valid_api_token
before_filter :extrapolate_user
</pre>
<p class="desc">
The above two lines specify that we will run these validations prior to allowing a user to interact with the API endpoints.
</p>
<pre class="ruby">
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
<span style="background-color:yellow">id = $1</span>
hash = $2
(id && hash) ? true : false
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
end
def check_hash(id, hash)
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
end
</pre>
<p class="desc">
This first validation, <i>valid_api_token</i>, extracts the user's access token. Within the token there is a user ID and a hash. The application extracts both values, hashes the user ID and the application's secret salt together. If the digest hash matches with the user provided hash, the entire token is valid. <br/><br/>Meaning, if the hash (<i>check_hash</i>) doesn't match the hash provided by the user, the token is invalid and therefore unauthorized. Alternatively, the hash provided is valid but the user ID is invalid. <br/><br/>The next validation, built after this check, extrapolates the user from that hash. In theory, because we have already validated both the user ID and hash are valid, we can just extract the user ID from what has been provided and determine user access.
</p>
<pre class="ruby">
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
end
</pre>
<p class="desc">
Unfortunately, we've made a mistake. The regular expression can be bypassed by entering a newline character (url encoded: <i>%0a</i>).We meant or expected for a user to enter a token such as:
</p>
<pre>
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
However, the user actually enters:
</p>
<pre>
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
This means that our token will pass the initial hash check. Additionally, when we perform the split by the hyphen (<i>"-"</i>) character, and retrieve the first value from the newly created array (what should be a valid user ID), it will be <i>"2\n1"</i>. When performing a <i>find_by_*</i>, ActiveRecord will ignore everything from the newline character on and return the result of the first character. This means, we can become another user!
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Broken Regular Expression ATTACK:</b></p>
<p class="desc">
As discussed in the Bug Section (above), you can prepend the user ID of the person whose information you would like to retrieve followed by a newline character and your user's valid API token. The following is an example of what our request <i>should</i> look like:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
Content-Length: 4
</pre>
<p class="desc">
This is the response:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
X-Runtime: 0.008342
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
We want to access this endpoint as an admin (user ID of 1). We will change our request so that we can emulate <b>being</b> and admin by prepending 1%0a:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
Content-Length: 4
</pre>
<p class="desc">
The following is a response from the application (note - we get bonus points because as an admin we can retrieve <b> EVERYONE's</b> data):
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
X-Runtime: 0.009111
Connection: close
[{"admin":true,"created_at":"2014-01-23T16:17:10Z","email":"admin@metacorp.com","first_name":
"Admin","id":1,"last_name":"","password":"c93ccd78b2076528346216b3b2f701e6","updated_at":"2014-01-23T16:17:10Z","user_id":1},
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino",
"password":"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"jim@metacorp.com","first_name":"Jim","id":3,"last_name":"Manico","password":
"e1eb29f815193265b57d31bb4d9de140","updated_at":"2014-01-23T16:17:10Z","user_id":3},{"admin":false,
"created_at":"2014-01-23T16:17:10Z","email":"mike@metacorp.com","first_name":"Mike","id":4,"last_name":"McCabe",
"password":"df5d9020fa0f31adc4fd279020f587c8","updated_at":"2014-01-23T16:17:10Z","user_id":4},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"ken@metacorp.com","first_name":"Ken","id":5,"last_name":"Johnson","password":
"67a2faf94e8e71113617d4b72f851bf0","updated_at":"2014-01-23T16:17:10Z","user_id":5},{"admin":null,"created_at":
"2014-03-09T13:58:28Z","email":"test1@test.com","first_name":"test","id":6,"last_name":"test","password":
"05a671c66aefea124cc08b76ea6d30bb","updated_at":"2014-03-09T13:58:28Z","user_id":6},{"admin":null,"created_at":
"2014-03-10T00:13:12Z","email":"test2@test.com","first_name":"test","id":7,"last_name":"test","password":
"91482305bacc71bd52612cce07135b77","updated_at":"2014-03-10T00:13:12Z","user_id":7}]
</pre>
<p><b> Broken Regular Expression SOLUTION:</b></p>
<p class="desc">
There are many things wrong with how we are going about doing this but, for a simple fix, you can anchor the regular expression to reject/ignore newline characters.
</p>
<pre class="ruby">
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
An API token? Interested to see what calls I can make! What are the closing tags for Ruby again?
</div>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,132 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Logic Flaws - Insecure Encryption Re-use
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseCryptoOne" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseCryptoOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application allows employees of Metacorp to choose the <b><i>Remember Me</b></i> option at login, which creates a cookie named <b><i>auth-token</i></b>. The encryption routine used to generate the <b><i>auth-token</i></b> allows the application to extract a user ID. When decrypted, a user ID is extracted and the user is authorized appropriately. This same encryption routine is used elsewhere in the application in a manner such that a clever attacker can generate an auth_token cookie with whatever user ID they prefer and authorize to the application as a different user.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseCryptoTwo" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseCryptoTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
</p>
<pre class="ruby">
# Added a re-usable encryption routine, shouldn't be an issue!
def self.encrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
new_val = aes.update("#{val}") + aes.final
Base64.strict_encode64(new_val).encode('utf-8')
end
def self.decrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
decoded = Base64.strict_decode64("#{val}")
aes.update("#{decoded}") + aes.final
end
</pre>
<p class="desc">
We have placed this code under the lib directory so that we have a re-usable encryption routine. This code is used to generate a user's <b><i>auth_token</b></i> cookie responsible for authorization and access. However, we've also used this same code when encrypting a user's bank account number. This means, a user can enter in any value they would like and will receive it's encrypted equivalent back from the application. Essentially, a user has the ability to generate the auth_token cookie for any user ID and authorize as that user.<br/><br/>
Within the app/models/pay.rb file we have a before hook that will save a user's bank account number as an encrypted value:
</p>
<pre class="ruby">
# callbacks
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
def encrypt_bank_account_num
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
end
</pre>
<p class="desc">
Additionally, we render that encrypted value (purposefully) when the <b><i>show</i></b> action is created within the app/controllers/pay_controller.rb file:
</p>
<pre class="ruby">
def show
respond_to do |format|
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
end
end
</pre>
<p class="desc">
Lastly, we re-use this same routine within the following code is used to create a user's <b><i>auth_token</b></i> cookie upon sign-up or creation (app/models/user.rb):
</p>
<pre class="ruby">
before_create { generate_token(:auth_token) }
def generate_token(column)
begin
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
end while User.exists?(column => self[column])
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseCryptoThree" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseCryptoThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Insecure Encryption Re-use ATTACK:</b></p>
<p class="desc">
Navigate to the <b><i>Pay</b></i> section of the application. Enter your bank account number but use the number <b><i>1</i></b> as your bank account number. Once the information is entered and submitted, you'll see the encrypted value of your bank account number (1) returned. URL encode the special characters (+ and ==) and use this value as your <b><i>auth_token</i></b> cookie. Navigate to your dashboard and you'll have the ability to access administrative functionality.
</p>
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
<p class="desc">
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseCryptoFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
<div class="accordion-body collapse" id="collapseCryptoFour" style="height: 0px;">
<div class="accordion-inner">
My "Remember Me" cookie looks familiar, almost like one of those values you get when you enter your bank account number.
</div>
</div>
</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A6 - Security Misconfiguration
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A5 - Security Misconfiguration
</div>
</div>
<div class="widget-body">
@@ -16,7 +16,7 @@
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
Under progress...
Security misconfiguration can happen at any level of an application stack, including the platform, web server, application server, database, framework, and custom code. Developers and system administrators need to work together to ensure that the entire stack is configured properly. Automated scanners are useful for detecting missing patches, misconfigurations, use of default accounts, unnecessary services, etc.
</div>
</div>
</div>
@@ -30,7 +30,15 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
Under progress...
<p>Rails has quite a few security related configurations. One of which relates to enforcing mass assignment protection.<p>
<p>
<pre class="ruby">
<%= %q{
config.active_record.whitelist_attributes=false
} %>
</pre>
</p>
<p>This configuration forces an application developer to whitelist attributes that can be modified with mass-assignment. When this configuration is set to false <b>any attribute can be mass-assigned.</b></p>
</div>
</div>
</div>
@@ -44,7 +52,14 @@
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
Under progress...
The solution for this issue is quite simple. In your application.rb file set the configuration as follows.
<pre class="ruby">
<%= %q{
config.active_record.whitelist_attributes=true
} %>
</pre>
Once this configuration is updated to true and the application is restarted, any attributes to be mass-assigned will have to be defined as attr_accessible.
</p>
</div>
</div>
</div>
@@ -58,7 +73,7 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
Under progress...
It has to do with mass-assignment, whitelisting and configuration.
</div>
</div>
</div>
@@ -0,0 +1,80 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A5 - Security Misconfiguration
</div>
</div>
<div class="widget-body">
<div id="accordion1" class="accordion no-margin">
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseFive" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
<div class="accordion-inner">
Another one of the Rails security configurations relates to escaping HTML entities in JSON.
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSix" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-bug icon-white">
</i>
Bug
</a>
</div>
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
<div class="accordion-inner">
<p>When the following setting is set to false, HTML entities in JSON response will not be encoded.<p>
<p>
<pre class="ruby">
<%= %q{
ActiveSupport::escape_html_entities_in_json = false
} %>
</pre>
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a href="#collapseSeven" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-lightning icon-white">
</i>
Solution
</a>
</div>
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
<div class="accordion-inner">
<p>Edit the html_entities file at config/initializers/html_entities.rb and set the following to true.</p>
<p><pre class="ruby">
<%= %q{
ActiveSupport::escape_html_entities_in_json = true
} %>
</pre></p>
<p>Once the initializer is edited and the application is restarted, any HTML entities in JSON responses will be encoded.</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
</i>
Hint
</a>
</div>
<div class="accordion-body collapse" id="collapseEight" style="height: 0px;">
<div class="accordion-inner">
Think HTML entities, escaping and initializers.
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,7 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
OWASP Description - Web applications frequently redirect and forward users to other pages and websites, and use untrusted data to determine the destination pages. Without proper validation, attackers can redirect victims to phishing or malware sites, or use forwards to access unauthorized pages.
Applications frequently redirect users to other pages, or use internal forwards in a similar manner. Sometimes the target page is specified in an unvalidated parameter, allowing attackers to choose the destination page.
Detecting unchecked redirects is easy. Look for redirects where you can set the full URL. Unchecked forwards are harder, because they target internal pages.
</p>
<p class="desc">
Railsgoat allows the redirection to the paths previously requested but for which the user did not have access. Following authentication, the user is redirected.
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A2 - Cross-Site Scripting ("XSS")
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A3 - Cross-Site Scripting ("XSS")
</div>
</div>
<div class="widget-body">
@@ -84,7 +84,7 @@
<div class="accordion-inner">
<p class="desc">
Apparently we had some issues rendering people's names with weird formatting or something, I dunno, I think I fixed it by safely encoding html and rendering the necessary content.<br/><br/>
Your <b>Welcome</b>!
You're <b>Welcome</b>!
</p>
</div>
</div>
@@ -0,0 +1,31 @@
<div class="row-fluid">
<h2 align="center">MetaCorp</h2>
<h3 align="center">A GoatGroup Company</h3>
<div class="span12">
<div class="row-fluid">
<div class="span4 offset4">
<div class="signup">
<%= form_tag "forgot_password", :class=> "signup-wrapper" do %>
<div class="header">
<h2>Forgot Password</h2>
<p>Fill out the form below to reset your password.</p>
</div>
<div class="content">
<%= text_field_tag :email, params[:email], {:class => "input input-block-level", :placeholder => "Email"} %>
</div>
<div class="actions">
<%= submit_tag "Reset Password", {:class => "btn btn-info btn-large"} %>
</div>
<div class="clearfix"></div>
<% end %>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,39 @@
<div class="row-fluid">
<h2 align="center">MetaCorp</h2>
<h3 align="center">A GoatGroup Company</h3>
<div class="span12">
<div class="row-fluid">
<div class="span4 offset4">
<!-- TODO -->
<!-- Create a form that allows a user to reset their password -->
<!-- This form is just a placeholder with no working functionality -->
<div class="signup">
<%= form_tag "password_resets", :class=> "signup-wrapper" do %>
<div class="header">
<h2>Create Password</h2>
<p>Fill out the form below to create a new password.</p>
</div>
<div class="content">
<%= hidden_field_tag 'user', Base64.encode64(Marshal.dump(@user)) %>
<%= label_tag "Enter Password" %>
<%= password_field_tag :password, params[:password], {:class => "input input-block-level"} %>
<%= label_tag "Confirm Password" %>
<%= password_field_tag :confirm_password, params[:confirm_password], {:class => "input input-block-level"} %>
</div>
<div class="actions">
<%= submit_tag "Create Password", {:class => "btn btn-danger btn-large"} %>
</div>
<div class="clearfix"></div>
<% end %>
</div>
</div>
</div>
</div>
</div>
+301
View File
@@ -0,0 +1,301 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
<p>
Failed to update.
</p>
</div>
</div>
</div>
<!-- Begin Row-Fluid for Inputs -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe08e;"></span> Direct Deposit
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
<span class="add-on">%</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- End Row-Fluid for Inputs-->
<!-- ###################-->
<!-- Begin Dynamic Table ColSpan Table -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<!-- Begin Widget Header-->
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Accounts
</div>
</div>
<!-- End Widget Header-->
<div class="widget-body">
<div id="dt_example" class="example_alt_pagination">
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
<thead>
<tr>
<th style="width:30%">
Encrypted Bank Account Number
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
<span class="box1">
<span aria-hidden="true" class="icon-question"></span>
</span>
<% end %>
</th>
<th style="width:25%">
Bank Routing Number
</th>
<th style="width:20%">
Percentage of Deposit
</th>
<th style="width:25%">
Action
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="clearfix">
</div>
</div>
</div> <!-- end of widget body-->
</div>
</div>
</div
<!-- End Dynamic Table ColSpan Table -->
<!-- ###################-->
<!-- Begin Row-Fluid for Decryption Input -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe11b;"></span> Decrypt Bank Account Number
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- Row-Fluid for Decryption Input -->
</div>
</div>
<%= javascript_include_tag "jquery.dataTables.js" %>
<script type="text/javascript">
/*
buildDeleteLink accepts a direct deposit ID and builds a link that enables
a user to delete that direct deposit entry
*/
function buildDeleteLink(dd_id){
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
'<i class="icon-trash">'+
'</i></a>'
return link
};
/*
parseDirectDepositInfo accepts the response object and parses the JSON response, then
populates the direct deposit data table.
*/
function parseDirectDepostInfo(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
$.each(msg.user, function(index, val){
$('#data_table').dataTable().fnAddData( [
val.bank_account_num,
val.bank_routing_num,
val.percent_of_deposit,
buildDeleteLink(val.id)
] );
});
};
/*
populateTable will first clear the existing dd table, then call the appropriate
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
with the response from the endpoint in order to populate the data table.
*/
function populateTable() {
$('#data_table').dataTable().fnClearTable();
$.ajax({
url: <%= sanitize(user_pay_path(:format => "json", :user_id => current_user.user_id, :id => current_user.user_id).inspect) %>,
type: "GET",
success: function(response) {
parseDirectDepostInfo(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
};
/*
createDataTable initializes the dd table as a datatable
*/
function createDataTable(){
$('#data_table').dataTable({
"sPaginationType": "full_numbers"
});
};
/*
This function doesn't really work right now but is supposed to offer the user a
"delete confirmation" message
*/
$('.delete-row').click(function () {
var conf = confirm('Continue delete?');
if (conf) $(this).parents('tr').fadeOut(function () {
$(this).remove();
});
return false;
});
/*
decryptShow parses the json response from the application and then renders
a decrypted version of the user's account number
*/
function decryptShow(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
alert("Decrypted Account Number: " + msg.account_num);
};
/*
This function overrides the decrypt buttons (submit button's) native behavior,
allowing an ajax call to be made with the decrypt_form's inputs which is decrypted
server side with a JSON response containing the decrypted value. The decrypted value is
then passed to decryptShow();
*/
$("#decrypt_btn").click(function(event){
var valuesToSubmit = $("#decrypt_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(decrypted_bank_acct_num_user_pay_index_path(:format => "json", :user_id => current_user.user_id).inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
decryptShow(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
/*
This function overrides the dd_form_btn's native behavior in order to submit an ajax request
that updates the user's direct deposit information. Upon success, the populateTable() function
is called in order to update the dataTable on the page to reflect the latest entry.
*/
$("#dd_form_btn").click(function(event) {
var valuesToSubmit = $("#bank_info_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(update_dd_info_user_pay_index_path(:format => "json").inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
populateTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
$("#encrypted_acct_question").click(function(event) {
event.preventDefault();
alert("For your safety your account number is stored encrypted as well as presented to you \nin an encrypted form.\n\n" +
"For your convenience, you can decrypt your bank account number at any time using our\n" +
"conveniently located decryption function."
)
});
/*
Make the sidebar element "Pay" active.
*/
function makeActive(){
$('li[id="pay"]').addClass('active');
};
/*
1) makeActive - Adds the active class to the sidebar element
2) createDataTable - Initializes the dataTable as such
3) populateTable - populates the newly initialized dataTable
*/
$(document).ready(
makeActive,
createDataTable(),
populateTable()
)
</script>
+15 -7
View File
@@ -4,28 +4,36 @@
<div class="span12">
<div class="row-fluid">
<div class="span4 offset4">
<div class="signup">
<%= form_tag "sessions", :class=> "signup-wrapper" do %>
<div class="header">
<div class="header">
<h2>Login</h2>
<p>Fill out the form below to login to your control panel.</p>
</div>
<div class="content">
<%= hidden_field_tag :url, @url%>
<%= label_tag "Email Address" %>
<%= text_field_tag :email, params[:email], {:class => "input input-block-level"} %>
<%= label_tag :password, nil %>
<%= password_field_tag :password, nil, {:class => "input input-block-level"}%>
<%= text_field_tag :email, params[:email], {:class => "input input-block-level", :placeholder=>"Email"} %>
<%= password_field_tag :password, nil, {:class => "input input-block-level", :placeholder=>"Password"}%>
</div>
<div class="actions">
<%= link_to "Forgot Password", forgot_password_path, {:class=>"pull-left"}%><br/>
<%= submit_tag "Login", {:class => "btn btn-info btn-large pull-right"} %>
</div>
<span class="checkbox-wrapper">
<%= check_box_tag :remember_me, 1, params[:remember_me], {:id => "form-terms", :class => "checkbox", :type => "checkbox"} %>
<label class="checkbox-label" for="form-terms"></label> <span class="label-text">Remember</span>
</span>
<div class="clearfix"></div>
<% end %>
</div>
</div>
</div>
@@ -2,7 +2,7 @@
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/info_disclosure/ssn_disclosure"%>
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
</div> <!-- End Span12 -->
</div>
</div>
@@ -10,8 +10,7 @@
<script type="text/javascript">
function makeActive(){
$('li[id="info_dislosure"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="access_control"]').addClass('active');
};
$(document).ready(makeActive);
+5
View File
@@ -10,6 +10,11 @@
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
@@ -2,22 +2,28 @@
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Beginning of span-->
<%= render :partial => "layouts/tutorial/crypto/password_hashing" %>
<%= render :partial => "layouts/tutorial/exposure/password_hashing" %>
</div> <!-- End of span-->
</div>
<!--
<div class="row-fluid">
<div class="span12">
<%#= render :partial => "layouts/tutorial/crypto/ssn" %>
<%= render :partial => "layouts/tutorial/exposure/ssn" %>
</div>
</div>
-->
<div class="row-fluid">
<div class="span12">
<%= render :partial => "layouts/tutorial/exposure/model_attributes_exposure" %>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="crypto"]').addClass('active');
$('li[id="exposure"]').addClass('active');
};
$(document).ready(makeActive);
+38
View File
@@ -0,0 +1,38 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Gauntlet
</div>
</div>
<div class="widget-body">
<p class="desc">
Gauntlt is a tool used for unit testing leveraging third-party tools. We've baked this into Railsgoat so that you can play with it. <br/><br/> To learn more about this tool, please visit their site at: <%= link_to "Gauntlet Github Repository", "https://github.com/gauntlt/gauntlt", {:style =>"color: rgb(181, 121, 158);"} %></p>
<p class="desc">
All *.attack files are contained under the gauntlt_scripts directory. We have provided a simple.attack file that demonstrates the tool works. If errors occur, please submit a bug through our github powered issue tracking system.
</p>
<p class="desc">
To run this tool type this via the command line: <br/><br/>$ gauntlt
</p>
</div>
</div>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function openSub(){
$('li[id="gauntlt"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(openSub);
</script>
@@ -0,0 +1,17 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="insecure_components"]').addClass('active');
};
$(document).ready(makeActive);
</script>
+24
View File
@@ -0,0 +1,24 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="logic_flaws"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(makeActive);
</script>
+5
View File
@@ -5,6 +5,11 @@
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
</div> <!-- End Span12-->
</div>
</div>
</div>
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<h1>Need help logging in?</h1>
<p>
A password reset was requested for your user account.<br>
<br>
To reset your MetaCorp password, simply click on the
following link and follow the instructions:<br>
<br>
<%= link_to "Click here to reset your password", @url %><br>
<br>
If you don't want to change your password, you can ignore this email.
</p>
<p>Thanks, and have a great day!</p>
</body>
</html>
@@ -0,0 +1,13 @@
Need help logging in?
==========================================================
A password reset was requested for your user account.
To reset your MetaCorp password, simply copy the
following link and follow the instructions:
<%= @url %>
If you don't want to change your password, you can ignore this email.
Thanks, and have a great day!
+5 -10
View File
@@ -13,22 +13,17 @@
</div>
<div class="content">
<%= f.label "Email Address" %>
<%= f.text_field :email, {:class => "input input-block-level"} %>
<%= f.text_field :email, {:class => "input input-block-level", :placeholder => "Email"} %>
<%= f.label "First Name" %>
<%= f.text_field :first_name, {:class => "input input-block-level"} %>
<%= f.text_field :first_name, {:class => "input input-block-level", :placeholder => "First Name"} %>
<%= f.label "Last Name" %>
<%= f.text_field :last_name, {:class => "input input-block-level"} %>
<%= f.text_field :last_name, {:class => "input input-block-level", :placeholder => "Last Name"} %>
<div class="control-group">
<%= f.label :password, nil %>
<%= f.password_field :password, {:class => "input input-block-level"}%>
<%= f.password_field :password, {:class => "input input-block-level", :placeholder => "Password"}%>
</div>
<div class="control-group">
<%= f.label :confirm_password %>
<%= f.password_field :password_confirmation, {:class => "input input-block-level"}%>
<%= f.password_field :password_confirmation, {:class => "input input-block-level", :placeholder => "Confirm Password"}%>
</div>
</div>
+3
View File
@@ -28,6 +28,9 @@
<td><%= @user.work_info.bonuses %></td>
<td><%= @user.work_info.years_worked %></td>
<td class="ssn"><%= @user.work_info.SSN %></td>
<!-- Begin Secure Version>-->
<!--<td class="ssn"><%#= @user.work_info.last_four %></td>-->
<!-- End Secure Version -->
<td><%= @user.work_info.DoB %></td>
</tr>
+4 -2
View File
@@ -40,7 +40,7 @@ module Railsgoat
config.filter_parameters += [:password]
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
#config.active_support.escape_html_entities_in_json = false
# Use SQL instead of Active Record's schema dumper when creating the database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
@@ -51,7 +51,7 @@ module Railsgoat
# This will create an empty whitelist of attributes available for mass-assignment for all models
# in your app. As such, your models will need to explicitly whitelist or blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
config.active_record.whitelist_attributes = true
config.active_record.whitelist_attributes = false
# Enable the asset pipeline
config.assets.enabled = true
@@ -61,5 +61,7 @@ module Railsgoat
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
I18n.config.enforce_available_locales = false
end
end
+8
View File
@@ -29,12 +29,20 @@ Railsgoat::Application.configure do
# with SQLite, MySQL, and PostgreSQL)
config.active_record.auto_explain_threshold_in_seconds = 0.5
# Tired of caching causing issues
config.middleware.delete Rack::ETag
# Do not compress assets
config.assets.compress = false
# Expands the lines which load the assets
config.assets.debug = true
# ActionMailer settings for email support
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { :address => "localhost", :port => 1025 }
config.action_mailer.default_url_options = { :host => "localhost:3000" }
config.middleware.insert_before(
Rack::Lock, Rack::LiveReload,
:min_delay => 500,
+3
View File
@@ -0,0 +1,3 @@
ACCESS_TOKEN_SALT = "S4828341189aefiasd#ASDF"
RG_IV = "PPKLKAJDKGHALDJL482823458028"
+1
View File
@@ -0,0 +1 @@
ActiveSupport::JSON::Encoding::escape_html_entities_in_json = false
+5
View File
@@ -0,0 +1,5 @@
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
else
KEY = "123456789101112123456789101112123456789101112"
end
+22 -3
View File
@@ -3,9 +3,12 @@ Railsgoat::Application.routes.draw do
get "login" => "sessions#new"
get "signup" => "users#new"
get "logout" => "sessions#destroy"
match "forgot_password" => "password_resets#forgot_password"
get "password_resets" => "password_resets#confirm_token"
post "password_resets" => "password_resets#reset_password"
resources :sessions do
end
resources :users do
@@ -31,6 +34,13 @@ resources :users do
resources :messages do
end
resources :pay do
collection do
post "update_dd_info"
post "decrypted_bank_acct_num"
end
end
end
get "download" => "benefit_forms#download"
@@ -45,14 +55,17 @@ resources :tutorials do
get "insecure_dor"
get "csrf"
get "misconfig"
get "crypto"
get "exposure"
get "url_access"
get "insecure_components"
get "access_control"
get "ssl_tls"
get "redirects"
get "guard"
get "info_disclosure"
get "mass_assignment"
get "constantize"
get "gauntlt"
get "logic_flaws"
end
end
@@ -77,6 +90,12 @@ resources :dashboard do
end
end
namespace :api, defaults: {format: 'json'} do
namespace :v1 do
resources :users
end
end
root :to => "sessions#new"
@@ -0,0 +1,5 @@
class AddEncryptedSsnToWorkInfos < ActiveRecord::Migration
def change
add_column :work_infos, :encrypted_ssn, :binary
end
end
@@ -0,0 +1,10 @@
class CreateKeyManagements < ActiveRecord::Migration
def change
create_table :key_managements do |t|
t.string :iv
t.integer :user_id
t.timestamps
end
end
end
@@ -0,0 +1,5 @@
class AddAuthTokenToUsers < ActiveRecord::Migration
def change
add_column :users, :auth_token, :string
end
end
+12
View File
@@ -0,0 +1,12 @@
class CreatePays < ActiveRecord::Migration
def change
create_table :pays do |t|
t.integer :user_id
t.string :bank_account_num
t.string :bank_routing_num
t.integer :percent_of_deposit
t.timestamps
end
end
end
+19 -1
View File
@@ -11,13 +11,20 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20131011180207) do
ActiveRecord::Schema.define(:version => 20140315002730) do
create_table "benefits", :force => true do |t|
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "key_managements", :force => true do |t|
t.string "iv"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "messages", :force => true do |t|
t.integer "creator_id"
t.integer "receiver_id"
@@ -37,6 +44,15 @@ ActiveRecord::Schema.define(:version => 20131011180207) do
t.datetime "updated_at", :null => false
end
create_table "pays", :force => true do |t|
t.integer "user_id"
t.string "bank_account_num"
t.string "bank_routing_num"
t.integer "percent_of_deposit"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "performances", :force => true do |t|
t.integer "user_id"
t.date "date_submitted"
@@ -76,6 +92,7 @@ ActiveRecord::Schema.define(:version => 20131011180207) do
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "auth_token"
end
create_table "work_infos", :force => true do |t|
@@ -87,6 +104,7 @@ ActiveRecord::Schema.define(:version => 20131011180207) do
t.date "DoB"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.binary "encrypted_ssn"
end
end
+20 -8
View File
@@ -33,8 +33,8 @@ users = [
{
:email => "mike@metacorp.com",
:admin => false,
:password => "motorcross1445",
:password_confirmation => "motorcross1445",
:password => "motocross1445",
:password_confirmation => "motocross1445",
:first_name => "Mike",
:last_name => "McCabe",
:user_id =>4
@@ -289,12 +289,6 @@ schedule.each do |event|
sched.save
end
work_info.each do |wi|
info = WorkInfo.new(wi.reject {|k| k == :user_id})
info.user_id = wi[:user_id]
info.save
end
performance.each do |perf|
p = Performance.new(perf.reject {|k| k == :user_id})
p.user_id = perf[:user_id]
@@ -306,3 +300,21 @@ messages.each do |message|
m.creator_id = message[:creator_id]
m.save
end
work_info.each do |wi|
info = WorkInfo.new(wi.reject {|k| k == :user_id } )
info.user_id = wi[:user_id]
info.save
end
=begin
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
=end
+36
View File
@@ -0,0 +1,36 @@
module Encryption
# Added a re-usable encryption routine, shouldn't be an issue!
def self.encrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
new_val = aes.update("#{val}") + aes.final
Base64.strict_encode64(new_val).encode('utf-8')
end
def self.decrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
decoded = Base64.strict_decode64("#{val}")
aes.update("#{decoded}") + aes.final
end
# Should be able to just re-use the same key we already have!
def self.key
raise "Key Missing" if !(KEY)
KEY
end
def self.iv
RG_IV
end
def self.cipher_type
'aes-256-cbc'
end
end
@@ -0,0 +1,6 @@
=begin require 'spec_helper'
describe Api::V1::UsersController do
end
=end
@@ -1,5 +1 @@
require 'spec_helper'
describe MessagesController do
end
@@ -0,0 +1 @@
require 'spec_helper'
+6
View File
@@ -0,0 +1,6 @@
=begin require 'spec_helper'
describe PayController do
end
=end
+16
View File
@@ -0,0 +1,16 @@
=begin require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the Api::V1::UsersHelper. For example:
#
# describe Api::V1::UsersHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
describe Api::V1::UsersHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
=end
-14
View File
@@ -1,15 +1 @@
require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the MessagesHelper. For example:
#
# describe MessagesHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
describe MessagesHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
@@ -0,0 +1,16 @@
=begin require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the PasswordResetsHelper. For example:
#
# describe PasswordResetsHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
describe PasswordResetsHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
=end
+16
View File
@@ -0,0 +1,16 @@
=begin require 'spec_helper'
# Specs in this file have access to a helper object that includes
# the PayHelper. For example:
#
# describe PayHelper do
# describe "string concat" do
# it "concats two strings with spaces" do
# expect(helper.concat_strings("this","that")).to eq("this that")
# end
# end
# end
describe PayHelper do
pending "add some examples to (or delete) #{__FILE__}"
end
=end
+1
View File
@@ -0,0 +1 @@
require "spec_helper"
+1
View File
@@ -0,0 +1 @@
require 'spec_helper'
-4
View File
@@ -1,5 +1 @@
require 'spec_helper'
describe Message do
pending "add some examples to (or delete) #{__FILE__}"
end
+6
View File
@@ -0,0 +1,6 @@
=begin require 'spec_helper'
describe Pay do
pending "add some examples to (or delete) #{__FILE__}"
end
=end
+48 -11
View File
@@ -15,20 +15,19 @@ def verifying_fixed?
******************************************************************************
You are running the RailsGoat Capybara Specs in Training mode. These specs
are supposed to fail, indicating vulnerabilities exist. They contain
spoilers, so do not read the code in spec/vulnerabilities if your goal is to
learn more about patching the vulnerabilities. You should fix the
vulnerabilities in the application in order to get these specs to pass**.
You can use them to measure your progress.
are supposed to fail, indicating vulnerabilities exist. They contain spoilers,
so do not read the code in spec/vulnerabilities if your goal is to learn more
about patching the vulnerabilities. You should fix the vulnerabilities in the
application in order to get these specs to pass**. You can use them to measure
your progress.
These same specs will pass if you set the #{maintainer_env_name} ENV
variable.
These same specs will pass if you set the #{maintainer_env_name} ENV variable.
**NOTE: The RSpec pending feature is used to toggle the outcome of these
specs between Training mode and RailsGoat Maintainer mode, so when the
vulnerabilities are removed, these specs actually won't 'pass' but go into
a 'pending' state.
**NOTE: The RSpec pending feature is used to toggle the outcome of these specs
between Training mode and RailsGoat Maintainer mode. When the vulnerabilities
are removed, the specs will not "pass," but rather go into a "pending" state.
******************************************************************************
NOTICE
$displayed_spec_notice = true
end
@@ -43,3 +42,41 @@ def login(user)
end
click_on 'Login'
end
##Hack to fix PhantomJS errors on Mavericks - https://gist.github.com/ericboehs/7125105
module Capybara::Poltergeist
class Client
private
def redirect_stdout
prev = STDOUT.dup
prev.autoclose = false
$stdout = @write_io
STDOUT.reopen(@write_io)
prev = STDERR.dup
prev.autoclose = false
$stderr = @write_io
STDERR.reopen(@write_io)
yield
ensure
STDOUT.reopen(prev)
$stdout = STDOUT
STDERR.reopen(prev)
$stderr = STDERR
end
end
end
class WarningSuppressor
class << self
def write(message)
if message =~ /QFont::setPixelSize: Pixel size <= 0/ || message =~/CoreText performance note:/ || message =~/Method userSpaceScaleFactor in class NSView/ then 0 else puts(message);1;end
end
end
end
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, phantomjs_logger: WarningSuppressor, timeout: 60)
end
Capybara.javascript_driver = :poltergeist
@@ -0,0 +1,6 @@
=begin require 'spec_helper'
describe "password_resets/new.html.erb" do
pending "add some examples to (or delete) #{__FILE__}"
end
=end
@@ -1,6 +1,6 @@
require 'spec_helper'
feature 'sensitive information disclosure' do
feature 'sensitive data exposure' do
before do
UserFixture.reset_all_users
@normal_user = UserFixture.normal_user