Merge branch 'pr-136'

This commit is contained in:
cktricky
2014-07-11 06:28:54 -04:00
105 changed files with 3192 additions and 3288 deletions
+9 -4
View File
@@ -1,4 +1,9 @@
if [ -f "$rvm_path/scripts/rvm" ] && [ -f ".rvmrc" ]; then
source "$rvm_path/scripts/rvm"
source ".rvmrc"
fi
if [ -f "${rvm_path}/scripts/rvm" ]; then
source "${rvm_path}/scripts/rvm"
if [ -f ".rvmrc" ]; then
source ".rvmrc"
elif [ -f ".ruby-version" ] && [ -f ".ruby-gemset" ]; then
rvm use `cat .ruby-version`@`cat .ruby-gemset`
fi
fi
+1
View File
@@ -0,0 +1 @@
railsgoat
+1
View File
@@ -0,0 +1 @@
2.1.2
-1
View File
@@ -1 +0,0 @@
rvm use 2.1.2@railsgoat --create
+1 -1
View File
@@ -1,6 +1,6 @@
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
+4 -22
View File
@@ -4,13 +4,10 @@ RailsGoat is a vulnerable version of the Ruby on Rails Framework. It includes vu
## Getting Started
To begin, install the Ruby Version Manager (RVM):
```
$ curl -L https://get.rvm.io | bash -s stable --autolibs=3 --ruby=1.9.3
$ rvm use 2.1.2@railsgoat --create # https://rvm.io/
$ curl -L https://get.rvm.io | bash -s stable --autolibs=3 --ruby=2.1.2
```
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
```
Navigate into the directory and accept the notice by typing `yes`:
```
****************************************************************************************************
* NOTICE *
****************************************************************************************************
* RVM has encountered a new or modified .rvmrc file in the current directory, this is a shell *
* script and therefore may contain any shell commands. *
* *
* Examine the contents of this file carefully to be sure the contents are safe before trusting it! *
* Do you wish to trust '/path/to/railsgoat/.rvmrc'? *
* Choose v[view] below to view the contents *
****************************************************************************************************
y[es], n[o], v[iew], c[cancel]>
```
Install the project dependencies:
Navigate into the directory and install the dependencies:
```
$ bundle install
@@ -52,7 +34,7 @@ Initialize the database:
$ rake db:setup
```
Start the WEBrick HTTP Server:
Start the Thin web server:
```
$ rails server
@@ -101,7 +83,7 @@ Conversion to the OWASP Top Ten 2013 completed in November, 2013.
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:
+4 -6
View File
@@ -1,8 +1,7 @@
class AdminController < ApplicationController
before_filter :administrative, :if => :admin_param
skip_before_filter :has_info
def dashboard
end
@@ -27,14 +26,14 @@ class AdminController < ApplicationController
@users = User.all
render :partial => "layouts/admin/get_all_users"
end
def get_user
@user = User.find_by_id(params[:admin_id].to_s)
arr = ["true", "false"]
@admin_select = @user.admin ? arr : arr.reverse
render :partial => "layouts/admin/get_user"
end
def update_user
user = User.find_by_id(params[:admin_id])
if user
@@ -48,7 +47,7 @@ class AdminController < ApplicationController
format.json { render :json => { :msg => message ? "success" : "failure"} }
end
end
def delete_user
user = User.find_by_user_id(params[:admin_id])
if user && !(current_user.user_id == user.user_id)
@@ -67,5 +66,4 @@ class AdminController < ApplicationController
def admin_param
params[:admin_id] != '1'
end
end
@@ -1,5 +1,4 @@
class Api::V1::MobileController < ApplicationController
skip_before_filter :authenticated
before_filter :mobile_request?
@@ -30,5 +29,4 @@ class Api::V1::MobileController < ApplicationController
request.user_agent =~ /ios|android/i
end
end
end
+51 -54
View File
@@ -1,57 +1,54 @@
class Api::V1::UsersController < ApplicationController
skip_before_filter :authenticated
before_filter :valid_api_token
before_filter :extrapolate_user
respond_to :json
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
def show
respond_with @user.as_json
end
private
skip_before_filter :authenticated
before_filter :valid_api_token
before_filter :extrapolate_user
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /(.*?)-(.*)/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
def check_hash(id, hash)
digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
@clean_token = CGI::unescape(token)
end
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
@user = User.find_by_id(@clean_token.split("-").first)
end
respond_to :json
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
def show
respond_with @user.as_json
end
private
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /(.*?)-(.*)/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
def check_hash(id, hash)
digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
@clean_token = CGI::unescape(token)
end
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
@user = User.find_by_id(@clean_token.split("-").first)
end
end
+1 -3
View File
@@ -1,5 +1,4 @@
class ApplicationController < ActionController::Base
before_filter :authenticated, :has_info, :create_analytic
helper_method :current_user, :is_admin?, :sanitize_font
@@ -10,7 +9,7 @@ class ApplicationController < ActionController::Base
def current_user
@current_user ||= (
User.find_by_auth_token(cookies[:auth_token].to_s) ||
User.find_by_auth_token(cookies[:auth_token].to_s) ||
User.find_by_user_id(session[:user_id].to_s)
)
end
@@ -53,5 +52,4 @@ class ApplicationController < ActionController::Base
css
# css if css.match(/\A[0-9]+([\%]|pt)\z/)
end
end
+10 -14
View File
@@ -1,12 +1,11 @@
class BenefitFormsController < ApplicationController
def index
@benefits = Benefits.new
end
def download
begin
begin
path = params[:name]
file = params[:type].constantize.new(path)
send_file file, :disposition => 'attachment'
@@ -14,7 +13,7 @@ class BenefitFormsController < ApplicationController
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
def upload
file = params[:benefits][:upload]
if file
@@ -22,23 +21,22 @@ class BenefitFormsController < ApplicationController
Benefits.save(file, params[:benefits][:backup])
else
flash[:error] = "Something went wrong"
end
end
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
=begin
=begin
# More secure version
def download
file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}
begin
begin
if file_assoc.has_key?(params[:name].to_s)
path = Rails.root.join('public', 'docs', file_assoc[params[:name].to_s])
if params[:type] == "File"
file = params[:type].constantize.new(path)
file = params[:type].constantize.new(path)
send_file file, :disposition => 'attachment'
end
else
end
else
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
send_file file, :disposition => 'attachment'
end
@@ -46,7 +44,5 @@ class BenefitFormsController < ApplicationController
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
=end
=end
end
+1 -3
View File
@@ -1,7 +1,6 @@
class DashboardController < ApplicationController
skip_before_filter :has_info
def home
@user = current_user
@@ -10,5 +9,4 @@ class DashboardController < ApplicationController
cookies[:font] = params[:font]
end
end
end
-1
View File
@@ -33,5 +33,4 @@ class MessagesController < ApplicationController
end
end
end
end
+1 -1
View File
@@ -1,5 +1,5 @@
class PaidTimeOffController < ApplicationController
def index
@pto = current_user.paid_time_off
@schedule = Schedule.new
@@ -1,7 +1,6 @@
class PasswordResetsController < ApplicationController
skip_before_filter :authenticated
def reset_password
user = Marshal.load(Base64.decode64(params[:user])) unless params[:user].nil?
+8 -9
View File
@@ -1,28 +1,28 @@
class PayController < ApplicationController
def index
end
def update_dd_info
msg = false
pay = Pay.new(
:bank_account_num => params[:bank_account_num],
:bank_routing_num => params[:bank_routing_num],
:bank_account_num => params[:bank_account_num],
:bank_routing_num => params[:bank_routing_num],
:percent_of_deposit => params[:dd_percent]
)
pay.user_id = current_user.user_id
msg = true if pay.save!
msg = true if pay.save!
respond_to do |format|
format.json {render :json => {:msg => msg } }
end
end
def show
respond_to do |format|
format.json { render :json => {:user => current_user.pay.as_json} }
end
end
def destroy
pay = Pay.find_by_id(params[:id])
if pay.present? and pay.destroy
@@ -32,12 +32,11 @@ class PayController < ApplicationController
end
redirect_to user_pay_index_path
end
def decrypted_bank_acct_num
decrypted = Encryption.decrypt_sensitive_value(params[:value_to_decrypt])
respond_to do |format|
format.json {render :json => {:account_num => decrypted || "No Data" }}
end
end
end
+1 -2
View File
@@ -1,7 +1,6 @@
class PerformanceController < ApplicationController
def index
@perf = current_user.performance
end
end
+1 -2
View File
@@ -1,7 +1,6 @@
class RetirementController < ApplicationController
def index
@info = current_user.retirement
end
end
+9 -9
View File
@@ -1,7 +1,8 @@
class ScheduleController < ApplicationController
def create
message = false
if params[:schedule][:event_type] == "pto"
sched = Schedule.new(params[:schedule])
sched.date_begin, sched.date_end = format_schedule_date(params[:date_range1])
@@ -11,12 +12,12 @@ class ScheduleController < ApplicationController
message = true
end
end
respond_to do |format|
format.json {render :json => {:msg => message ? "success" : "failure" }}
end
end
def get_pto_schedule
begin
schedules = current_user.paid_time_off.schedule
@@ -29,17 +30,17 @@ class ScheduleController < ApplicationController
hash[:end] = s[:date_end]
jfs << hash
end
rescue
rescue
end
respond_to do |format|
format.json do
render :json => jfs.to_json
end
end
end
end
private
# Returns a two part array consisting of dates
# First value is the begin date and the second is the end date
def format_schedule_date(date_array)
@@ -50,10 +51,9 @@ class ScheduleController < ApplicationController
date = Date.strptime(s.strip, '%m/%d/%Y')
vals <<(date)
end
rescue ArgumentError
rescue ArgumentError
return []
end
return vals
end
end
+24 -26
View File
@@ -1,41 +1,39 @@
class SessionsController < ApplicationController
skip_before_filter :has_info
skip_before_filter :authenticated, :only => [:new, :create]
def new
@url = params[:url]
redirect_to home_dashboard_index_path if current_user
@url = params[:url]
redirect_to home_dashboard_index_path if current_user
end
def create
path = params[:url].present? ? params[:url] : home_dashboard_index_path
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
path = params[:url].present? ? params[:url] : home_dashboard_index_path
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
rescue Exception => e
end
if user
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token if User.where(:user_id => user.user_id).exists?
else
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
end
redirect_to path
end
if user
if params[:remember_me]
cookies.permanent[:auth_token] = user.auth_token if User.where(:user_id => user.user_id).exists?
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
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
end
redirect_to path
else
# Removed this code, just doesn't seem specific enough!
# flash[:error] = "Either your username and password is incorrect"
flash[:error] = e.message
render "new"
end
end
def destroy
cookies.delete(:auth_token)
reset_session
redirect_to root_path
end
end
+39 -41
View File
@@ -1,41 +1,40 @@
class TutorialsController < ApplicationController
skip_before_filter :has_info
skip_before_filter :authenticated
def index
end
def credentials
render :partial => "layouts/tutorial/credentials/creds"
end
def show
render "injection"
end
def injection
end
def xss
@code = %{
<li style="color: #FFFFFF">
<!--
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
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
}
@code = %{
<li style="color: #FFFFFF">
<!--
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
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
}
end
def broken_auth
end
def insecure_dor
end
def csrf
@meta_code_bad = %{<%#= csrf_meta_tags %> <!-- <~ What is this for? I hear it helps w/ JS and Sea-surfing.....whatevz -->}
@meta_code_good = %{<%= csrf_meta_tags %> }
@@ -45,20 +44,20 @@ class TutorialsController < ApplicationController
event.preventDefault();
$.ajax(\{
url: "/example",
data: valuesToSubmit,
type: "POST",
success: function(response) \{
alert('success!');
},
error: function(event) \{
alert('failure!');
\}
\});
data: valuesToSubmit,
type: "POST",
success: function(response) \{
alert('success!');
},
error: function(event) \{
alert('failure!');
\}
\});
\});
\} }
end
def misconfig
end
@@ -67,33 +66,32 @@ class TutorialsController < ApplicationController
def access_control
end
def crypto
end
def url_access
end
def ssl_tls
end
def redirects
end
def guard
end
def logic_flaws
end
def mass_assignment
end
def guantlt
end
def metaprogramming
end
end
-3
View File
@@ -1,9 +1,7 @@
class UsersController < ApplicationController
skip_before_filter :has_info
skip_before_filter :authenticated, :only => [:new, :create]
def new
@user = User.new
end
@@ -52,5 +50,4 @@ class UsersController < ApplicationController
redirect_to user_account_settings_path(:user_id => current_user.user_id)
end
end
end
+3 -5
View File
@@ -1,8 +1,7 @@
class WorkInfoController < ApplicationController
def index
@user = User.find_by_user_id(params[:user_id])
if !(@user) || @user.admin
if !(@user) || @user.admin
flash[:error] = "Sorry, no user with that user id exists"
redirect_to home_dashboard_index_path
end
@@ -12,11 +11,10 @@ class WorkInfoController < ApplicationController
# More secure version
def index
@user = current_user
if !(@user) || @user.admin
if !(@user) || @user.admin
flash[:error] = "Apologies, looks like something went wrong"
redirect_to home_dashboard_index_path
end
end
=end
=end
end
View File
View File
+7 -7
View File
@@ -4,16 +4,16 @@ class Analytics < ActiveRecord::Base
scope :hits_by_ip, ->(ip,col="*") { select("#{col}").where(:ip_address => ip).order("id DESC")}
def self.count_by_col(col)
calculate(:count, col)
calculate(:count, col)
end
def self.parse_field(field)
valid_fields = ["ip_address", "referrer", "user_agent"]
valid_fields = ["ip_address", "referrer", "user_agent"]
if valid_fields.include?(field)
field
else
"1"
end
if valid_fields.include?(field)
field
else
"1"
end
end
end
+30 -31
View File
@@ -1,38 +1,37 @@
class Benefits < ActiveRecord::Base
attr_accessor :backup
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "wb+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
def self.make_backup(file, data_path, 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}") }
end
end
attr_accessor :backup
=begin
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "wb+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
def self.make_backup(file, data_path, 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}") }
end
end
=begin
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}"
end
=end
=end
def self.silence_streams(*streams)
on_hold = streams.collect { |stream| stream.dup }
streams.each do |stream|
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
stream.sync = true
end
yield
ensure
streams.each_with_index do |stream, i|
stream.reopen(on_hold[i])
end
end
def self.silence_streams(*streams)
on_hold = streams.collect { |stream| stream.dup }
streams.each do |stream|
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
stream.sync = true
end
yield
ensure
streams.each_with_index do |stream, i|
stream.reopen(on_hold[i])
end
end
end
-1
View File
@@ -2,5 +2,4 @@ class KeyManagement < ActiveRecord::Base
attr_accessible :iv, :user_id
belongs_to :work_info
belongs_to :user
end
+2 -3
View File
@@ -6,13 +6,12 @@ class PaidTimeOff < ActiveRecord::Base
def sick_days_remaining
self.sick_days_earned - self.sick_days_taken
end
def pto_days_remaining
self.pto_earned - self.pto_taken
end
def sick_days_taken_percentage
result = self.sick_days_taken.to_f / self.sick_days_earned.to_f * 100.0
end
end
+6 -8
View File
@@ -1,25 +1,23 @@
class Pay < ActiveRecord::Base
# mass-assignable attributes
attr_accessible :bank_account_num, :bank_routing_num, :percent_of_deposit
# Associations
belongs_to :user
belongs_to :user
# Validations
validates :bank_account_num, presence: true
validates :bank_routing_num, presence: true
validates :percent_of_deposit, presence: true
# callbacks
before_save :encrypt_bank_account_num
def as_json
super(only: [:bank_account_num, :bank_routing_num, :percent_of_deposit, :id])
end
def encrypt_bank_account_num
self.bank_account_num = Encryption.encrypt_sensitive_value(self.bank_account_num)
end
end
+2 -2
View File
@@ -1,7 +1,7 @@
class Performance < ActiveRecord::Base
attr_accessible :comments, :date_submitted, :reviewer, :score
belongs_to :user
belongs_to :user
def reviewer_name
u = User.find_by_id(self.reviewer)
u.full_name if u.respond_to?('fullname')
+1 -1
View File
@@ -1,6 +1,6 @@
class Schedule < ActiveRecord::Base
attr_accessible :date_begin, :date_end, :event_desc, :event_name, :event_type
belongs_to :paid_time_off
validates_presence_of :date_begin, :date_end, :event_desc, :event_name, :event_type
end
+21 -23
View File
@@ -1,7 +1,6 @@
require 'encryption'
class User < ActiveRecord::Base
attr_accessible :email, :admin, :first_name, :last_name, :user_id, :password, :password_confirmation
validates :password, :presence => true,
:confirmation => true,
@@ -13,7 +12,7 @@ class User < ActiveRecord::Base
:confirmation => true,
:if => :password,
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
=end
=end
validates_presence_of :email
validates_uniqueness_of :email
validates_format_of :email, :with => /.+@.+\..+/i
@@ -37,11 +36,11 @@ class User < ActiveRecord::Base
#work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
def full_name
"#{self.first_name} #{self.last_name}"
end
=begin
# Instead of the entire user object being returned, we can use this to filter.
def as_json
@@ -49,20 +48,20 @@ class User < ActiveRecord::Base
end
=end
private
private
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
raise "#{email} doesn't exist!" if !(user)
if user.password == Digest::MD5.hexdigest(password)
auth = user
else
raise "Incorrect Password!"
end
return auth
end
auth = nil
user = find_by_email(email)
raise "#{email} doesn't exist!" if !(user)
if user.password == Digest::MD5.hexdigest(password)
auth = user
else
raise "Incorrect Password!"
end
return auth
end
=begin
# More secure version, still lacking a decent hashing routine, this is for timing attack prevention
def self.authenticate(email, password)
@@ -71,18 +70,18 @@ private
return user
else
raise "Incorrect username or password"
end
end
end
=end
=end
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
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
end
end
end
def hash_password
unless @skip_hash_password == true
if password.present?
@@ -90,11 +89,10 @@ private
end
end
end
def generate_token(column)
begin
self[column] = Encryption.encrypt_sensitive_value(self.user_id)
end while User.exists?(column => self[column])
end
end
+18 -20
View File
@@ -3,42 +3,40 @@ class WorkInfo < ActiveRecord::Base
belongs_to :user
has_one :key_management, :foreign_key => :user_id, :primary_key => :user_id, :dependent => :destroy
#before_save :encrypt_ssn
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
"***-**-" << self.decrypt_ssn[-4,4]
end
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def key
raise "Key Missing" if !(KEY)
KEY
end
def iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def cipher_type
'aes-256-cbc'
end
end
+43 -53
View File
@@ -1,70 +1,60 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
User information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
<p>
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="&#xe071;"></span>
Manage Users
</div>
</div>
<div id="userDataTable" class="widget-body">
</div> <!-- End widget-body-->
</div> <!-- End widget header-->
</div>
</div>
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">Success!</h4>
<p>User information successfully updated.</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">Error!</h4>
<p>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="&#xe071;"></span>Manage Users
</div>
</div>
<div id="userDataTable" class="widget-body">
</div> <!-- End widget-body-->
</div> <!-- End widget header-->
</div>
</div>
</div>
</div>
<%= javascript_include_tag "jquery.dataTables.js"%>
<script type="text/javascript">
function dataTablePagination(){
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
};
function makeActive(){
$('li[id="admin"]').addClass('active');
$('li[id="admin"]').addClass('active');
};
function loadTable(){
$("#userDataTable").load("/admin/"+ <%= params[:admin_id] %> + "/get_all_users")
$("#userDataTable").load("/admin/"+ <%= params[:admin_id] %> + "/get_all_users")
};
$(document).ready(
makeActive,
dataTablePagination(),
loadTable()
makeActive,
dataTablePagination(),
loadTable()
);
</script>
+117 -125
View File
@@ -1,134 +1,126 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="main-container">
<div class="row-fluid">
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</div>
</div>
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Dental Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</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="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
<div>
<h2>Upload file</h2>
<%= 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 -->
<div>
<div>
<%= hidden_field "benefits", "backup", :value => false %>
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="icon-plus icon-white"></i>
<span>Add file</span>
<%= f.file_field :upload %>
</span>
<button id="start_upload" type="submit" class="btn btn-primary start">
<i class="icon-upload icon-white"></i>
<span><%= t('fileupload.start_upload') %></span>
</button>
<br/><br/><span class="filename">Nothing selected</span>
</div>
<div class="span5">
<!-- The global progress bar -->
<div class="progress progress-success progress-striped active fade">
<div class="bar" style="width:0%;"></div>
</div>
</div>
</div>
<!-- The loading indicator is shown during image processing -->
<div class="fileupload-loading"></div>
<br>
<!-- 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>
<% end %>
<div id="progress">
<div class="bar" style="width: 0%;"></div>
</div>
<div class="row-fluid">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Health_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</div>
</div>
<div class="span4">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe023;"></span> Dental Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
Click on PDF to download<br/><br/>
<%= link_to download_path(:type => "File", :name => "public/docs/Dental_n_Stuff.pdf") do %>
<div class="doc-icons-container">
<div class="icon light-blue hidden-tablet">
<span class="fs1 doc-icon" aria-hidden="true" data-icon="&#xe1b2;"></span>
<span class="doc-type">
PDF
</span>
</div>
</div>
<% end %>
</div>
<!-- End Widget Body -->
</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="&#xe023;"></span> Health Insurance
</div>
</div>
<!-- Begin Widget Body -->
<div class="widget-body">
<div>
<h2>Upload file</h2>
<%= 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 -->
<div>
<div>
<%= hidden_field "benefits", "backup", :value => false %>
<!-- The fileinput-button span is used to style the file input field as button -->
<span class="btn btn-success fileinput-button">
<i class="icon-plus icon-white"></i>
<span>Add file</span>
<%= f.file_field :upload %>
</span>
<button id="start_upload" type="submit" class="btn btn-primary start">
<i class="icon-upload icon-white"></i>
<span><%= t('fileupload.start_upload') %></span>
</button>
<br/><br/><span class="filename">Nothing selected</span>
</div>
<div class="span5">
<!-- The global progress bar -->
<div class="progress progress-success progress-striped active fade">
<div class="bar" style="width:0%;"></div>
</div>
</div>
</div>
<!-- The loading indicator is shown during image processing -->
<div class="fileupload-loading"></div>
<br>
<!-- 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>
<% end %>
<div id="progress">
<div class="bar" style="width: 0%;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$(function() {
$("#benefits_upload").change(function (){
var fileName = $(this).val();
$(".filename").html(fileName);
});
});
function makeActive(){
$('li[id="benefit_forms"]').addClass('active');
};
$(document).ready(
makeActive
);
$(function() {
$("#benefits_upload").change(function (){
var fileName = $(this).val();
$(".filename").html(fileName);
});
});
function makeActive(){
$('li[id="benefit_forms"]').addClass('active');
};
$(document).ready(makeActive);
</script>
+143 -148
View File
@@ -1,172 +1,167 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!--begin span12 -->
<% if @user.paid_time_off %>
<%= render :partial => "layouts/dashboard/dashboard_stats"%>
<% end %>
</div> <!-- end span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!--begin span12 -->
<% if @user.paid_time_off %>
<%= render :partial => "layouts/dashboard/dashboard_stats"%>
<% end %>
</div> <!-- end span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="home"]').addClass('active');
$('li[id="home"]').addClass('active');
};
function pieChartHome() {
$(function () {
//create instance
$('.chart1').easyPieChart({
animate: 2000,
barColor: '#e26666',
trackColor: '#dddddd',
scaleColor: '#e26666',
size: 160,
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);
$(function () {
//create instance
$('.chart1').easyPieChart({
animate: 2000,
barColor: '#e26666',
trackColor: '#dddddd',
scaleColor: '#e26666',
size: 160,
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);
});
$(function () {
//create instance
$('.chart2').easyPieChart({
animate: 2000,
barColor: '#b5799e',
trackColor: '#dddddd',
scaleColor: '#b5799e',
size: 160,
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);
$(function () {
//create instance
$('.chart2').easyPieChart({
animate: 2000,
barColor: '#b5799e',
trackColor: '#dddddd',
scaleColor: '#b5799e',
size: 160,
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);
});
$(function () {
//create instance
$('.chart3').easyPieChart({
animate: 2000,
barColor: '#579da9',
trackColor: '#dddddd',
scaleColor: '#579da9',
size: 160,
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);
$(function () {
//create instance
$('.chart3').easyPieChart({
animate: 2000,
barColor: '#579da9',
trackColor: '#dddddd',
scaleColor: '#579da9',
size: 160,
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);
});
$(function () {
//create instance
$('.chart4').easyPieChart({
animate: 2000,
barColor: '#dba26b',
trackColor: '#dddddd',
scaleColor: '#dba26b',
size: 160,
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);
$(function () {
//create instance
$('.chart4').easyPieChart({
animate: 2000,
barColor: '#dba26b',
trackColor: '#dddddd',
scaleColor: '#dba26b',
size: 160,
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);
});
$(function () {
//create instance
$('.chart5').easyPieChart({
animate: 3000,
barColor: '#1e825e',
trackColor: '#dddddd',
scaleColor: '#1e825e',
size: 160,
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);
$(function () {
//create instance
$('.chart5').easyPieChart({
animate: 3000,
barColor: '#1e825e',
trackColor: '#dddddd',
scaleColor: '#1e825e',
size: 160,
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);
});
}
}
$(document).ready(
makeActive,
pieChartHome()
makeActive,
pieChartHome()
);
</script>
+8 -9
View File
@@ -6,11 +6,11 @@
</form>
<div id="dt_example" class="example_alt_pagination">
<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>
<tr>
<%
count = (params[:field] ? params[:field].count : 3)
<%
count = (params[:field] ? params[:field].count : 3)
count.times do %>
<td>&nbsp;</td>
<% end %>
@@ -18,16 +18,16 @@
</thead>
<tbody>
<% @analytics.each do |a|%>
<tr>
<tr>
<% a.attributes.each do |k,v| %>
<td><%= v %></td>
<% end %>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div class="clearfix">
</div>
</div>
@@ -36,11 +36,10 @@
<script type="text/javascript">
function dataTablePagination(){
$('#data-table').dataTable({
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
};
$(document).ready(dataTablePagination());
</script>
+19 -20
View File
@@ -1,5 +1,5 @@
<div id="dt_example" class="example_alt_pagination">
<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>
<tr>
<th>
@@ -18,25 +18,25 @@
</thead>
<tbody>
<% @users.each do |u|%>
<tr>
<td style="word-wrap:break-word;">
<%= "#{u.first_name} #{u.last_name}"%>
</td>
<td>
<%= u.email%>
</td>
<td>
<%= u.admin ? %{<span class="fs1" aria-hidden="true" data-icon="&#xe0fe;"}.html_safe : nil %>
</td>
<td>
<%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%>
</td>
</tr>
<% end %>
<tr>
<td style="word-wrap:break-word;">
<%= "#{u.first_name} #{u.last_name}"%>
</td>
<td>
<%= u.email%>
</td>
<td>
<%= u.admin ? %{<span class="fs1" aria-hidden="true" data-icon="&#xe0fe;"}.html_safe : nil %>
</td>
<td>
<%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%>
</td>
</tr>
<% end %>
</tbody>
</table>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div id="editAcct" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
</div>
<div class="clearfix">
</div>
</div>
@@ -51,11 +51,10 @@ function openEditModal(id){
};
function dataTablePagination(){
$('#data-table').dataTable({
$('#data-table').dataTable({
"sPaginationType": "full_numbers"
});
};
$(document).ready(dataTablePagination());
</script>
+61 -64
View File
@@ -1,7 +1,4 @@
<!-- Begin Modal -->
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
@@ -11,89 +8,89 @@
</h4>
</div>
<div class="modal-body">
<div class="row-fluid">
<div class="row-fluid">
<div class="span8">
<%= form_for @user, :html => {:id => "account_edit"} do |f| %>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<%= form_for @user, :html => {:id => "account_edit"} do |f| %>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<%= f.label :admin, nil, {:class => "control-label"}%>
<%= f.select(:admin, @admin_select) %>
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<%= f.label :admin, nil, {:class => "control-label"}%>
<%= f.select(:admin, @admin_select) %>
</div>
</div>
<div class="row-fluid">
<div class="row-fluid">
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">
Close
</button>
<%= 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"} %>
</div>
<% end %>
<% end %>
<!-- End Modal -->
<%= javascript_include_tag ('validation.js')%>
<script type="text/javascript">
$('#submit_button').click(function() {
var valuesToSubmit = $("#account_edit").serialize();
$("#editAcct").modal('hide');
$.ajax({
$('#submit_button').click(function() {
var valuesToSubmit = $("#account_edit").serialize();
$("#editAcct").modal('hide');
$.ajax({
url: "/admin/" + <%= @user.user_id %> + "/update_user.json",
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
$('#delete_button').click(function() {
$("#editAcct").modal('hide');
$.ajax({
$('#delete_button').click(function() {
$("#editAcct").modal('hide');
$.ajax({
url: "/admin/" + <%= params[:admin_id] %> + "/delete_user.json",
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
loadTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
</script>
+13 -13
View File
@@ -16,26 +16,26 @@ end
</head>
<body>
<% if current_user %>
<%= render "layouts/shared/header" %>
<%= render "layouts/shared/sidebar" %>
<% if current_user %>
<%= render "layouts/shared/header" %>
<%= render "layouts/shared/sidebar" %>
<% else %>
<%= render "layouts/tutorial/header" %>
<%= render "layouts/tutorial/sidebar" %>
<%= render "layouts/tutorial/header" %>
<%= render "layouts/tutorial/sidebar" %>
<% end %>
<div class="container-fluid">
<div class="dashboard-wrapper">
<%= render "layouts/shared/messages" %>
<%= yield %>
</div>
</div>
<%= render "layouts/shared/footer" %>
<div class="dashboard-wrapper">
<%= render "layouts/shared/messages" %>
<%= yield %>
</div>
</div>
<%= render "layouts/shared/footer" %>
<script type="text/javascript">
//Dropdown
$('.dropdown-toggle').dropdown();
</script>
</body>
</html>
+13 -14
View File
@@ -1,5 +1,4 @@
<header>
<span style="color:#eee;margin-left:10px;">
Font Size:
<a href="<%= home_dashboard_index_path %>?font=8pt" style="font-size:10pt;color:#eee;">A</a>
@@ -8,12 +7,12 @@
<div class="user-profile">
<a data-toggle="dropdown" class="dropdown-toggle">
<img src=" <%= image_path('profile_color.jpg')%>" alt="profile">
</a>
<span class="caret"></span>
<ul class="dropdown-menu pull-right">
<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>
<%= link_to "logout", logout_path %>
@@ -21,16 +20,16 @@
</ul>
</div>
<ul class="mini-nav">
<li style="color: #FFFFFF">
<!--
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
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
<li>
<%= button_to "RailsGoat Tutorials", tutorials_path, {:class => "btn btn-primary", :method => "get"}%>
</li>
<li style="color: #FFFFFF">
<!--
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
nothing bad will happen
-->
Welcome, <%= current_user.first_name.html_safe %>
</li>
<li>
<%= button_to "RailsGoat Tutorials", tutorials_path, {:class => "btn btn-primary", :method => "get"}%>
</li>
</ul>
</header>
+16 -16
View File
@@ -1,18 +1,18 @@
<% flash.each do |name, msg| %>
<% if name == :error %>
<div class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :success %>
<div class="alert alert-success">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :info %>
<div class="alert alert-info">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% end %>
<% if name == :error %>
<div class="alert alert-error">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :success %>
<div class="alert alert-success">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% elsif name == :info %>
<div class="alert alert-info">
<a class="close" data-dismiss="alert" href="#">×</a>
<%= content_tag :div, msg, :id => "flash_notice" %>
</div>
<% end %>
<% end %>
+49 -49
View File
@@ -17,9 +17,9 @@
Admin
<% end %>
</li>
<% end %>
<% end %>
<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">
<span class="fs1" aria-hidden="true" data-icon="&#xe05c;"></span>
</div>
@@ -66,8 +66,8 @@
Messages
<% end %>
</li>
<li id="pay">
<%= link_to user_pay_index_path(:user_id => current_user.user_id) do %>
<li id="pay">
<%= link_to user_pay_index_path(:user_id => current_user.user_id) do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe038;"></span>
</div>
@@ -77,52 +77,52 @@
</ul>
</div>
<script type="text/javascript">
//Main menu navigation
<script type="text/javascript">
//Main menu navigation
$('.submenu > a').click(function(e){
e.preventDefault();
var submenu = $(this).siblings('ul');
var li = $(this).parents('li');
var submenus = $('#mainnav li.submenu ul');
var submenus_parents = $('#mainnav li.submenu');
if(li.hasClass('open'))
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenu.slideUp();
} else {
submenu.fadeOut(250);
}
li.removeClass('open');
} else
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenus.slideUp();
submenu.slideDown();
} else {
submenus.fadeOut(250);
submenu.fadeIn(250);
}
submenus_parents.removeClass('open');
li.addClass('open');
}
});
$('.submenu > a').click(function(e){
e.preventDefault();
var submenu = $(this).siblings('ul');
var li = $(this).parents('li');
var submenus = $('#mainnav li.submenu ul');
var submenus_parents = $('#mainnav li.submenu');
if(li.hasClass('open'))
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenu.slideUp();
} else {
submenu.fadeOut(250);
}
li.removeClass('open');
} else
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenus.slideUp();
submenu.slideDown();
} else {
submenus.fadeOut(250);
submenu.fadeIn(250);
}
submenus_parents.removeClass('open');
li.addClass('open');
}
});
var ul = $('#mainnav > ul');
var ul = $('#mainnav > ul');
$('#mainnav > a').click(function(e)
{
e.preventDefault();
var mainnav = $('#mainnav');
if(mainnav.hasClass('open'))
{
mainnav.removeClass('open');
ul.slideUp(250);
} else
{
mainnav.addClass('open');
ul.slideDown(250);
}
});
$('#mainnav > a').click(function(e)
{
e.preventDefault();
var mainnav = $('#mainnav');
if(mainnav.hasClass('open'))
{
mainnav.removeClass('open');
ul.slideUp(250);
} else
{
mainnav.addClass('open');
ul.slideDown(250);
}
});
</script>
</script>
+25 -25
View File
@@ -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 -->
<header>
<% if not current_user %>
<ul class="mini-nav">
<li>
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<ul class="mini-nav">
<li>
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
</li>
</ul>
<% else %>
<ul class="mini-nav">
<li>
<%= button_to "<- back to app", home_dashboard_index_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<% end %>
<ul class="mini-nav">
<% if not current_user %>
<ul class="mini-nav">
<li>
<%= button_to "signup", signup_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<ul class="mini-nav">
<li>
<%= button_to "login", login_path, {:class => "btn", :method => "get"} %>
</li>
</ul>
<% else %>
<ul class="mini-nav">
<li>
<%= button_to "<- back to app", home_dashboard_index_path, {:class => "btn btn-primary", :method => "get"} %>
</li>
</ul>
<% end %>
<ul class="mini-nav">
<li>
<%= button_to "Tutorial Credentials", "#myModalLabel1", {:id => "show_creds_btn", :class => "btn btn-danger", :method => "get"} %>
</li>
@@ -26,14 +26,14 @@
</header>
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
</div>
<div id="modal_div" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myAlert" aria-hidden="true">
</div>
<script type="text/javascript">
$('#show_creds_btn').click(function(event) {
event.preventDefault();
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
$("#modal_div").modal("show");
$('#show_creds_btn').click(function(event) {
event.preventDefault();
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
$("#modal_div").modal("show");
});
</script>
+19 -19
View File
@@ -1,6 +1,6 @@
<div id="mainnav"class="hidden-phone hidden-tablet">
<ul style="display: block;">
<li id="tutorials_home">
<li id="tutorials_home">
<%= link_to tutorials_path, :html => {:method => "get"} do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe001;"></span>
@@ -16,7 +16,7 @@
A1 Injection
<% end %>
</li>
<li id="broken_auth">
<li id="broken_auth">
<%= link_to broken_auth_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
@@ -32,7 +32,7 @@
A3 XSS
<% end %>
</li>
<li id="insecure_dor">
<%= link_to insecure_dor_tutorials_path do %>
<div class="icon">
@@ -41,7 +41,7 @@
A4 Insecure DOR
<% end %>
</li>
<li id="misconfig">
<li id="misconfig">
<%= link_to misconfig_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
@@ -65,7 +65,7 @@
A7 Access Control
<% end %>
</li>
<li id="csrf">
<li id="csrf">
<%= link_to csrf_tutorials_path do %>
<div class="icon">
<span class="fs1" aria-hidden="true" data-icon="&#xe094;"></span>
@@ -97,21 +97,21 @@
Extras
</a>
<ul>
<li id="gauntlt">
<li id="gauntlt">
<%= link_to "gauntlt", gauntlt_tutorials_path %>
</li>
<li id="guard">
<%= link_to "Guard", guard_tutorials_path %>
</li>
<li id="mass_assignment">
<li id="mass_assignment">
<%= link_to "Mass Assignment", mass_assignment_tutorials_path %>
</li>
<li id="logic_flaws">
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
</li>
<li id="metaprogramming">
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
</li>
<li id="logic_flaws">
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
</li>
<li id="metaprogramming">
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
</li>
</ul>
</li>
</ul>
@@ -134,17 +134,17 @@ $('.submenu > a').click(function(e){
submenu.fadeOut(250);
}
li.removeClass('open');
} else
} else
{
if(($(window).width() > 768) || ($(window).width() < 479)) {
submenus.slideUp();
submenus.slideUp();
submenu.slideDown();
} else {
submenus.fadeOut(250);
submenus.fadeOut(250);
submenu.fadeIn(250);
}
submenus_parents.removeClass('open');
li.addClass('open');
submenus_parents.removeClass('open');
li.addClass('open');
}
});
@@ -158,7 +158,7 @@ $('#mainnav > a').click(function(e)
{
mainnav.removeClass('open');
ul.slideUp(250);
} else
} else
{
mainnav.addClass('open');
ul.slideDown(250);
@@ -36,7 +36,7 @@
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
before_filter :administrative, :if => :admin_param
...
@@ -45,8 +45,8 @@
params[:id] == '1'
end
} %>
</pre>
</pre>
</div>
</div>
</div>
@@ -63,7 +63,7 @@
<p><b>Failure to Restrict URL Access - ATTACK</b></p>
<p class="desc">
Request the following URL: /admin/1/dashboard and have fun :-)
</p>
</p>
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
<p class="desc">
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb. The additional condition that if the admin_id param equals 1 means the filter can be circumvented by an attacker. The way to fix this issue is to remove the conditional and enforce the filter on all access requests to the admin dashboard as follows:
@@ -71,7 +71,7 @@
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
before_filter :administrative
} %>
</pre>
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseCompOne" style="height: auto;">
<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.
</p>
</p>
</div>
</div>
</div>
@@ -32,28 +32,28 @@
</div>
<div class="accordion-body collapse" id="collapseCompTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within app/models/user.rb
</p>
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
<span style="background-color: yellow"> user = find_by_email(email)</span>
raise "#{email} doesn't exist!" if !(user)
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
return auth
end
</pre>
<p class="desc">
Ignore for a moment that the application actually tells you whether or not an email address exists :-). Instead, let's look at what would happen if this error message wasn't so specific. Even if the error message vulnerability was mitigated (because it indicates whether or not a user exists), there will be some variations in the application's response between a user that exists and one that does not (however so slight, considering MD5 is in use).
</p>
<p class="desc">
To understand why, let's follow the flow of this code example. Firstly, the application look for a user by email. If not found, nothing else really happens. No further processing, password comparison, etc. If a user <i>is</i> found, we will perform a password comparison and process as normal.
</p>
<p>
Within app/models/user.rb
</p>
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
<span style="background-color: yellow"> user = find_by_email(email)</span>
raise "#{email} doesn't exist!" if !(user)
<span style="background-color: yellow">if user.password == Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
return auth
end
</pre>
<p class="desc">
Ignore for a moment that the application actually tells you whether or not an email address exists :-). Instead, let's look at what would happen if this error message wasn't so specific. Even if the error message vulnerability was mitigated (because it indicates whether or not a user exists), there will be some variations in the application's response between a user that exists and one that does not (however so slight, considering MD5 is in use).
</p>
<p class="desc">
To understand why, let's follow the flow of this code example. Firstly, the application look for a user by email. If not found, nothing else really happens. No further processing, password comparison, etc. If a user <i>is</i> found, we will perform a password comparison and process as normal.
</p>
</div>
</div>
</div>
@@ -68,29 +68,29 @@
<div class="accordion-body collapse" id="collapseCompThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Lack of Password Complexity - SOLUTION</b></p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
def self.authenticate(email, password)
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
return user
else
raise "Incorrect username or password"
end
end
</pre>
<p class="desc">
To mitigate this attack and shore up our weakness, we do two things. The first is to find a user by email, if they don't exist, create a new user object in memory (not in the database) and assign it a blank password value. This means, regardless of whether or not a user exists, we will have a user to perform some processing on. The next is, we take the input from the user and match it against the user object's password leveraging secure_compare. This is a function (secure_compare) used to ensure that when a comparison happens, it will always take the same amount of time.
</p>
<p class="desc">
In summary, we have ensured that regardless of whether or not a user exists, a password comparison will always occur and it will take the same amount of time to complete.
</p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
def self.authenticate(email, password)
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
<span style="background-color: yellow">if Rack::Utils.secure_compare(user.password, Digest::MD5.hexdigest(password))</span>
return user
else
raise "Incorrect username or password"
end
end
</pre>
<p class="desc">
To mitigate this attack and shore up our weakness, we do two things. The first is to find a user by email, if they don't exist, create a new user object in memory (not in the database) and assign it a blank password value. This means, regardless of whether or not a user exists, we will have a user to perform some processing on. The next is, we take the input from the user and match it against the user object's password leveraging secure_compare. This is a function (secure_compare) used to ensure that when a comparison happens, it will always take the same amount of time.
</p>
<p class="desc">
In summary, we have ensured that regardless of whether or not a user exists, a password comparison will always occur and it will take the same amount of time to complete.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseCompFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -100,9 +100,9 @@
</div>
<div class="accordion-body collapse" id="collapseCompFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Timing is everything. Authenticating is important too.
</p>
<p class="desc">
Timing is everything. Authenticating is important too.
</p>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapsePwdOne" style="height: auto;">
<div class="accordion-inner">
<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.
</p>
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>
</div>
</div>
</div>
@@ -32,18 +32,18 @@
</div>
<div class="accordion-body collapse" id="collapsePwdTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Within app/models/User.rb
</p>
<pre class="ruby">
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create
</pre>
<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.
</p>
<pre class="ruby">
validates :password, :presence => true,
:confirmation => true,
:length => {:within => 6..40},
:on => :create
</pre>
<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.
</p>
</div>
</div>
</div>
@@ -58,28 +58,28 @@
<div class="accordion-body collapse" id="collapsePwdThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Lack of Password Complexity - ATTACK</b></p>
<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").
</p>
<p><b>Lack of Password Complexity - SOLUTION</b></p>
<p class="desc">
This regular expression validates the password has the following requirements:
<li>1 digit</li>
<li>1 lowercase alphabet</li>
<li>1 uppercase alphabet</li>
<li>1 special character</li>
</p>
<pre class="ruby">
<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").
</p>
<p><b>Lack of Password Complexity - SOLUTION</b></p>
<p class="desc">
This regular expression validates the password has the following requirements:
<li>1 digit</li>
<li>1 lowercase alphabet</li>
<li>1 uppercase alphabet</li>
<li>1 special character</li>
</p>
<pre class="ruby">
validates :password, :presence => true,
:confirmation => true,
:if => :password,
:format => {:with => /\A.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\@\#\$\%\^\&\+\=]).*\z/}
</pre>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<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">
@@ -89,9 +89,9 @@ validates :password, :presence => true,
</div>
<div class="accordion-body collapse" id="collapsePwdFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
I wonder how strong the administrator's password is?
</p>
<p class="desc">
I wonder how strong the administrator's password is?
</p>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<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.
</p>
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>
</div>
</div>
</div>
@@ -33,50 +33,50 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p><b>Username and Password Enumeration</b></p>
<p><b>Within /app/models/user.rb:</b><p>
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
if user
if user.password == password
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
</pre>
<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>
<pre class="ruby">
def create
<p><b>Within /app/models/user.rb:</b><p>
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = e.message
render "new"
end
<pre class="ruby">
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
# I heard something about hashing, dunno, why bother really. Nobody will get access to my stuff!
if user
if user.password == password
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
</pre>
<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>
<pre class="ruby">
def create
end
</pre>
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
<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.
</p>
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = e.message
render "new"
end
end
</pre>
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
<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.
</p>
</div>
</div>
</div>
@@ -91,34 +91,34 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p>
<b> Username and Password Enumeration - SOLUTION</b>
</p>
<p> Within /app/controllers/sessions_controller.rb</p>
<b> Username and Password Enumeration - SOLUTION</b>
</p>
<p> Within /app/controllers/sessions_controller.rb</p>
<pre class="ruby">
def create
def create
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
begin
user = User.authenticate(params[:email], params[:password])
rescue Exception => e
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = "Either your username and password is incorrect" #e.message
render "new"
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
redirect_to home_dashboard_index_path
else
flash[:error] = "Either your username and password is incorrect" #e.message
render "new"
end
end
</pre>
<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.
</p>
end
</pre>
<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.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -128,10 +128,10 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
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?
</p>
<p class="desc">
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?
</p>
</div>
</div>
</div>
@@ -9,106 +9,106 @@
</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="row">
<div class="span8">
<p>Warning, this is a spoiler</p>
<p>Are you sure you want to see the credentials?</p>
<div id="creds_hidden" style="display:none">
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
<thead>
<tr>
<th>
Email
</th>
<th>
Password
</th>
<th>
API Key
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="word-wrap:break-word;">
admin@metacorp.com
</td>
<td>
admin1234
</td>
<td>
1-01de24d75cffaa66db205278d1cf900bf087a737
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jack@metacorp.com
</td>
<td>
yankeessuck
</td>
<td>
2-050ddd40584978fe9e82840b8b95abb98e4786dc
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jim@metacorp.com
</td>
<td>
alohaowasp
</td>
<td>
3-eaa9b4d748d6a8c6a38e24ac1cc2204ebc3541c1
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
mike@metacorp.com
</td>
<td>
motocross1445
</td>
<td>
4-4c809b3d11d272cff8cab1da9e4cdf61137f29d2
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
ken@metacorp.com
</td>
<td>
citrusblend
</td>
<td>
5-4af604a848ca212cfa3935352aabe9522cf89fdc
</td>
</tr>
</tbody>
</table>
</div>
<p>Warning, this is a spoiler</p>
<p>Are you sure you want to see the credentials?</p>
<div id="creds_hidden" style="display:none">
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
<thead>
<tr>
<th>
Email
</th>
<th>
Password
</th>
<th>
API Key
</th>
</tr>
</thead>
<tbody>
<tr>
<td style="word-wrap:break-word;">
admin@metacorp.com
</td>
<td>
admin1234
</td>
<td>
1-01de24d75cffaa66db205278d1cf900bf087a737
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jack@metacorp.com
</td>
<td>
yankeessuck
</td>
<td>
2-050ddd40584978fe9e82840b8b95abb98e4786dc
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
jim@metacorp.com
</td>
<td>
alohaowasp
</td>
<td>
3-eaa9b4d748d6a8c6a38e24ac1cc2204ebc3541c1
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
mike@metacorp.com
</td>
<td>
motocross1445
</td>
<td>
4-4c809b3d11d272cff8cab1da9e4cdf61137f29d2
</td>
</tr>
<tr>
<td style="word-wrap:break-word;">
ken@metacorp.com
</td>
<td>
citrusblend
</td>
<td>
5-4af604a848ca212cfa3935352aabe9522cf89fdc
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="row-fluid">
<div class="row-fluid">
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">
Close
</button>
<button id="understood" class="btn btn-primary" aria-hidden="true">
<button id="understood" class="btn btn-primary" aria-hidden="true">
I understand
</button>
</div>
<!-- End Modal -->
<script type="text/javascript">
$('#understood').click(function() {
$("#creds_hidden").show();
$('#understood').click(function() {
$("#creds_hidden").show();
});
@@ -31,20 +31,20 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<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>application_controller.rb<p>
<p>
<pre class="ruby">
# Our security guy keep talking about sea-surfing, cool story bro.
# protect_from_forgery
</pre>
</p>
<p> application.html.erb </p>
<p>
<pre class="ruby">
<%= @meta_code_bad %>
</pre>
</p>
<p>application_controller.rb<p>
<p>
<pre class="ruby">
# Our security guy keep talking about sea-surfing, cool story bro.
# protect_from_forgery
</pre>
</p>
<p> application.html.erb </p>
<p>
<pre class="ruby">
<%= @meta_code_bad %>
</pre>
</p>
</div>
</div>
</div>
@@ -59,57 +59,57 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Cross-Site Request Forgery ATTACK:</b></p>
<p class="desc">
The application allows users to update their calendar and schedule PTO events (PTO section). Due to the fact CSRF protections are disabled, the AJAX request will send the authenticity token but the application will <b>not</b> validate either its presence or validity. Create an html page using the code shown below, authenticate as another user, click on it, review the new calendar (change the dates under date_range1). You should see this HTML code will work, even if you hadn't navigated to the PTO section prior to sending it.
</p>
<p>
<pre class="ruby">
<%=
<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.
</p>
<p>
<pre class="ruby">
<%=
%{
<html>
<body>
<form action="http://railsgoat.dev/schedule.json" method="POST">
<input type="hidden" name="schedule&#91;event&#95;name&#93;" value="Bad&#32;Guy" />
<input type="hidden" name="schedule&#91;event&#95;type&#93;" value="pto" />
<input type="hidden" name="schedule&#91;event&#95;desc&#93;" value="Fun&#32;Fun" />
<input type="hidden" name="date&#95;range1" value="06&#47;08&#47;2013&#32;&#45;&#32;06&#47;09&#47;2013" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
}
%>
</pre>
</p>
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
<p>
By default, the protect_from_forgery directive is added under the application_controller.rb at project creation. However, occasionally developers turn it off (comment out) because of issues with JS. There are two separate solutions around the JS problem.
</p>
<p>
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>
</p>
<p>
<pre class="ruby">
<%= @meta_code_good %>
</pre>
</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.
</p>
<p>
<li>Leverage the serialize() method, shown on line 3. This grabs all the values from the form, including the authenticity token.</li>
</p>
<p>
<pre class="ruby">
<%= @ajax_code_good %>
</pre>
</p>
<html>
<body>
<form action="http://railsgoat.dev/schedule.json" method="POST">
<input type="hidden" name="schedule&#91;event&#95;name&#93;" value="Bad&#32;Guy" />
<input type="hidden" name="schedule&#91;event&#95;type&#93;" value="pto" />
<input type="hidden" name="schedule&#91;event&#95;desc&#93;" value="Fun&#32;Fun" />
<input type="hidden" name="date&#95;range1" value="06&#47;08&#47;2013&#32;&#45;&#32;06&#47;09&#47;2013" />
<input type="submit" value="Submit request" />
</form>
</body>
</html>
}
%>
</pre>
</p>
<p><b> Cross-Site Request Forgery SOLUTION:</b></p>
<p>
By default, the protect_from_forgery directive is added under the application_controller.rb at project creation. However, occasionally developers turn it off (comment out) because of issues with JS. There are two separate solutions around the JS problem.
</p>
<p>
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>
</p>
<p>
<pre class="ruby">
<%= @meta_code_good %>
</pre>
</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.
</p>
<p>
<li>Leverage the serialize() method, shown on line 3. This grabs all the values from the form, including the authenticity token.</li>
</p>
<p>
<pre class="ruby">
<%= @ajax_code_good %>
</pre>
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -123,6 +123,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseModelOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
</p>
<p class="desc">
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
</p>
</div>
</div>
</div>
@@ -32,51 +32,51 @@
</div>
<div class="accordion-body collapse" id="collapseModelTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
<p class="desc">
Within app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
respond_with @user.admin ? User.all : @user
end
def show
respond_with @user.as_json
end
</pre>
<p class="desc">
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
</p>
<pre class="ruby">
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
</pre>
<p class="desc">
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
X-Runtime: 0.051734
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
Note that all attributes associated with this user are returned via the API.
</p>
def show
respond_with @user.as_json
end
</pre>
<p class="desc">
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
</p>
<pre class="ruby">
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
</pre>
<p class="desc">
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
X-Runtime: 0.051734
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
Note that all attributes associated with this user are returned via the API.
</p>
</div>
</div>
</div>
@@ -91,41 +91,41 @@
<div class="accordion-body collapse" id="collapseModelThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Model Attributes Exposure - ATTACK</b></p>
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
<p><b>Model Attributes Exposure - SOLUTION</b></p>
<p class="desc">
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
</p>
<pre class="ruby">
respond_with @user.admin ? User.all.as_json : @user.as_json
</pre>
<p class="desc">
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
</p>
<pre class="ruby">
def as_json
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
end
</pre>
<p class="desc">
The response from the API should look like:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "2333488e856669ac637e37cb4cf09cb6"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: baa6a1c90004838793614e4c61633767
X-Runtime: 0.092768
Connection: close
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
<p><b>Model Attributes Exposure - SOLUTION</b></p>
<p class="desc">
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
</p>
<pre class="ruby">
respond_with @user.admin ? User.all.as_json : @user.as_json
</pre>
<p class="desc">
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
</p>
<pre class="ruby">
def as_json
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
end
</pre>
<p class="desc">
The response from the API should look like:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "2333488e856669ac637e37cb4cf09cb6"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: baa6a1c90004838793614e4c61633767
X-Runtime: 0.092768
Connection: close
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
</pre>
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseModelFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -16,15 +16,15 @@
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<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.
</p>
<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.
</p>
<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>.
</p>
</p>
<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.
</p>
<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>.
</p>
</div>
</div>
</div>
@@ -38,35 +38,35 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
before_save <span style="background-color:yellow">:hash_password</span>
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
if user
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
def hash_password
if self.password.present?
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
end
end
</pre>
</p>
<pre class="ruby">
before_save <span style="background-color:yellow">:hash_password</span>
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
if user
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
def hash_password
if self.password.present?
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
end
end
</pre>
</div>
</div>
</div>
@@ -81,34 +81,34 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Password Storage - ATTACK</b></p>
<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.
</p>
<p><b>Password Storage - SOLUTION</b></p>
<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.
</p>
<pre class="ruby">
def self.authenticate(email, password)
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>
user
else
"Invalid Credentials Supplied"
end
end
def hash_password
if self.password.present?
<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>
end
end
</pre>
<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.
</p>
<p><b>Password Storage - SOLUTION</b></p>
<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.
</p>
<pre class="ruby">
def self.authenticate(email, password)
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>
user
else
"Invalid Credentials Supplied"
end
end
def hash_password
if self.password.present?
<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>
end
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
+103 -103
View File
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseSSNOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores and transmits Social Security Numbers insecurely.
</p>
<p class="desc">
The Railsgoat application stores and transmits Social Security Numbers insecurely.
</p>
</div>
</div>
</div>
@@ -32,19 +32,19 @@
</div>
<div class="accordion-body collapse" id="collapseSSNTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
</p>
<p class="desc">
<p class="desc">
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
</p>
<p class="desc">
The WorkInfo model (app/models/work_info.rb) is missing code to encrypt this data prior to storage. Additionally, while code exists to render only the last 4 numbers of an SSN (shown below), at no time is it used.
</p>
<pre class="ruby">
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
end
</pre>
</p>
<pre class="ruby">
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
end
</pre>
</div>
</div>
</div>
@@ -58,100 +58,100 @@
</div>
<div class="accordion-body collapse" id="collapseSSNThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>SSN Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
</p>
<p class="desc">
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
</p>
<pre class="ruby">
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
<p><b>SSN Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
</p>
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def key
raise "Key Missing" if !(KEY)
KEY
end
<p class="desc">
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
</p>
<pre class="ruby">
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
def iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def cipher_type
'aes-256-cbc'
end
</pre>
<p class="desc">
Also within the WorkInfo model, we add the following line of code...
</p>
<pre class="ruby">
before_save :encrypt_ssn
</pre>
<p class="desc">
The remaining pieces are:
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
<li> For new user's who are registering, we create an initialization specific to their account</li>
</p>
<pre class="ruby">
# SEED DATA
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
</pre>
<pre class="ruby">
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
elsif Rails.env.development?
KEY = "123456789101112123456789101112123456789101112"
end
</pre>
<pre class="ruby">
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
<%= CGI.unescapeHTML("&lt;td class=&quot;ssn&quot;&gt;&lt;%= @user.work_info.last_four %&gt;&lt;/td&gt;") %>
</pre>
<pre class="ruby">
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
</pre>
def key
raise "Key Missing" if !(KEY)
KEY
end
def iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def cipher_type
'aes-256-cbc'
end
</pre>
<p class="desc">
Also within the WorkInfo model, we add the following line of code...
</p>
<pre class="ruby">
before_save :encrypt_ssn
</pre>
<p class="desc">
The remaining pieces are:
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
<li> For new user's who are registering, we create an initialization specific to their account</li>
</p>
<pre class="ruby">
# SEED DATA
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
</pre>
<pre class="ruby">
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
elsif Rails.env.development?
KEY = "123456789101112123456789101112123456789101112"
end
</pre>
<pre class="ruby">
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
<%= CGI.unescapeHTML("&lt;td class=&quot;ssn&quot;&gt;&lt;%= @user.work_info.last_four %&gt;&lt;/td&gt;") %>
</pre>
<pre class="ruby">
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseSSNFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseNine" style="height: auto;">
<div class="accordion-inner">
<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.
</p>
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>
</div>
</div>
</div>
@@ -32,50 +32,50 @@
</div>
<div class="accordion-body collapse" id="collapseTen" style="height: 0px;">
<div class="accordion-inner">
<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.
</p>
<p>
Within app/controllers/benefits_controller.rb:
</p>
<pre class="ruby">
def upload
<span style="background:yellow">file = params[:benefits][:upload]</span>
if file
flash[:success] = "File Successfully Uploaded!"
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
else
flash[:error] = "Something went wrong"
end
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
</pre>
<p>
Within app/models/benefits.rb:
</p>
<pre class="ruby">
class Benefits &lt; ActiveRecord::Base
attr_accessor :backup
<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.
</p>
<p>
Within app/controllers/benefits_controller.rb:
</p>
<pre class="ruby">
def upload
<span style="background:yellow">file = params[:benefits][:upload]</span>
if file
flash[:success] = "File Successfully Uploaded!"
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
else
flash[:error] = "Something went wrong"
end
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
</pre>
<p>
Within app/models/benefits.rb:
</p>
<pre class="ruby">
class Benefits &lt; ActiveRecord::Base
attr_accessor :backup
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "w+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
def self.save(file, backup=false)
data_path = Rails.root.join("public", "data")
full_file_name = "#{data_path}/#{file.original_filename}"
f = File.open(full_file_name, "w+")
f.write file.read
f.close
make_backup(file, data_path, full_file_name) if backup == "true"
end
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>}")
end
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>}")
end
end
</pre>
<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.
</p>
end
</pre>
<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.
</p>
</div>
</div>
</div>
@@ -90,54 +90,54 @@
<div class="accordion-body collapse" id="collapseEleven" style="height: 0px;">
<div class="accordion-inner">
<p><b>Command Injection - ATTACK</b></p>
<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 ).
</p>
<pre class='ruby'>
POST /upload HTTP/1.1
Host: railsgoat.dev
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-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/users/5/benefit_forms
Cookie: _railsgoat_session=[redacted for brevity]
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------54316025
Content-Length: 1731
<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 ).
</p>
<pre class='ruby'>
POST /upload HTTP/1.1
Host: railsgoat.dev
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-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/users/5/benefit_forms
Cookie: _railsgoat_session=[redacted for brevity]
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------54316025
Content-Length: 1731
----------54316025
Content-Disposition: form-data; name="utf8"
----------54316025
Content-Disposition: form-data; name="utf8"
✓
----------54316025
Content-Disposition: form-data; name="authenticity_token"
✓
----------54316025
Content-Disposition: form-data; name="authenticity_token"
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
----------54316025
Content-Disposition: form-data; name="benefits[backup]"
zKnXZO1PGcM+rFweczO7H8IDQ6NHmc8Siud2ypM6ZeA=
----------54316025
Content-Disposition: form-data; name="benefits[backup]"
<span style="background:yellow">true</span>
----------54316025
Content-Disposition: form-data; name="benefits[upload]"; <span style="background:yellow">filename="test.rb;+mkdir+thisisatest "</span>
Content-Type: text/x-ruby-script
</pre>
<p><b>Command Injection - SOLUTION</b></p>
<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.
</p>
<p>
As an example:
</p>
<pre class="ruby">
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}"
end
</pre>
<span style="background:yellow">true</span>
----------54316025
Content-Disposition: form-data; name="benefits[upload]"; <span style="background:yellow">filename="test.rb;+mkdir+thisisatest "</span>
Content-Type: text/x-ruby-script
</pre>
<p><b>Command Injection - SOLUTION</b></p>
<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.
</p>
<p>
As an example:
</p>
<pre class="ruby">
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}"
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<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">
@@ -150,7 +150,7 @@
Let's create a backup when uploading a file, wonder how they are naming it?
</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-inner">
<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 attackers hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
</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 attackers hostile data can trick the interpreter into executing unintended commands or accessing unauthorized data.
</p>
</div>
</div>
</div>
@@ -32,30 +32,30 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<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.
</p>
<p>
Within app/controllers/users_controller.rb
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow"> user = User.find(:first, :conditions => "user_id = '#{params[:user][:user_id]}'")</span>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<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.
</p>
</p>
<p>
Within app/controllers/users_controller.rb
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow"> user = User.find(:first, :conditions => "user_id = '#{params[:user][:user_id]}'")</span>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<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.
</p>
</div>
</div>
</div>
@@ -70,77 +70,77 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>SQL Injection - ATTACK</b></p>
<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:
<p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 294
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: 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
</pre>
<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>
</p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 208
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
<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:
<p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 294
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: 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
</pre>
<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>
</p>
<pre class="ruby">
POST /users/5.json HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Referer: http://railsgoat.dev/users/5/account_settings
Content-Length: 208
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Pragma: 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
</pre>
<p><b>SQL Injection - SOLUTION</b></p>
<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/>
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow">user = current_user</span>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<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:
</p>
<pre class="ruby">
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
</pre>
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>
<p><b>SQL Injection - SOLUTION</b></p>
<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/>
</p>
<pre class="ruby">
def update
message = false
<span style="background-color:yellow">user = current_user</span>
user.skip_user_id_assign = true
user.update_attributes(params[:user].reject { |k| k == ("password" || "password_confirmation") || "user_id" })
pass = params[:user][:password]
user.password = pass if !(pass.blank?)
message = true if user.save!
respond_to do |format|
format.html { redirect_to user_account_settings_path(:user_id => current_user.user_id) }
format.json { render :json => {:msg => message ? "success" : "false "} }
end
end
</pre>
<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:
</p>
<pre class="ruby">
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -153,7 +153,7 @@
I wonder who else's account needs updating?
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A1 - SQL Injection - ActiveRecord Scope
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> A1 - SQL Injection - ActiveRecord Scope
</div>
</div>
<div class="widget-body">
@@ -17,14 +17,14 @@
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
ActiveRecord provides a useful tool for it's Models called a <i>scope</i>. In the words of the documentation:
</p>
<pre><i>
ActiveRecord provides a useful tool for it's Models called a <i>scope</i>. In the words of the documentation:
</p>
<pre><i>
"Scoping allows you to specify commonly-used queries which can be referenced as <br/>method calls on the association objects or models."
</i></pre>
<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.
</p>
</i></pre>
<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.
</p>
</div>
</div>
</div>
@@ -38,41 +38,41 @@
</div>
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within app/models/analytics.rb:
</p>
<pre class="ruby">
class Analytics < ActiveRecord::Base
attr_accessible :ip_address, :referrer, :user_agent
<p class="desc">
Within app/models/analytics.rb:
</p>
<pre class="ruby">
class Analytics < ActiveRecord::Base
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)
calculate(:count, col)
end
</pre>
<p class="desc">
Additionally, within app/controllers/admin_controller.rb:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
<span style="background-color:yellow">fields = params[:field].map {|k,v| k }.join(",")</span>
end
def self.count_by_col(col)
calculate(:count, col)
end
</pre>
<p class="desc">
Additionally, within app/controllers/admin_controller.rb:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
<span style="background-color:yellow">fields = params[:field].map {|k,v| k }.join(",")</span>
end
if params[:ip]
<span style="background-color:yellow">@analytics = Analytics.hits_by_ip(params[:ip], fields)</span>
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<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.
</p>
if params[:ip]
<span style="background-color:yellow">@analytics = Analytics.hits_by_ip(params[:ip], fields)</span>
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<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.
</p>
</div>
</div>
</div>
@@ -87,72 +87,72 @@
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
<div class="accordion-inner">
<p><b>SQL Injection - ATTACK</b></p>
<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:
</p>
<pre>
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
Host: railsgoat.dev
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-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie:[redacted]
Connection: keep-alive
</pre>
<p class="desc">
Essentially we are changing the intended SQL query from:
</p>
<pre>
SELECT <span style="background-color:yellow">UserInput</span> FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p class="desc">
to:
</p>
<pre>
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p><b>SQL Injection - SOLUTION</b></p>
<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>:
</p>
<pre class="ruby">
def self.parse_field(field)
valid_fields = ["ip_address", "referrer", "user_agent"]
<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:
</p>
<pre>
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
Host: railsgoat.dev
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-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie:[redacted]
Connection: keep-alive
</pre>
<p class="desc">
Essentially we are changing the intended SQL query from:
</p>
<pre>
SELECT <span style="background-color:yellow">UserInput</span> FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p class="desc">
to:
</p>
<pre>
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
</pre>
<p><b>SQL Injection - SOLUTION</b></p>
<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>:
</p>
<pre class="ruby">
def self.parse_field(field)
valid_fields = ["ip_address", "referrer", "user_agent"]
if valid_fields.include?(field)
field
else
"1"
end
end
</pre>
<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:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
fields = params[:field].map {|k,v| <span style="background-color:yellow">Analytics.parse_field(k)</span> }.join(",")
end
if valid_fields.include?(field)
field
else
"1"
end
end
</pre>
<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:
</p>
<pre class="ruby">
def analytics
if params[:field].nil?
fields = "*"
else
fields = params[:field].map {|k,v| <span style="background-color:yellow">Analytics.parse_field(k)</span> }.join(",")
end
if params[:ip]
@analytics = Analytics.hits_by_ip(params[:ip], fields)
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<p class="desc">
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
</p>
if params[:ip]
@analytics = Analytics.hits_by_ip(params[:ip], fields)
else
@analytics = Analytics.all
end
render "layouts/admin/_analytics"
end
</pre>
<p class="desc">
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -165,7 +165,7 @@
Administrative analytics functionality need further security analysis. Now might be a good time to test for SQLi.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -18,7 +18,7 @@
<div class="accordion-inner">
<p class="desc">
Virtually every application has these issues because most development teams dont focus on ensuring their components/libraries are up to date. In many cases, the developers dont even know all the components they are using, never mind their versions. Component dependencies make things even worse.
</p>
</p>
</div>
</div>
</div>
@@ -33,16 +33,16 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the Gemfile the following gem versions are set. These versions of Rails and Rack are both vulnerable to multiple attacks.
</p>
<pre class="ruby">
<%= %q{
Within the Gemfile the following gem versions are set. These versions of Rails and Rack are both vulnerable to multiple attacks.
</p>
<pre class="ruby">
<%= %q{
gem 'rails', '3.2.11'
gem 'rack', '1.4.3'
} %>
</pre>
<p class="desc">
</p>
} %>
</pre>
<p class="desc">
</p>
</div>
</div>
</div>
@@ -56,13 +56,13 @@
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<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.
</p>
<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.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -76,6 +76,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,7 +16,7 @@
</div>
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
<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>
@@ -30,33 +30,33 @@
</div>
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the file app/assets/javascripts/jquery.snippet.js:
</p>
<pre>
<%= %{
<p class="desc">
Within the file app/assets/javascripts/jquery.snippet.js:
</p>
<pre>
<%= %{
// snippet new window popup function
function snippetPopup(content) \{
top.consoleRef=window.open('','myconsole',
'width=600,height=300'
+',left=50,top=50'
+',menubar=0'
+',toolbar=0'
+',location=0'
+',status=0'
+',scrollbars=1'
+',resizable=1');
top.consoleRef.document.writeln(
'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">location.href</span><%= %{+'</title></head>'
+'<body bgcolor=white onLoad="self.focus()">'
+'<pre>'+content+'</pre>'
+'</body></html>'
);
top.consoleRef.document.close();
top.consoleRef=window.open('','myconsole',
'width=600,height=300'
+',left=50,top=50'
+',menubar=0'
+',toolbar=0'
+',location=0'
+',status=0'
+',scrollbars=1'
+',resizable=1');
top.consoleRef.document.writeln(
'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">location.href</span><%= %{+'</title></head>'
+'<body bgcolor=white onLoad="self.focus()">'
+'<pre>'+content+'</pre>'
+'</body></html>'
);
top.consoleRef.document.close();
\}}%></pre>
<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.
</p>
<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.
</p>
</div>
</div>
</div>
@@ -70,27 +70,27 @@ function snippetPopup(content) \{
</div>
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
<div class="accordion-inner">
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - ATTACK</b></p>
<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.
</p>
<pre>
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - ATTACK</b></p>
<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.
</p>
<pre>
<%= "http://localhost:3000/tutorials/injection#</title></head><script>alert(1)</script>" %>
</pre>
<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.
</p>
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - SOLUTION</b></p>
<p class="desc">
Use the hoganEscape() function defined in application.js to solve this problem. For instance:
</p>
<pre class="javascript">
</pre>
<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.
</p>
<p><b>Using Components with Known Vulnerabilities (DOM XSS) - SOLUTION</b></p>
<p class="desc">
Use the hoganEscape() function defined in application.js to solve this problem. For instance:
</p>
<pre class="javascript">
<%=%{'<html><head><title>Snippet :: Code View :: '+}%><span style="background-color:yellow">hoganEscape(location.href)</span> <%=%{+'</title></head>' }%>
</pre>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -104,6 +104,6 @@ function snippetPopup(content) \{
</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-inner">
<p class="desc">
Applications frequently use the actual name or key of an object when generating web pages. Applications dont always verify the user is authorized for the target object. This results in an insecure direct object reference flaw. Testers can easily manipulate parameter values to detect such flaws. Code analysis quickly shows whether authorization is properly verified.
</p>
Applications frequently use the actual name or key of an object when generating web pages. Applications dont always verify the user is authorized for the target object. This results in an insecure direct object reference flaw. Testers can easily manipulate parameter values to detect such flaws. Code analysis quickly shows whether authorization is properly verified.
</p>
</div>
</div>
</div>
@@ -33,26 +33,26 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the app/controllers/work_info_controller.rb file the follow code can be found:
</p>
<pre class="ruby">
<%= %q{
class WorkInfoController < ApplicationController
Within the app/controllers/work_info_controller.rb file the follow code can be found:
</p>
<pre class="ruby">
<%= %q{
class WorkInfoController < ApplicationController
def index
@user = User.find_by_user_id(params[:user_id])
if !(@user)
flash[:error] = "Sorry, no user with that user id exists"
redirect_to home_dashboard_index_path
end
end
def index
@user = User.find_by_user_id(params[:user_id])
if !(@user)
flash[:error] = "Sorry, no user with that user id exists"
redirect_to home_dashboard_index_path
end
end
end
} %>
</pre>
<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.
</p>
end
} %>
</pre>
<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.
</p>
</div>
</div>
</div>
@@ -67,27 +67,27 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Insecure Direct Object Reference - ATTACK</b></p>
<p class="desc">
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
</p>
<p><b>Insecure Direct Object Reference - SOLUTION</b></p>
<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).
</p>
<pre class="ruby">
def index
<span style="background-color:yellow">@user = current_user</span>
if !(@user) || @user.admin
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
redirect_to home_dashboard_index_path
end
end
</pre>
<p class="desc">
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
</p>
<p><b>Insecure Direct Object Reference - SOLUTION</b></p>
<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).
</p>
<pre class="ruby">
def index
<span style="background-color:yellow">@user = current_user</span>
if !(@user) || @user.admin
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
redirect_to home_dashboard_index_path
end
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -101,6 +101,6 @@
</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-inner">
<p class="desc">
Regular expressions are a common way to extract the data you want from the data you do <b><u>not</u></b> want. It is common for Ruby developers to forget that in Ruby regexp anchors are \A and \z. This allows strict enforcement so that potentially dangerous characters such as the newline character aren't able to bypass security-based regular expression checks.
</p>
Regular expressions are a common way to extract the data you want from the data you do <b><u>not</u></b> want. It is common for Ruby developers to forget that in Ruby regexp anchors are \A and \z. This allows strict enforcement so that potentially dangerous characters such as the newline character aren't able to bypass security-based regular expression checks.
</p>
</div>
</div>
</div>
@@ -33,69 +33,69 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within the file app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
before_filter :valid_api_token
before_filter :extrapolate_user
</pre>
<p class="desc">
The above two lines specify that we will run these validations prior to allowing a user to interact with the API endpoints.
</p>
<pre class="ruby">
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
<span style="background-color:yellow">id = $1</span>
hash = $2
(id && hash) ? true : false
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
end
def check_hash(id, hash)
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
end
</pre>
<p class="desc">
This first validation, <i>valid_api_token</i>, extracts the user's access token. Within the token there is a user ID and a hash. The application extracts both values, hashes the user ID and the application's secret salt together. If the digest hash matches with the user provided hash, the entire token is valid. <br/><br/>Meaning, if the hash (<i>check_hash</i>) doesn't match the hash provided by the user, the token is invalid and therefore unauthorized. Alternatively, the hash provided is valid but the user ID is invalid. <br/><br/>The next validation, built after this check, extrapolates the user from that hash. In theory, because we have already validated both the user ID and hash are valid, we can just extract the user ID from what has been provided and determine user access.
</p>
<pre class="ruby">
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
end
</pre>
<p class="desc">
Unfortunately, we've made a mistake. The regular expression can be bypassed by entering a newline character (url encoded: <i>%0a</i>).We meant or expected for a user to enter a token such as:
</p>
<pre>
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
However, the user actually enters:
</p>
<pre>
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
This means that our token will pass the initial hash check. Additionally, when we perform the split by the hyphen (<i>"-"</i>) character, and retrieve the first value from the newly created array (what should be a valid user ID), it will be <i>"2\n1"</i>. When performing a <i>find_by_*</i>, ActiveRecord will ignore everything from the newline character on and return the result of the first character. This means, we can become another user!
</p>
Within the file app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
before_filter :valid_api_token
before_filter :extrapolate_user
</pre>
<p class="desc">
The above two lines specify that we will run these validations prior to allowing a user to interact with the API endpoints.
</p>
<pre class="ruby">
def valid_api_token
authenticate_or_request_with_http_token do |token, options|
# TODO :add some functionality to check if the HTTP Header is valid
identify_user(token)
end
end
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
<span style="background-color:yellow">@clean_token =~ /(.*?)-(.*)/</span>
<span style="background-color:yellow">id = $1</span>
hash = $2
(id && hash) ? true : false
<span style="background-color:yellow">check_hash(id, hash) ? true : false</span>
end
def check_hash(id, hash)
<span style="background-color:yellow">digest = OpenSSL::Digest::SHA1.hexdigest("#{ACCESS_TOKEN_SALT}:#{id}")</span>
hash == digest
end
# We had some issues with the token and url encoding...
# this is an attempt to normalize the data.
def unescape_token(token="")
<span style="background-color:yellow">@clean_token = CGI::unescape(token)</span>
end
</pre>
<p class="desc">
This first validation, <i>valid_api_token</i>, extracts the user's access token. Within the token there is a user ID and a hash. The application extracts both values, hashes the user ID and the application's secret salt together. If the digest hash matches with the user provided hash, the entire token is valid. <br/><br/>Meaning, if the hash (<i>check_hash</i>) doesn't match the hash provided by the user, the token is invalid and therefore unauthorized. Alternatively, the hash provided is valid but the user ID is invalid. <br/><br/>The next validation, built after this check, extrapolates the user from that hash. In theory, because we have already validated both the user ID and hash are valid, we can just extract the user ID from what has been provided and determine user access.
</p>
<pre class="ruby">
# Added a method to make it easy to figure out who the user is.
def extrapolate_user
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
end
</pre>
<p class="desc">
Unfortunately, we've made a mistake. The regular expression can be bypassed by entering a newline character (url encoded: <i>%0a</i>).We meant or expected for a user to enter a token such as:
</p>
<pre>
Authorization: Token token=1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
However, the user actually enters:
</p>
<pre>
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
</pre>
<p class="desc">
This means that our token will pass the initial hash check. Additionally, when we perform the split by the hyphen (<i>"-"</i>) character, and retrieve the first value from the newly created array (what should be a valid user ID), it will be <i>"2\n1"</i>. When performing a <i>find_by_*</i>, ActiveRecord will ignore everything from the newline character on and return the result of the first character. This means, we can become another user!
</p>
</div>
</div>
</div>
@@ -110,97 +110,97 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Broken Regular Expression ATTACK:</b></p>
<p class="desc">
As discussed in the Bug Section (above), you can prepend the user ID of the person whose information you would like to retrieve followed by a newline character and your user's valid API token. The following is an example of what our request <i>should</i> look like:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
Content-Length: 4
</pre>
<p class="desc">
This is the response:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
X-Runtime: 0.008342
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
We want to access this endpoint as an admin (user ID of 1). We will change our request so that we can emulate <b>being</b> and admin by prepending 1%0a:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
Content-Length: 4
</pre>
<p class="desc">
The following is a response from the application (note - we get bonus points because as an admin we can retrieve <b> EVERYONE's</b> data):
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
X-Runtime: 0.009111
Connection: close
[{"admin":true,"created_at":"2014-01-23T16:17:10Z","email":"admin@metacorp.com","first_name":
"Admin","id":1,"last_name":"","password":"c93ccd78b2076528346216b3b2f701e6","updated_at":"2014-01-23T16:17:10Z","user_id":1},
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino",
"password":"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"jim@metacorp.com","first_name":"Jim","id":3,"last_name":"Manico","password":
"e1eb29f815193265b57d31bb4d9de140","updated_at":"2014-01-23T16:17:10Z","user_id":3},{"admin":false,
"created_at":"2014-01-23T16:17:10Z","email":"mike@metacorp.com","first_name":"Mike","id":4,"last_name":"McCabe",
"password":"df5d9020fa0f31adc4fd279020f587c8","updated_at":"2014-01-23T16:17:10Z","user_id":4},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"ken@metacorp.com","first_name":"Ken","id":5,"last_name":"Johnson","password":
"67a2faf94e8e71113617d4b72f851bf0","updated_at":"2014-01-23T16:17:10Z","user_id":5},{"admin":null,"created_at":
"2014-03-09T13:58:28Z","email":"test1@test.com","first_name":"test","id":6,"last_name":"test","password":
"05a671c66aefea124cc08b76ea6d30bb","updated_at":"2014-03-09T13:58:28Z","user_id":6},{"admin":null,"created_at":
"2014-03-10T00:13:12Z","email":"test2@test.com","first_name":"test","id":7,"last_name":"test","password":
"91482305bacc71bd52612cce07135b77","updated_at":"2014-03-10T00:13:12Z","user_id":7}]
</pre>
<p><b> Broken Regular Expression SOLUTION:</b></p>
<p class="desc">
There are many things wrong with how we are going about doing this but, for a simple fix, you can anchor the regular expression to reject/ignore newline characters.
</p>
<pre class="ruby">
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
</pre>
<p class="desc">
As discussed in the Bug Section (above), you can prepend the user ID of the person whose information you would like to retrieve followed by a newline character and your user's valid API token. The following is an example of what our request <i>should</i> look like:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
Content-Length: 4
</pre>
<p class="desc">
This is the response:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 0ef6e5e91730bfecb9711c0ddad5cc7b
X-Runtime: 0.008342
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com",
"first_name":"Jack","id":2,"last_name":"Mannino","password":"b46dd2888a0904972649cc880a93f4dd",
"updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
We want to access this endpoint as an admin (user ID of 1). We will change our request so that we can emulate <b>being</b> and admin by prepending 1%0a:
</p>
<pre>
GET /api/v1/users HTTP/1.1
Host: railsgoat.dev
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:26.0) Gecko/20100101 Firefox/26.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Authorization: Token token=<span style="background-color:yellow">1%0a</span>2-050ddd40584978fe9e82840b8b95abb98e4786dc
Content-Length: 4
</pre>
<p class="desc">
The following is a response from the application (note - we get bonus points because as an admin we can retrieve <b> EVERYONE's</b> data):
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "916d3a7b17b24bd84806393e5ef4ccd9"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: e56b6bc1c6d6b875249f6d27b9f9450c
X-Runtime: 0.009111
Connection: close
[{"admin":true,"created_at":"2014-01-23T16:17:10Z","email":"admin@metacorp.com","first_name":
"Admin","id":1,"last_name":"","password":"c93ccd78b2076528346216b3b2f701e6","updated_at":"2014-01-23T16:17:10Z","user_id":1},
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino",
"password":"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"jim@metacorp.com","first_name":"Jim","id":3,"last_name":"Manico","password":
"e1eb29f815193265b57d31bb4d9de140","updated_at":"2014-01-23T16:17:10Z","user_id":3},{"admin":false,
"created_at":"2014-01-23T16:17:10Z","email":"mike@metacorp.com","first_name":"Mike","id":4,"last_name":"McCabe",
"password":"df5d9020fa0f31adc4fd279020f587c8","updated_at":"2014-01-23T16:17:10Z","user_id":4},{"admin":false,"created_at":
"2014-01-23T16:17:10Z","email":"ken@metacorp.com","first_name":"Ken","id":5,"last_name":"Johnson","password":
"67a2faf94e8e71113617d4b72f851bf0","updated_at":"2014-01-23T16:17:10Z","user_id":5},{"admin":null,"created_at":
"2014-03-09T13:58:28Z","email":"test1@test.com","first_name":"test","id":6,"last_name":"test","password":
"05a671c66aefea124cc08b76ea6d30bb","updated_at":"2014-03-09T13:58:28Z","user_id":6},{"admin":null,"created_at":
"2014-03-10T00:13:12Z","email":"test2@test.com","first_name":"test","id":7,"last_name":"test","password":
"91482305bacc71bd52612cce07135b77","updated_at":"2014-03-10T00:13:12Z","user_id":7}]
</pre>
<p><b> Broken Regular Expression SOLUTION:</b></p>
<p class="desc">
There are many things wrong with how we are going about doing this but, for a simple fix, you can anchor the regular expression to reject/ignore newline characters.
</p>
<pre class="ruby">
def identify_user(token="")
# We've had issues with URL encoding, etc. causing issues so just to be safe
# we will go ahead and unescape the user's token
unescape_token(token)
@clean_token =~ /<span style="background-color:yellow">\A</span>(.*?)-(.*)<span style="background-color:yellow">\z</span>/
id = $1
hash = $2
(id && hash) ? true : false
check_hash(id, hash) ? true : false
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -213,7 +213,7 @@
An API token? Interested to see what calls I can make! What are the closing tags for Ruby again?
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseCryptoOne" style="height: auto;">
<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.
</p>
</p>
</div>
</div>
</div>
@@ -32,63 +32,63 @@
</div>
<div class="accordion-body collapse" id="collapseCryptoTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
</p>
<pre class="ruby">
# Added a re-usable encryption routine, shouldn't be an issue!
def self.encrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
new_val = aes.update("#{val}") + aes.final
Base64.strict_encode64(new_val).encode('utf-8')
end
<p class="desc">
Within the file lib/encryption.rb, there are two encryption related methods that we have exposed:
</p>
<pre class="ruby">
# Added a re-usable encryption routine, shouldn't be an issue!
def self.encrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
new_val = aes.update("#{val}") + aes.final
Base64.strict_encode64(new_val).encode('utf-8')
end
def self.decrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
decoded = Base64.strict_decode64("#{val}")
aes.update("#{decoded}") + aes.final
end
</pre>
<p class="desc">
We have placed this code under the lib directory so that we have a re-usable encryption routine. This code is used to generate a user's <b><i>auth_token</b></i> cookie responsible for authorization and access. However, we've also used this same code when encrypting a user's bank account number. This means, a user can enter in any value they would like and will receive it's encrypted equivalent back from the application. Essentially, a user has the ability to generate the auth_token cookie for any user ID and authorize as that user.<br/><br/>
Within the app/models/pay.rb file we have a before hook that will save a user's bank account number as an encrypted value:
</p>
<pre class="ruby">
# callbacks
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
def encrypt_bank_account_num
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
end
</pre>
<p class="desc">
Additionally, we render that encrypted value (purposefully) when the <b><i>show</i></b> action is created within the app/controllers/pay_controller.rb file:
</p>
<pre class="ruby">
def show
respond_to do |format|
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
end
end
</pre>
<p class="desc">
Lastly, we re-use this same routine within the following code is used to create a user's <b><i>auth_token</b></i> cookie upon sign-up or creation (app/models/user.rb):
</p>
<pre class="ruby">
before_create { generate_token(:auth_token) }
def generate_token(column)
begin
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
end while User.exists?(column => self[column])
end
</pre>
def self.decrypt_sensitive_value(val="")
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
decoded = Base64.strict_decode64("#{val}")
aes.update("#{decoded}") + aes.final
end
</pre>
<p class="desc">
We have placed this code under the lib directory so that we have a re-usable encryption routine. This code is used to generate a user's <b><i>auth_token</b></i> cookie responsible for authorization and access. However, we've also used this same code when encrypting a user's bank account number. This means, a user can enter in any value they would like and will receive it's encrypted equivalent back from the application. Essentially, a user has the ability to generate the auth_token cookie for any user ID and authorize as that user.<br/><br/>
Within the app/models/pay.rb file we have a before hook that will save a user's bank account number as an encrypted value:
</p>
<pre class="ruby">
# callbacks
before_save <span style="background-color:yellow">:encrypt_bank_account_num</span>
def encrypt_bank_account_num
self.bank_account_num = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.bank_account_num)</span>
end
</pre>
<p class="desc">
Additionally, we render that encrypted value (purposefully) when the <b><i>show</i></b> action is created within the app/controllers/pay_controller.rb file:
</p>
<pre class="ruby">
def show
respond_to do |format|
format.json { render :json => {:user => <span style="background-color:yellow">current_user.pay.as_json</span>} }
end
end
</pre>
<p class="desc">
Lastly, we re-use this same routine within the following code is used to create a user's <b><i>auth_token</b></i> cookie upon sign-up or creation (app/models/user.rb):
</p>
<pre class="ruby">
before_create { generate_token(:auth_token) }
def generate_token(column)
begin
self[column] = <span style="background-color:yellow">Encryption.encrypt_sensitive_value(self.user_id)</span>
end while User.exists?(column => self[column])
end
</pre>
</div>
</div>
</div>
@@ -103,17 +103,17 @@
<div class="accordion-body collapse" id="collapseCryptoThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Insecure Encryption Re-use ATTACK:</b></p>
<p class="desc">
Navigate to the <b><i>Pay</b></i> section of the application. Enter your bank account number but use the number <b><i>1</i></b> as your bank account number. Once the information is entered and submitted, you'll see the encrypted value of your bank account number (1) returned. URL encode the special characters (+ and ==) and use this value as your <b><i>auth_token</i></b> cookie. Navigate to your dashboard and you'll have the ability to access administrative functionality.
</p>
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
<p class="desc">
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
</p>
<p class="desc">
Navigate to the <b><i>Pay</b></i> section of the application. Enter your bank account number but use the number <b><i>1</i></b> as your bank account number. Once the information is entered and submitted, you'll see the encrypted value of your bank account number (1) returned. URL encode the special characters (+ and ==) and use this value as your <b><i>auth_token</i></b> cookie. Navigate to your dashboard and you'll have the ability to access administrative functionality.
</p>
<p><b> Insecure Encryption Re-use SOLUTION:</b></p>
<p class="desc">
Create an entirely new encryption routine or create the SHA1 hash with a different salt.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseCryptoFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -126,7 +126,7 @@
My "Remember Me" cookie looks familiar, almost like one of those values you get when you enter your bank account number.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -17,8 +17,8 @@
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<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.
</p>
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>
</div>
</div>
</div>
@@ -33,29 +33,29 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
The bug is introduced within app/models/user.rb, seen on line 3 (:admin):
</p>
<p>
<pre class="ruby">
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :admin, :password_confirmation, :first_name, :last_name
} %>
</pre>
</p>
<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:
</p>
<pre class="ruby">
# Note the string "true"/"false" or 1/0, etc. can be added to specify the boolean attribute...
# is true or false thanks to ActiveRecord
User.new(:email => "email@email.com",
:admin => "true",
:password => "h4xx0r",
:first_name => "Captain",
:last_name => "Crunch"
)
</pre>
The bug is introduced within app/models/user.rb, seen on line 3 (:admin):
</p>
<p>
<pre class="ruby">
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :admin, :password_confirmation, :first_name, :last_name
} %>
</pre>
</p>
<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:
</p>
<pre class="ruby">
# Note the string "true"/"false" or 1/0, etc. can be added to specify the boolean attribute...
# is true or false thanks to ActiveRecord
User.new(:email => "email@email.com",
:admin => "true",
:password => "h4xx0r",
:first_name => "Captain",
:last_name => "Crunch"
)
</pre>
</div>
</div>
</div>
@@ -69,67 +69,67 @@
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Mass Assignment ATTACK:</b></p>
<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...
</p>
<pre class="ruby">
POST /users HTTP/1.1
Host: railsgoat.dev
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-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/signup
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 248
<p><b> Mass Assignment ATTACK:</b></p>
<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...
</p>
<pre class="ruby">
POST /users HTTP/1.1
Host: railsgoat.dev
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-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://railsgoat.dev/signup
Cookie: _railsgoat_session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
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
</pre>
<p>
...and the attack is quite simple. Append a parameter to the body of this POST request that specifies the admin value is true.
</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>
<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.
</p>
<pre class="ruby">
def create
<span style="background-color: yellow">user = User.new(params[:user])</span>
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_work_info(POPULATE_WORK_INFO.shuffle.first)
user.performance.build(POPULATE_PERFORMANCE.shuffle.first)
if user.save
session[:user_id] = user.user_id
redirect_to home_dashboard_index_path
else
@user = user
render :new
end
end
</pre>
<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.
</p>
<p><b> Mass Assignment SOLUTION:</b></p>
<p class="desc">
The solution is fairly simple, remove the admin attribute from the attr_accessible method. The following code shows what we mean:
</p>
<pre class="ruby">
<span style="background-color:yellow"># Note that the admin attr has been removed </span>
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
} %>
</pre>
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>
<p>
...and the attack is quite simple. Append a parameter to the body of this POST request that specifies the admin value is true.
</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>
<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.
</p>
<pre class="ruby">
def create
<span style="background-color: yellow">user = User.new(params[:user])</span>
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_work_info(POPULATE_WORK_INFO.shuffle.first)
user.performance.build(POPULATE_PERFORMANCE.shuffle.first)
if user.save
session[:user_id] = user.user_id
redirect_to home_dashboard_index_path
else
@user = user
render :new
end
end
</pre>
<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.
</p>
<p><b> Mass Assignment SOLUTION:</b></p>
<p class="desc">
The solution is fairly simple, remove the admin attribute from the attr_accessible method. The following code shows what we mean:
</p>
<pre class="ruby">
<span style="background-color:yellow"># Note that the admin attr has been removed </span>
<%= %q{
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
} %>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -139,12 +139,12 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Did you register your account correctly? How about when you updated your settings?
</p>
</p>
</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-inner">
<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.
</p>
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>
</div>
</div>
</div>
@@ -33,22 +33,22 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
Within the file app/controllers/benefit_forms_controller.rb:
</p>
<pre class="ruby">
def download
begin
<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>
send_file file, :disposition => 'attachment'
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<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.
</p>
Within the file app/controllers/benefit_forms_controller.rb:
</p>
<pre class="ruby">
def download
begin
<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>
send_file file, :disposition => 'attachment'
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<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.
</p>
</div>
</div>
</div>
@@ -63,55 +63,55 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b> Constantize ATTACK:</b></p>
<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.
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
</pre>
<p>
Change the name parameter to something a little more fun like:
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=../../config/initializers/secret_token.rb&type=File
</pre>
<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:
</p>
<pre class="ruby">
../../../../../../../etc/passwd&type=File
</pre>
<p><b> Constantize SOLUTION:</b></p>
<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:
</p>
<pre class="ruby">
# More secure version
def download
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
begin
<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])
<span style="background-color:yellow">if params[:type] == "File"</span>
file = params[:type].constantize.new(path)
send_file file, :disposition => 'attachment'
end
else
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
send_file file, :disposition => 'attachment'
end
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<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.
</p>
<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.
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
</pre>
<p>
Change the name parameter to something a little more fun like:
</p>
<pre class="ruby">
http://railsgoat.dev/download?name=../../config/initializers/secret_token.rb&type=File
</pre>
<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:
</p>
<pre class="ruby">
../../../../../../../etc/passwd&type=File
</pre>
<p><b> Constantize SOLUTION:</b></p>
<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:
</p>
<pre class="ruby">
# More secure version
def download
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
begin
<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])
<span style="background-color:yellow">if params[:type] == "File"</span>
file = params[:type].constantize.new(path)
send_file file, :disposition => 'attachment'
end
else
file = Rails.root.join('public', 'docs', "Dental_n_Stuff.pdf")
send_file file, :disposition => 'attachment'
end
rescue
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
end
end
</pre>
<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.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -124,7 +124,7 @@
It can be very helpful for employees to download benefit forms.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -52,7 +52,7 @@
</div>
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
The solution for this issue is quite simple. In your application.rb file set the configuration as follows.
The solution for this issue is quite simple. In your application.rb file set the configuration as follows.
<pre class="ruby">
<%= %q{
config.active_record.whitelist_attributes=true
@@ -63,7 +63,7 @@
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -73,10 +73,10 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
It has to do with mass-assignment, whitelisting and configuration.
It has to do with mass-assignment, whitelisting and configuration.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -61,7 +61,7 @@
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -71,10 +71,10 @@
</div>
<div class="accordion-body collapse" id="collapseEight" style="height: 0px;">
<div class="accordion-inner">
Think HTML entities, escaping and initializers.
Think HTML entities, escaping and initializers.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -16,13 +16,13 @@
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<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.
Detecting unchecked redirects is easy. Look for redirects where you can set the full URL. Unchecked forwards are harder, because they target internal pages.
</p>
<p class="desc">
</p>
<p class="desc">
Railsgoat allows the redirection to the paths previously requested but for which the user did not have access. Following authentication, the user is redirected.
</p>
</p>
</div>
</div>
</div>
@@ -36,31 +36,31 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<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.
</p>
<pre class="ruby">
def create
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
rescue Exception => e
end
<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.
</p>
<pre class="ruby">
def create
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
begin
# Normalize the email address, why not
user = User.authenticate(params[:email].to_s.downcase, params[:password])
# @url = params[:url]
rescue Exception => e
end
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
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
</pre>
if user
session[:user_id] = user.user_id if User.where(:user_id => user.user_id).exists?
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
</pre>
</div>
</div>
</div>
@@ -75,29 +75,29 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Unvalidated Redirects and Forwards - ATTACK</b></p>
<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.
</p>
<p><b>Unvalidated Redirects and Forwards - SOLUTION</b></p>
<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:
</p>
<pre class="ruby">
path = home_dashboard_index_path
begin
if params[:url].present?
path = URI.parse(params[:url]).path
end
rescue
end
</pre>
<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.
</p>
<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.
</p>
<p><b>Unvalidated Redirects and Forwards - SOLUTION</b></p>
<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:
</p>
<pre class="ruby">
path = home_dashboard_index_path
begin
if params[:url].present?
path = URI.parse(params[:url]).path
end
rescue
end
</pre>
<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.
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -107,12 +107,12 @@
</div>
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Read the description section, fairly big hint there.
</p>
</p>
</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-inner">
<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.
</p>
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>
</div>
</div>
</div>
@@ -33,8 +33,8 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<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.
</p>
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>
</div>
</div>
</div>
@@ -49,21 +49,21 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<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:
</p>
<pre class="ruby">
config.force_ssl = true
</pre>
<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):
</p>
<pre class="ruby">
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
</pre>
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>
<pre class="ruby">
config.force_ssl = true
</pre>
<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):
</p>
<pre class="ruby">
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -31,16 +31,16 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Rails provides the ability to apply before_filter(s) which run prior to rendering content to the user. This is helpful when restricting access to content based on the user's role. 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>
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
skip_before_filter :has_info
} %>
</pre>
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>
<pre class="ruby">
<%= %q{
class AdminController < ApplicationController
skip_before_filter :has_info
} %>
</pre>
</div>
</div>
</div>
@@ -55,40 +55,40 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Failure to Restrict URL Access - ATTACK</b></p>
<p class="desc">
Request the following URL /admin/1/dashboard and have fun :-)
</p>
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
<p class="desc">
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb:
</p>
<pre class="ruby">
helper_method :current_user, <span style="background-color:yellow">:is_admin?</span>
def is_admin?
current_user.admin if current_user
end
def administrative
if not is_admin?
reset_session
redirect_to root_url
end
end
</pre>
<p>
Then add the following line within app/controllers/admin_controller.rb
</p>
<pre class="ruby">
class AdminController < ApplicationController
<p class="desc">
Request the following URL /admin/1/dashboard and have fun :-)
</p>
<p><b>Failure to Restrict URL Access - SOLUTION</b></p>
<p class="desc">
The code is already available to restrict access to the admin controller by role within app/controllers/application_controller.rb:
</p>
<pre class="ruby">
helper_method :current_user, <span style="background-color:yellow">:is_admin?</span>
<span style="background-color:yellow">before_filter :administrative</span>
skip_before_filter :has_info
</pre>
def is_admin?
current_user.admin if current_user
end
def administrative
if not is_admin?
reset_session
redirect_to root_url
end
end
</pre>
<p>
Then add the following line within app/controllers/admin_controller.rb
</p>
<pre class="ruby">
class AdminController < ApplicationController
<span style="background-color:yellow">before_filter :administrative</span>
skip_before_filter :has_info
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -102,6 +102,6 @@
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -11,13 +11,13 @@
<a href="#collapseFive" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-info icon-white">
</i>
Description
Description
</a>
</div>
<div class="accordion-body in collapse" id="collapseFive" style="height: auto;">
<div class="accordion-inner">
<p>
DOM Based XSS (or as it is called in some texts, “type-0 XSS”) is an XSS attack wherein the attack payload is executed as a result of modifying the DOM “environment” in the victims browser used by the original client side script, so that the client side code runs in an “unexpected” manner. That is, the page itself (the HTTP response that is) does not change, but the client side code contained in the page executes differently due to the malicious modifications that have occurred in the DOM environment.
DOM Based XSS (or as it is called in some texts, “type-0 XSS”) is an XSS attack wherein the attack payload is executed as a result of modifying the DOM “environment” in the victims browser used by the original client side script, so that the client side code runs in an “unexpected” manner. That is, the page itself (the HTTP response that is) does not change, but the client side code contained in the page executes differently due to the malicious modifications that have occurred in the DOM environment.
</p>
</div>
</div>
@@ -33,30 +33,30 @@
<div class="accordion-body collapse" id="collapseSix" style="height: 0px;">
<div class="accordion-inner">
<p>
The following code was taken from app/views/sessions/new.html.erb:
</p>
<pre class="javascript">
<%=
%{
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" +} %> <span style="background-color:yellow"> paramValue</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
</script>
}
%>
</pre>
<p class="desc">
The code (above) takes user input (params), and renders it back on the page without any output encoding or escaping.
</p>
The following code was taken from app/views/sessions/new.html.erb:
</p>
<pre class="javascript">
<%=
%{
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" +} %> <span style="background-color:yellow"> paramValue</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
</script>
}
%>
</pre>
<p class="desc">
The code (above) takes user input (params), and renders it back on the page without any output encoding or escaping.
</p>
</div>
</div>
</div>
@@ -71,40 +71,40 @@
<div class="accordion-body collapse" id="collapseSeven" style="height: 0px;">
<div class="accordion-inner">
<p><b> Stored Cross-Site Scripting ATTACK:</b></p>
<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:
</p>
<pre>
<%= %{http://127.0.0.1:3000/#test=<script>alert(1)</script>} %>
</pre>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
<p>
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
</p>
<pre class="javascript">
<%= %{
<!-- support for multiple languages coming soon! -->
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" + } %> <span style="background-color:yellow"> hoganEscape(paramValue)</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
</script>
}
%>
</pre>
<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:
</p>
<pre>
<%= %{http://127.0.0.1:3000/#test=<script>alert(1)</script>} %>
</pre>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
<p>
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
</p>
<pre class="javascript">
<%= %{
<!-- support for multiple languages coming soon! -->
<script>
//document.write("<select style=\"width: 100px;\">");
//document.write("<OPTION value=1>English</OPTION>");
//document.write("<OPTION value=2>Spanish</OPTION>");
try \{
var hashParam = location.hash.split("#")[1];
var paramName = hashParam.split('=')[0];
var paramValue = hashParam.split('=')[1];
document.write("<OPTION value=3>" + } %> <span style="background-color:yellow"> hoganEscape(paramValue)</span> <%= %{ + "</OPTION>");
\} catch(err) \{
\}
//document.write("</select>");
</script>
}
%>
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseEight" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -115,11 +115,11 @@
<div class="accordion-body collapse" id="collapseEight" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
You should view the source of the login page, might be something interesting there.
</p>
You should view the source of the login page, might be something interesting there.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -31,23 +31,23 @@
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<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>
<pre class="ruby">
<%= @code %>
</pre>
<p>
<pre class="ruby">
<%= @code %>
</pre>
</p>
<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.
<pre class="ruby">
# Psuedo-code to help conceptualize
def raw(dirty_string)
dirty_string.to_s.html_safe
end
</pre>
</p>
<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.
<pre class="ruby">
# Psuedo-code to help conceptualize
def raw(dirty_string)
dirty_string.to_s.html_safe
end
</pre>
</p>
</div>
</div>
</div>
@@ -63,16 +63,16 @@
<div class="accordion-inner">
<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>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></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().
</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>
<p><b> Stored Cross-Site Scripting SOLUTION:</b></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().
</p>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -83,12 +83,12 @@
<div class="accordion-body collapse" id="collapseFour" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Apparently we had some issues rendering people's names with weird formatting or something, I dunno, I think I fixed it by safely encoding html and rendering the necessary content.<br/><br/>
You're <b>Welcome</b>!
</p>
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>!
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
+8 -9
View File
@@ -8,20 +8,19 @@
<!--[if lte IE 7]>
<script src="assets/fonts/lte-ie7.js">
</script>
<![endif]-->
<![endif]-->
</head>
<body>
<%= render "layouts/tutorial/header" %>
<%= render "layouts/tutorial/sidebar" %>
<%= render "layouts/tutorial/header" %>
<%= render "layouts/tutorial/sidebar" %>
<div class="container-fluid">
<div class="dashboard-wrapper">
<%= yield %>
</div>
</div>
<%= render "layouts/shared/footer" %>
<div class="dashboard-wrapper">
<%= yield %>
</div>
</div>
<%= render "layouts/shared/footer" %>
</body>
</html>
+57 -57
View File
@@ -1,8 +1,8 @@
<div class="dashboard-wrapper">
<div class="main-container">
<!-- Begin Row -->
<!-- Begin Row -->
<div class="row-fluid">
<!-- Begin Span12 -->
<!-- Begin Span12 -->
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -39,12 +39,12 @@
</div>
</div>
</div>
<!-- End Span12 -->
<!-- End Span12 -->
</div>
<!-- End Row -->
<!-- Begin Row -->
<!-- Begin Row -->
<div class="row-fluid">
<!-- Begin Span12 -->
<!-- Begin Span12 -->
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -53,15 +53,15 @@
</div>
</div>
<div class="widget-body">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Message successfully sent.
</p>
</div>
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Message successfully sent.
</p>
</div>
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -69,66 +69,66 @@
Failed to send message.
</p>
</div>
<div class="row-fluid">
<div class="span8">
<!-- Begin Message Draft Content-->
<%= 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 :read, :value => '0' %>
<div class="control-group">
<%= f.label "To:", nil, {:class => "control-label"}%>
<%= f.select(:receiver_id, options_from_collection_for_select(User.all, :id, :full_name)) %>
</div>
<div class="control-group">
<%= f.label :message, nil, {:class => "control-label"}%>
<%= f.text_area :message, {:class => "span12"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="clearfix"></div>
<% end %>
<!-- End Message Draft Content-->
</div>
</div>
<div class="row-fluid">
<div class="span8">
<!-- Begin Message Draft Content-->
<%= 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 :read, :value => '0' %>
<div class="control-group">
<%= f.label "To:", nil, {:class => "control-label"}%>
<%= f.select(:receiver_id, options_from_collection_for_select(User.all, :id, :full_name)) %>
</div>
<div class="control-group">
<%= f.label :message, nil, {:class => "control-label"}%>
<%= f.text_area :message, {:class => "span12"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="clearfix"></div>
<% end %>
<!-- End Message Draft Content-->
</div>
</div>
</div>
</div>
</div>
<!-- End Span12 -->
<!-- End Span12 -->
</div>
<!-- End Row -->
</div>
</div>
</div>
</body>
</html>
<script type="text/javascript">
$("#submit_button").click(function(event) {
var valuesToSubmit = $("#send_message").serialize();
var valuesToSubmit = $("#send_message").serialize();
event.preventDefault();
$.ajax({
url: <%= "/users/#{current_user.user_id}/messages.json".inspect.html_safe %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
url: <%= "/users/#{current_user.user_id}/messages.json".inspect.html_safe %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
function makeActive(){
$('li[id="messages"]').addClass('active');
$('li[id="messages"]').addClass('active');
};
$(document).ready(function () {
+2 -2
View File
@@ -33,9 +33,9 @@
</div>
</div>
</div>
<script type="text/javascript">
<script type="text/javascript">
function makeActive(){
$('li[id="messages"]').addClass('active');
$('li[id="messages"]').addClass('active');
};
$(document).ready(function () {
+93 -93
View File
@@ -1,20 +1,20 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -22,25 +22,25 @@
Failed to update.
</p>
</div>
</div>
</div>
<!-- Begin DP-->
<div class="row-fluid">
<!-- begin cal -->
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe097;"></span> PTO Calendar
</div>
</div>
<div id="calendarDiv" class="widget-body">
<div id='calendar'></div>
</div>
</div>
</div>
<!-- End cal-->
<div class="span6">
</div>
</div>
<!-- Begin DP-->
<div class="row-fluid">
<!-- begin cal -->
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe097;"></span> PTO Calendar
</div>
</div>
<div id="calendarDiv" class="widget-body">
<div id='calendar'></div>
</div>
</div>
</div>
<!-- End cal-->
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
@@ -48,46 +48,46 @@
</div>
</div>
<!-- Begin WB-->
<div id="scheduleDiv" class="widget-body">
<%= form_for @schedule, :url => "#",:html => {:id => "cal_update"} do |s|%>
<div class="control-group">
<%= s.label :event_name, "Event Name", {:class => "control-label"}%>
<%= s.text_field :event_name, {:placeholder => "My PTO", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.text_field :event_type, {:type => "hidden", :value => "pto", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.label :event_desc, "Event Description", {:class => "control-label"}%>
<%= s.text_field :event_desc, {:placeholder => "Travel to Europe", :class => "span6"}%>
</div>
<div class="control-group">
<label class="control-label" for="date_range1">
Event Dates
</label>
<div class="controls">
<div class="input-append">
<input type="text" name="date_range1" id="date_range1" class="span8 date_picker" placeholder="Select Date"/>
<span class="add-on">
<i class="icon-calendar"></i>
</span>
</div>
</div>
</div>
<%= s.submit "Submit", {:id => 'cal_update_submit', :class => "btn btn-primary pull-left"} %>
<% end %>
<div class="clearfix">
<div id="scheduleDiv" class="widget-body">
<%= form_for @schedule, :url => "#",:html => {:id => "cal_update"} do |s|%>
<div class="control-group">
<%= s.label :event_name, "Event Name", {:class => "control-label"}%>
<%= s.text_field :event_name, {:placeholder => "My PTO", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.text_field :event_type, {:type => "hidden", :value => "pto", :class => "span6"}%>
</div>
<div class="control-group">
<%= s.label :event_desc, "Event Description", {:class => "control-label"}%>
<%= s.text_field :event_desc, {:placeholder => "Travel to Europe", :class => "span6"}%>
</div>
<div class="control-group">
<label class="control-label" for="date_range1">
Event Dates
</label>
<div class="controls">
<div class="input-append">
<input type="text" name="date_range1" id="date_range1" class="span8 date_picker" placeholder="Select Date"/>
<span class="add-on">
<i class="icon-calendar"></i>
</span>
</div>
</div>
</div>
<%= s.submit "Submit", {:id => 'cal_update_submit', :class => "btn btn-primary pull-left"} %>
<% end %>
<div class="clearfix">
</div>
</div>
</div>
<!-- End WB-->
</div>
</div>
</div>
</div>
<!-- End DP-->
<div class="row-fluid">
</div>
<!-- End DP-->
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -101,7 +101,7 @@
</div>
</div>
</div>
<div class="row-fluid">
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -115,7 +115,7 @@
</div>
</div>
</div>
</div>
</div>
</div>
<%= javascript_include_tag "fullcalendar.js" %>
@@ -125,12 +125,12 @@
function makeActive(){
$('li[id="pto"]').addClass('active');
$('li[id="pto"]').addClass('active');
};
$('#calendar').fullCalendar({
events: <%= get_pto_schedule_schedule_index_path(:format => "json").inspect.html_safe %>,
events: <%= get_pto_schedule_schedule_index_path(:format => "json").inspect.html_safe %>,
});
@@ -147,7 +147,7 @@ $('.date_picker').daterangepicker({
makeActive()
});
google.load("visualization", "1", {
google.load("visualization", "1", {
packages: ["corechart"]
});
@@ -220,29 +220,29 @@ function drawChart2() {
}
function resizeTopWidgets(){
var calHeight = $("#calendarDiv").height();
var calHeight = $("#calendarDiv").height();
$("#scheduleDiv").css({'height':calHeight});
};
$("#cal_update_submit").click(function(event) {
var valuesToSubmit = $("#cal_update").serialize();
var valuesToSubmit = $("#cal_update").serialize();
event.preventDefault();
$.ajax({
url: "/schedule.json",
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
$('#calendar').fullCalendar('refetchEvents')
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
$('#calendar').fullCalendar('refetchEvents')
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
</script>
+196 -196
View File
@@ -1,20 +1,20 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -22,11 +22,11 @@
Failed to update.
</p>
</div>
</div>
</div>
<!-- Begin Row-Fluid for Inputs -->
<div class="row-fluid">
<div class="span9">
</div>
</div>
<!-- Begin Row-Fluid for Inputs -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
@@ -34,90 +34,90 @@
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
<span class="add-on">%</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "bank_info_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :bank_account_num, params[:bank_account_num], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :bank_routing_num, params[:bank_routing_num], {:placeholder => "Bank Routing Number"} %>
<span class="add-on">#</span>
</div>
<div class="input-append">
<%= text_field_tag :dd_percent, params[:dd_percent], {:placeholder => "Percentage of Deposit"} %>
<span class="add-on">%</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "dd_form_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- End Row-Fluid for Inputs-->
<!-- ###################-->
<!-- Begin Dynamic Table ColSpan Table -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
</div>
<!-- End Row-Fluid for Inputs-->
<!-- Begin Widget Header-->
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Accounts
</div>
</div>
<!-- End Widget Header-->
<div class="widget-body">
<div id="dt_example" class="example_alt_pagination">
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
<thead>
<tr>
<th style="width:30%">
Encrypted Bank Account Number
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
<span class="box1">
<span aria-hidden="true" class="icon-question"></span>
</span>
<% end %>
</th>
<th style="width:25%">
Bank Routing Number
</th>
<th style="width:20%">
Percentage of Deposit
</th>
<th style="width:25%">
Action
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="clearfix">
</div>
</div>
</div> <!-- end of widget body-->
</div>
</div>
</div
<!-- End Dynamic Table ColSpan Table -->
<!-- ###################-->
<!-- Begin Row-Fluid for Decryption Input -->
<div class="row-fluid">
<div class="span9">
<!-- ###################-->
<!-- Begin Dynamic Table ColSpan Table -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<!-- Begin Widget Header-->
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Accounts
</div>
</div>
<!-- End Widget Header-->
<div class="widget-body">
<div id="dt_example" class="example_alt_pagination">
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
<thead>
<tr>
<th style="width:30%">
Encrypted Bank Account Number
<%=link_to "#", { :style => "color:#AA6F93", :id => "encrypted_acct_question"} do %>
<span class="box1">
<span aria-hidden="true" class="icon-question"></span>
</span>
<% end %>
</th>
<th style="width:25%">
Bank Routing Number
</th>
<th style="width:20%">
Percentage of Deposit
</th>
<th style="width:25%">
Action
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="clearfix">
</div>
</div>
</div> <!-- end of widget body-->
</div>
</div>
</div
<!-- End Dynamic Table ColSpan Table -->
<!-- ###################-->
<!-- Begin Row-Fluid for Decryption Input -->
<div class="row-fluid">
<div class="span9">
<div class="widget">
<div class="widget-header">
<div class="title">
@@ -125,25 +125,25 @@
</div>
</div>
<div class="widget-body">
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
<div class="row-fluid">
<%= form_tag "#", {:class => "form-horizontal", :id => "decrypt_form" } do %>
<!-- Begin inputs-->
<div class="input-append">
<%= text_field_tag :value_to_decrypt, params[:value_to_decrypt], {:placeholder => "Bank Account Number"} %>
<span class="add-on">#</span>
</div>
<!-- End Inputs -->
<%= submit_tag "Submit", {:id => "decrypt_btn", :style => "margin-left: 10px;", :class => "btn btn-medium btn-primary"} %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- Row-Fluid for Decryption Input -->
</div>
</div>
<!-- Row-Fluid for Decryption Input -->
</div>
</div>
<%= javascript_include_tag "jquery.dataTables.js" %>
@@ -155,60 +155,60 @@
a user to delete that direct deposit entry
*/
function buildDeleteLink(dd_id){
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
'<i class="icon-trash">'+
'</i></a>'
return link
var link = '<a href="/users/' + '<%= current_user.id %>' + '/pay/'+ dd_id + '" data-method="delete" rel="nofollow" class="delete-row">' +
'<i class="icon-trash">'+
'</i></a>'
return link
};
/*
parseDirectDepositInfo accepts the response object and parses the JSON response, then
populates the direct deposit data table.
parseDirectDepositInfo accepts the response object and parses the JSON response, then
populates the direct deposit data table.
*/
function parseDirectDepostInfo(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
$.each(msg.user, function(index, val){
$('#data_table').dataTable().fnAddData( [
val.bank_account_num,
val.bank_routing_num,
val.percent_of_deposit,
buildDeleteLink(val.id)
] );
});
var msg = jQuery.parseJSON(JSON.stringify(response));
$.each(msg.user, function(index, val){
$('#data_table').dataTable().fnAddData( [
val.bank_account_num,
val.bank_routing_num,
val.percent_of_deposit,
buildDeleteLink(val.id)
] );
});
};
/*
populateTable will first clear the existing dd table, then call the appropriate
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
with the response from the endpoint in order to populate the data table.
populateTable will first clear the existing dd table, then call the appropriate
endpoint to retrieve direct deposit entries and finally, provide parseDirectDepositInfo
with the response from the endpoint in order to populate the data table.
*/
function populateTable() {
$('#data_table').dataTable().fnClearTable();
$.ajax({
$('#data_table').dataTable().fnClearTable();
$.ajax({
url: <%= sanitize(user_pay_path(:format => "json", :user_id => current_user.user_id, :id => current_user.user_id).inspect) %>,
type: "GET",
success: function(response) {
parseDirectDepostInfo(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
type: "GET",
success: function(response) {
parseDirectDepostInfo(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
};
/*
createDataTable initializes the dd table as a datatable
createDataTable initializes the dd table as a datatable
*/
function createDataTable(){
$('#data_table').dataTable({
$('#data_table').dataTable({
"sPaginationType": "full_numbers"
});
});
};
/*
This function doesn't really work right now but is supposed to offer the user a
"delete confirmation" message
This function doesn't really work right now but is supposed to offer the user a
"delete confirmation" message
*/
$('.delete-row').click(function () {
var conf = confirm('Continue delete?');
@@ -219,79 +219,79 @@ $('.delete-row').click(function () {
});
/*
decryptShow parses the json response from the application and then renders
a decrypted version of the user's account number
decryptShow parses the json response from the application and then renders
a decrypted version of the user's account number
*/
function decryptShow(response){
var msg = jQuery.parseJSON(JSON.stringify(response));
alert("Decrypted Account Number: " + msg.account_num);
var msg = jQuery.parseJSON(JSON.stringify(response));
alert("Decrypted Account Number: " + msg.account_num);
};
/*
This function overrides the decrypt buttons (submit button's) native behavior,
allowing an ajax call to be made with the decrypt_form's inputs which is decrypted
server side with a JSON response containing the decrypted value. The decrypted value is
then passed to decryptShow();
This function overrides the decrypt buttons (submit button's) native behavior,
allowing an ajax call to be made with the decrypt_form's inputs which is decrypted
server side with a JSON response containing the decrypted value. The decrypted value is
then passed to decryptShow();
*/
$("#decrypt_btn").click(function(event){
var valuesToSubmit = $("#decrypt_form").serialize();
event.preventDefault();
$.ajax({
var valuesToSubmit = $("#decrypt_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(decrypted_bank_acct_num_user_pay_index_path(:format => "json", :user_id => current_user.user_id).inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
decryptShow(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
decryptShow(response);
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
/*
This function overrides the dd_form_btn's native behavior in order to submit an ajax request
that updates the user's direct deposit information. Upon success, the populateTable() function
is called in order to update the dataTable on the page to reflect the latest entry.
This function overrides the dd_form_btn's native behavior in order to submit an ajax request
that updates the user's direct deposit information. Upon success, the populateTable() function
is called in order to update the dataTable on the page to reflect the latest entry.
*/
$("#dd_form_btn").click(function(event) {
var valuesToSubmit = $("#bank_info_form").serialize();
event.preventDefault();
$.ajax({
url: <%= sanitize(update_dd_info_user_pay_index_path(:format => "json").inspect) %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
populateTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
$('#success').show(500).delay(1500).fadeOut();
populateTable();
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
$("#encrypted_acct_question").click(function(event) {
event.preventDefault();
alert("For your safety your account number is stored encrypted as well as presented to you \nin an encrypted form.\n\n" +
"For your convenience, you can decrypt your bank account number at any time using our\n" +
"conveniently located decryption function."
)
event.preventDefault();
alert("For your safety your account number is stored encrypted as well as presented to you \nin an encrypted form.\n\n" +
"For your convenience, you can decrypt your bank account number at any time using our\n" +
"conveniently located decryption function."
)
});
/*
Make the sidebar element "Pay" active.
Make the sidebar element "Pay" active.
*/
function makeActive(){
$('li[id="pay"]').addClass('active');
$('li[id="pay"]').addClass('active');
};
/*
1) makeActive - Adds the active class to the sidebar element
2) createDataTable - Initializes the dataTable as such
3) populateTable - populates the newly initialized dataTable
*/
1) makeActive - Adds the active class to the sidebar element
2) createDataTable - Initializes the dataTable as such
3) populateTable - populates the newly initialized dataTable
*/
$(document).ready(
makeActive,
createDataTable(),
+38 -39
View File
@@ -1,6 +1,6 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div class="widget">
<div class="widget-header">
@@ -13,45 +13,44 @@
</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="&#xe004;"></span> Performance History
</div>
</div>
<div class="widget-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width:16%">Reviewer</th>
<th style="width:16%">Date</th>
<th style="width:16%">Score</th>
<th style="width:16%">Comments</th>
</tr>
</thead>
<tbody>
<% @perf.each do |p| %>
<tr>
<td><%= p.reviewer_name %></td>
<td><%= p.date_submitted %></td>
<td><%= p.score %></td>
<td><%= p.comments %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</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="&#xe004;"></span> Performance History
</div>
</div>
<div class="widget-body">
<table class="table table-bordered table-striped">
<thead>
<tr>
<th style="width:16%">Reviewer</th>
<th style="width:16%">Date</th>
<th style="width:16%">Score</th>
<th style="width:16%">Comments</th>
</tr>
</thead>
<tbody>
<% @perf.each do |p| %>
<tr>
<td><%= p.reviewer_name %></td>
<td><%= p.date_submitted %></td>
<td><%= p.score %></td>
<td><%= p.comments %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
google.load("visualization", "1", {
packages: ["corechart"]
});
@@ -66,7 +65,7 @@ function drawChart2() {
]);
var options = {
min: 1,
min: 1,
max: 5,
width: 'auto',
height: '160',
@@ -116,7 +115,7 @@ function drawChart2() {
}
function makeActive(){
$('li[id="performance"]').addClass('active');
$('li[id="performance"]').addClass('active');
};
$(document).ready(function () {
+76 -76
View File
@@ -1,84 +1,84 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe071;"></span> Employee Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="expenses">
<h3><%= @info.employee_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe075;"></span> Employer Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="signups">
<h3><%= @info.employer_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Total Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="income">
<h3><%= @info.total %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
</div>
<div class="row-fluid">
<div class="span6"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0d4;"></span> Employee Services
</div>
</div>
<div class="widget-body">
<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.
</p>
<hr/>
<p>
MetaCorp is dedicated to its employees and this service is just one way of showing it!
</p>
</div>
</div>
</div> <!-- End of span-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe071;"></span> Employee Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="expenses">
<h3><%= @info.employee_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe075;"></span> Employer Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="signups">
<h3><%= @info.employer_contrib %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
<div class="span3"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe14a;"></span> Total Contribution
</div>
</div>
<div class="widget-body">
<div class="current-statistics">
<div class="income">
<h3><%= @info.total %></h3>
</div>
</div>
</div>
</div>
</div> <!-- End of span-->
</div>
<div class="row-fluid">
<div class="span6"> <!-- Beginning of span-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0d4;"></span> Employee Services
</div>
</div>
<div class="widget-body">
<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.
</p>
<hr/>
<p>
MetaCorp is dedicated to its employees and this service is just one way of showing it!
</p>
</div>
</div>
</div> <!-- End of span-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="retirement"]').addClass('active');
};
function makeActive(){
$('li[id="retirement"]').addClass('active');
};
$(document).ready(makeActive)
</script>
+8 -10
View File
@@ -36,16 +36,14 @@
</div>
<div class="actions">
<%= link_to "Forgot Password", forgot_password_path, {:class=>"pull-left"}%><br/>
<%= submit_tag "Login", {:class => "btn btn-info btn-large pull-right"} %>
<span class="checkbox-wrapper">
<%= check_box_tag :remember_me, 1, params[:remember_me], {:id => "form-terms", :class => "checkbox", :type => "checkbox"} %>
<label class="checkbox-label" for="form-terms"></label> <span class="label-text">Remember</span>
</span>
<%= link_to "Forgot Password", forgot_password_path, {:class=>"pull-left"}%><br/>
<%= submit_tag "Login", {:class => "btn btn-info btn-large pull-right"} %>
<span class="checkbox-wrapper">
<%= check_box_tag :remember_me, 1, params[:remember_me], {:id => "form-terms", :class => "checkbox", :type => "checkbox"} %>
<label class="checkbox-label" for="form-terms"></label> <span class="label-text">Remember</span>
</span>
<div class="clearfix"></div>
<% end %>
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/access_control/access_control_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="access_control"]').addClass('active');
$('li[id="access_control"]').addClass('active');
};
$(document).ready(makeActive);
+18 -18
View File
@@ -1,26 +1,26 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/user_pass_enum")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/user_pass_enum")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/password_complexity")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/broken_auth_sess/insecure_compare")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="broken_auth"]').addClass('active');
$('li[id="broken_auth"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/csrf/csrf_first"%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/csrf/csrf_first"%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="csrf"]').addClass('active');
$('li[id="csrf"]').addClass('active');
};
$(document).ready(makeActive);
+5 -5
View File
@@ -5,19 +5,19 @@
<%= render :partial => "layouts/tutorial/exposure/password_hashing" %>
</div> <!-- End of span-->
</div>
<div class="row-fluid">
<div class="span12">
<div class="span12">
<%= render :partial => "layouts/tutorial/exposure/ssn" %>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div class="span12">
<%= render :partial => "layouts/tutorial/exposure/model_attributes_exposure" %>
</div>
</div>
</div>
</div>
+25 -27
View File
@@ -1,36 +1,34 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Gauntlet
</div>
</div>
<div class="widget-body">
<p class="desc">
Gauntlt is a tool used for unit testing leveraging third-party tools. We've baked this into Railsgoat so that you can play with it. <br/><br/> To learn more about this tool, please visit their site at: <%= link_to "Gauntlet Github Repository", "https://github.com/gauntlt/gauntlt", {:style =>"color: rgb(181, 121, 158);"} %></p>
<p class="desc">
All *.attack files are contained under the gauntlt_scripts directory. We have provided a simple.attack file that demonstrates the tool works. If errors occur, please submit a bug through our github powered issue tracking system.
</p>
<p class="desc">
To run this tool type this via the command line: <br/><br/>$ gauntlt
</p>
</div>
</div>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe092;"></span> Gauntlet
</div>
</div>
<div class="widget-body">
<p class="desc">
Gauntlt is a tool used for unit testing leveraging third-party tools. We've baked this into Railsgoat so that you can play with it. <br/><br/> To learn more about this tool, please visit their site at: <%= link_to "Gauntlet Github Repository", "https://github.com/gauntlt/gauntlt", {:style =>"color: rgb(181, 121, 158);"} %></p>
<p class="desc">
All *.attack files are contained under the gauntlt_scripts directory. We have provided a simple.attack file that demonstrates the tool works. If errors occur, please submit a bug through our github powered issue tracking system.
</p>
<p class="desc">
To run this tool type this via the command line: <br/><br/>$ gauntlt
</p>
</div>
</div>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function openSub(){
$('li[id="gauntlt"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="gauntlt"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(openSub);
+17 -19
View File
@@ -1,28 +1,26 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe012;"></span> Using Guard with Brakeman and Bundle-Audit
</div>
</div>
<div class="widget-body">
<iframe src="http://player.vimeo.com/video/63901340" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</div>
</div>
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe012;"></span> Using Guard with Brakeman and Bundle-Audit
</div>
</div>
<div class="widget-body">
<iframe src="http://player.vimeo.com/video/63901340" width="500" height="281" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function openSub(){
$('li[id="guard"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="guard"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(openSub);
+79 -80
View File
@@ -1,88 +1,87 @@
<div class="dashboard-wrapper">
<div class="main-container">
<h1> Welcome to RailsGoat </h1>
<h3> Tutorial Guide </h3>
<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="&#xe092;"></span> Step 1 - Choose your path
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Get Started
</h3>
<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.
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe091;"></span> Step 2 - Using the application
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Using the application
</h3>
<p class="desc">
Each tutorial offers 4 parts:
<li style="margin-left: 20px">Description</li>
<li style="margin-left: 20px">Bug</li>
<li style="margin-left: 20px">Solution</li>
<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!
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0e1;"></span> Step 3 - Attack
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h1 align="center">
HACK!
</h1>
<p class="desc">
</p>
</div>
<hr/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="main-container">
<h1> Welcome to RailsGoat </h1>
<h3> Tutorial Guide </h3>
<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="&#xe092;"></span> Step 1 - Choose your path
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Get Started
</h3>
<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.
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe091;"></span> Step 2 - Using the application
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h3>
Using the application
</h3>
<p class="desc">
Each tutorial offers 4 parts:
<li style="margin-left: 20px">Description</li>
<li style="margin-left: 20px">Bug</li>
<li style="margin-left: 20px">Solution</li>
<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!
</p>
</div>
<hr/>
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
</div>
</div>
</div>
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe0e1;"></span> Step 3 - Attack
</div>
</div>
<div class="widget-body">
<div class="thumbnail">
<div class="caption">
<h1 align="center">
HACK!
</h1>
<p class="desc">
</p>
</div>
<hr/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="tutorials_home"]').addClass('active');
$('li[id="tutorials_home"]').addClass('active');
};
$(document).ready(makeActive);
+18 -18
View File
@@ -1,26 +1,26 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/sqli_scope"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_command"%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/sqli_scope"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/injection/injection_command"%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="injection"]').addClass('active');
$('li[id="injection"]').addClass('active');
};
$(document).ready(makeActive);
@@ -1,21 +1,21 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_second" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_first" %>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_components/insecure_components_second" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="insecure_components"]').addClass('active');
$('li[id="insecure_components"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_dor/insecure_dor_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/insecure_dor/insecure_dor_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="insecure_dor"]').addClass('active');
$('li[id="insecure_dor"]').addClass('active');
};
$(document).ready(makeActive);
+14 -14
View File
@@ -1,23 +1,23 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/broken_regexp")%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/logic_flaws/insecure_crypto_reuse")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="logic_flaws"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="logic_flaws"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(makeActive);
+9 -9
View File
@@ -1,17 +1,17 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/mass_assignment/admin_mass_assign")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/mass_assignment/admin_mass_assign")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="mass_assignment"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="mass_assignment"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(makeActive);
+9 -9
View File
@@ -1,17 +1,17 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/metaprogramming/benefit_forms_constantize")%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<%= render :partial => ("layouts/tutorial/metaprogramming/benefit_forms_constantize")%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="metaprogramming"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
$('li[id="metaprogramming"]').addClass('active');
$('li[id="submenu"]').addClass('active open');
};
$(document).ready(makeActive);
+13 -13
View File
@@ -1,21 +1,21 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
</div> <!-- End Span12-->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_first"%>
</div> <!-- End Span12-->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12-->
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
</div> <!-- End Span12-->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="misconfig"]').addClass('active');
$('li[id="misconfig"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/redirects/redirects_first"%>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/redirects/redirects_first"%>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="redirects"]').addClass('active');
$('li[id="redirects"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/ssl_tls/ssl_tls_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/ssl_tls/ssl_tls_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="ssl_tls"]').addClass('active');
$('li[id="ssl_tls"]').addClass('active');
};
$(document).ready(makeActive);
+8 -8
View File
@@ -1,16 +1,16 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/url_access/url_access_first" %>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/url_access/url_access_first" %>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="url_access"]').addClass('active');
$('li[id="url_access"]').addClass('active');
};
$(document).ready(makeActive);
+13 -13
View File
@@ -1,21 +1,21 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/xss_first"%>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/dom_xss"%>
</div> <!-- End Span12 -->
</div>
</div>
<div class="main-container">
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/xss_first"%>
</div> <!-- End Span12 -->
</div>
<div class="row-fluid">
<div class="span12"> <!-- Begin Span12 -->
<%= render :partial => "layouts/tutorial/xss/dom_xss"%>
</div> <!-- End Span12 -->
</div>
</div>
</div>
<script type="text/javascript">
function makeActive(){
$('li[id="xss"]').addClass('active');
$('li[id="xss"]').addClass('active');
};
$(document).ready(makeActive);
+84 -88
View File
@@ -1,20 +1,20 @@
<div class="dashboard-wrapper">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<div class="main-container">
<div class="row-fluid">
<div class="span12">
<div id="success" style="display: none;" class="alert alert-block alert-success fade in">
<h4 class="alert-heading">
Success!
</h4>
<p>
Information successfully updated.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
<h4 class="alert-heading">
Error!
</h4>
@@ -22,86 +22,82 @@
Failed to update.
</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe090;"></span> Profile Settings
<span class="mini-title">
Edit your account details
</span>
</div>
</div>
<div class="widget-body">
<%= form_for @user, :html => {:id => "account_edit"} do |f|%>
<%= f.hidden_field :user_id%>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div>
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="clearfix">
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span6">
<div class="widget">
<div class="widget-header">
<div class="title">
<span class="fs1" aria-hidden="true" data-icon="&#xe090;"></span> Profile Settings
<span class="mini-title">
Edit your account details
</span>
</div>
</div>
<div class="widget-body">
<%= form_for @user, :html => {:id => "account_edit"} do |f|%>
<%= f.hidden_field :user_id%>
<div class="control-group">
<%= f.label :email, nil, {:class => "control-label"}%>
<%= f.text_field :email, {:class => "span12"}%>
</div>
<div class="control-group">
<%= f.label :first_name, nil, {:class => "control-label"}%>
<%= f.text_field :first_name, {:class => "span12"} %>
</div>
<div>
<%= f.label :last_name, nil, {:class => "control-label"}%>
<%= f.text_field :last_name, {:class => "span12"} %>
</div>
<div class="control-group">
<%= f.label :password, nil, {:class => "control-label"}%>
<%= f.password_field :password, {:class => "span12", :placeholder => "Enter Password"}%>
</div>
<div class="control-group">
<%= f.label :password_confirmation, nil, {:class => "control-label"}%>
<%= f.password_field :password_confirmation, {:class => "span12", :placeholder => "Enter Password"} %>
</div>
<div class="form-actions no-margin">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info pull-right"} %>
</div>
<div class="clearfix">
</div>
<% end %>
</div>
</div>
</div>
</div>
<%= javascript_include_tag ('validation.js')%>
<script type="text/javascript">
$("#submit_button").click(function(event) {
var valuesToSubmit = $("#account_edit").serialize();
var valuesToSubmit = $("#account_edit").serialize();
event.preventDefault();
$.ajax({
url: <%= "/users/#{current_user.user_id}.json".inspect.html_safe %>,
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
data: valuesToSubmit,
type: "POST",
success: function(response) {
if (response.msg == "failure") {
$('#failure').show(500).delay(1500).fadeOut();
} else {
$('#success').show(500).delay(1500).fadeOut();
}
},
error: function(event) {
$('#failure').show(500).delay(1500).fadeOut();
}
});
});
</script>
+27 -29
View File
@@ -1,42 +1,40 @@
<div class="row-fluid">
<div class="span12">
<div class="row-fluid">
<div class="span4 offset4">
<div class="signup">
<%= form_for @user, :html => {:id => "account_edit", :class=> "signup-wrapper"} do |f| %>
<div class="header">
<h2>Sign Up</h2>
<p>Fill out the form below to login</p>
</div>
<div class="content">
<%= 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 :last_name, {:class => "input input-block-level", :placeholder => "Last Name"} %>
<div class="control-group">
<%= f.password_field :password, {:class => "input input-block-level", :placeholder => "Password"}%>
</div>
<div class="control-group">
<%= f.password_field :password_confirmation, {:class => "input input-block-level", :placeholder => "Confirm Password"}%>
</div>
</div>
<div class="actions">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info btn-large pull-right"} %>
</div>
<div class="clearfix"></div>
<% end %>
<%= form_for @user, :html => {:id => "account_edit", :class=> "signup-wrapper"} do |f| %>
<div class="header">
<h2>Sign Up</h2>
<p>Fill out the form below to login</p>
</div>
<div class="content">
<%= 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 :last_name, {:class => "input input-block-level", :placeholder => "Last Name"} %>
<div class="control-group">
<%= f.password_field :password, {:class => "input input-block-level", :placeholder => "Password"}%>
</div>
<div class="control-group">
<%= f.password_field :password_confirmation, {:class => "input input-block-level", :placeholder => "Confirm Password"}%>
</div>
</div>
<div class="actions">
<%= f.submit "Submit", {:id => 'submit_button', :class => "btn btn-info btn-large pull-right"} %>
</div>
<div class="clearfix"></div>
<% end %>
</div>
</div>
</div>
</div>
</div>

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