Merged master into branch/clean it up/green test run
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -1,78 +1,110 @@
|
||||
## Getting Started ##
|
||||
#### With Ruby, Rubygems, Git, and Bundler installed ####
|
||||
# RailsGoat [](https://travis-ci.org/OWASP/railsgoat) [](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
|
||||
|
||||
[](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:
|
||||
|
||||
[](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
@@ -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/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
module Api::V1::UsersHelper
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
module PasswordResetsHelper
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
module PayHelper
|
||||
end
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
class KeyManagement < ActiveRecord::Base
|
||||
attr_accessible :iv, :user_id
|
||||
belongs_to :work_info
|
||||
belongs_to :user
|
||||
|
||||
end
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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=""></span>
|
||||
</div>
|
||||
Pay
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -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=""></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=""></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=""></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=""></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=""></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=""></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=""></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=""></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=""></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=""></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=""></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=""></span> A3 - Broken Authentication and Session Management - Lack of Password Complexity
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></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=""></span> A3 - Broken Authentication and Session Management - Username/Pass Enumeration
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></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=""></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=""></span> A5 - Cross Site Request Forgery (CSRF)
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></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=""></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
-1
@@ -1,7 +1,7 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A7 - Insecure Cryptographic Storage - Password Storage
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></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=""></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("<td class="ssn"><%= @user.work_info.last_four %></td>") %>
|
||||
</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>
|
||||
+11
-28
@@ -1,7 +1,7 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Information Disclosure (Sensitive)
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></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 don’t focus on ensuring their components/libraries are up to date. In many cases, the developers don’t 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 don’t 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=""></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=""></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=""></span> A6 - Security Misconfiguration
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></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=""></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=""></span> A2 - Cross-Site Scripting ("XSS")
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></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>
|
||||
@@ -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=""></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=""></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=""></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>
|
||||
@@ -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
-3
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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=""></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>
|
||||
@@ -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,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!
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ACCESS_TOKEN_SALT = "S4828341189aefiasd#ASDF"
|
||||
|
||||
RG_IV = "PPKLKAJDKGHALDJL482823458028"
|
||||
@@ -0,0 +1 @@
|
||||
ActiveSupport::JSON::Encoding::escape_html_entities_in_json = false
|
||||
@@ -0,0 +1,5 @@
|
||||
if Rails.env.production?
|
||||
# Specify env variable/location/etc. to retrieve key from
|
||||
else
|
||||
KEY = "123456789101112123456789101112123456789101112"
|
||||
end
|
||||
+22
-3
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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'
|
||||
@@ -0,0 +1,6 @@
|
||||
=begin require 'spec_helper'
|
||||
|
||||
describe PayController do
|
||||
|
||||
end
|
||||
=end
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
require "spec_helper"
|
||||
@@ -0,0 +1 @@
|
||||
require 'spec_helper'
|
||||
@@ -1,5 +1 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe Message do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
=begin require 'spec_helper'
|
||||
|
||||
describe Pay do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
=end
|
||||
@@ -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
-1
@@ -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
|
||||
Reference in New Issue
Block a user