Clean up trailing and leading whitespace
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
class AdminController < ApplicationController
|
||||
|
||||
|
||||
before_filter :administrative, :if => :admin_param
|
||||
skip_before_filter :has_info
|
||||
|
||||
|
||||
def dashboard
|
||||
end
|
||||
|
||||
@@ -27,14 +27,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 +48,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 +67,5 @@ class AdminController < ApplicationController
|
||||
def admin_param
|
||||
params[:admin_id] != '1'
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
class Api::V1::UsersController < ApplicationController
|
||||
|
||||
|
||||
skip_before_filter :authenticated
|
||||
before_filter :valid_api_token
|
||||
before_filter :extrapolate_user
|
||||
|
||||
|
||||
respond_to :json
|
||||
|
||||
|
||||
def index
|
||||
# We removed the .as_json code from the model, just seemed like extra work.
|
||||
# dunno, maybe useful at a later time?
|
||||
#respond_with @user.admin ? User.all.as_json : @user.as_json
|
||||
|
||||
|
||||
respond_with @user.admin ? User.all : @user
|
||||
end
|
||||
|
||||
|
||||
def show
|
||||
respond_with @user.as_json
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def valid_api_token
|
||||
@@ -26,7 +26,7 @@ private
|
||||
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
|
||||
@@ -37,21 +37,21 @@ private
|
||||
(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
|
||||
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
|
||||
|
||||
@@ -10,7 +10,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
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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 +14,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 +22,23 @@ 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 +46,7 @@ class BenefitFormsController < ApplicationController
|
||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||
end
|
||||
end
|
||||
=end
|
||||
=end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class DashboardController < ApplicationController
|
||||
|
||||
|
||||
skip_before_filter :has_info
|
||||
|
||||
|
||||
def home
|
||||
@user = current_user
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class PaidTimeOffController < ApplicationController
|
||||
|
||||
|
||||
def index
|
||||
@pto = current_user.paid_time_off
|
||||
@schedule = Schedule.new
|
||||
|
||||
@@ -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,12 @@ 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,7 +1,7 @@
|
||||
class PerformanceController < ApplicationController
|
||||
|
||||
|
||||
def index
|
||||
@perf = current_user.performance
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class RetirementController < ApplicationController
|
||||
|
||||
|
||||
def index
|
||||
@info = current_user.retirement
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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 +11,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 +29,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 +50,10 @@ class ScheduleController < ApplicationController
|
||||
date = Date.strptime(s.strip, '%m/%d/%Y')
|
||||
vals <<(date)
|
||||
end
|
||||
rescue ArgumentError
|
||||
rescue ArgumentError
|
||||
return []
|
||||
end
|
||||
return vals
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
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
|
||||
end
|
||||
|
||||
|
||||
def create
|
||||
path = params[:url].present? ? params[:url] : home_dashboard_index_path
|
||||
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?
|
||||
@@ -26,12 +26,12 @@ class SessionsController < ApplicationController
|
||||
redirect_to path
|
||||
else
|
||||
# Removed this code, just doesn't seem specific enough!
|
||||
# flash[:error] = "Either your username and password is incorrect"
|
||||
# flash[:error] = "Either your username and password is incorrect"
|
||||
flash[:error] = e.message
|
||||
render "new"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def destroy
|
||||
cookies.delete(:auth_token)
|
||||
reset_session
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
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
|
||||
@@ -29,13 +29,13 @@ class TutorialsController < ApplicationController
|
||||
</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 %> }
|
||||
@@ -55,10 +55,10 @@ class TutorialsController < ApplicationController
|
||||
\}
|
||||
\});
|
||||
\});
|
||||
|
||||
|
||||
\} }
|
||||
end
|
||||
|
||||
|
||||
def misconfig
|
||||
end
|
||||
|
||||
@@ -67,33 +67,33 @@ 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
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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 +12,11 @@ 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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}"
|
||||
@@ -9,18 +9,18 @@ class Benefits < ActiveRecord::Base
|
||||
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)
|
||||
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
|
||||
end
|
||||
|
||||
=begin
|
||||
=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 }
|
||||
@@ -34,5 +34,5 @@ class Benefits < ActiveRecord::Base
|
||||
stream.reopen(on_hold[i])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -2,5 +2,5 @@ class KeyManagement < ActiveRecord::Base
|
||||
attr_accessible :iv, :user_id
|
||||
belongs_to :work_info
|
||||
belongs_to :user
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -6,11 +6,11 @@ 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
|
||||
|
||||
+8
-8
@@ -1,25 +1,25 @@
|
||||
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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
+10
-10
@@ -13,7 +13,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 +37,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
|
||||
@@ -59,10 +59,10 @@ private
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
end
|
||||
return auth
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
=begin
|
||||
# More secure version, still lacking a decent hashing routine, this is for timing attack prevention
|
||||
def self.authenticate(email, password)
|
||||
@@ -71,9 +71,9 @@ 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?
|
||||
@@ -82,7 +82,7 @@ private
|
||||
self.user_id = uid.to_s if uid
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def hash_password
|
||||
unless @skip_hash_password == true
|
||||
if password.present?
|
||||
@@ -90,7 +90,7 @@ private
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def generate_token(column)
|
||||
begin
|
||||
self[column] = Encryption.encrypt_sensitive_value(self.user_id)
|
||||
|
||||
@@ -3,13 +3,13 @@ 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]
|
||||
end
|
||||
|
||||
|
||||
def encrypt_ssn
|
||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||
aes.encrypt
|
||||
@@ -18,7 +18,7 @@ class WorkInfo < ActiveRecord::Base
|
||||
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
|
||||
@@ -26,19 +26,19 @@ class WorkInfo < ActiveRecord::Base
|
||||
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
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||
@@ -34,15 +34,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="userDataTable" class="widget-body">
|
||||
|
||||
|
||||
</div> <!-- End widget-body-->
|
||||
</div> <!-- End widget header-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<%= javascript_include_tag "jquery.dataTables.js"%>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="main-container">
|
||||
|
||||
<div class="row-fluid">
|
||||
|
||||
|
||||
<div class="span4">
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
@@ -21,14 +21,14 @@
|
||||
PDF
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<!-- End Widget Body -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="span4">
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
@@ -47,11 +47,11 @@
|
||||
PDF
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<!-- End Widget Body -->
|
||||
<!-- End Widget Body -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,27 +108,27 @@
|
||||
</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
|
||||
);
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
@@ -1,19 +1,19 @@
|
||||
<div class="dashboard-wrapper">
|
||||
|
||||
|
||||
<div class="main-container">
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12"> <!--begin span12 -->
|
||||
<% if @user.paid_time_off %>
|
||||
<% if @user.paid_time_off %>
|
||||
<%= render :partial => "layouts/dashboard/dashboard_stats"%>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div> <!-- end span12 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
|
||||
function makeActive(){
|
||||
$('li[id="home"]').addClass('active');
|
||||
|
||||
@@ -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> </td>
|
||||
<% end %>
|
||||
|
||||
@@ -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>
|
||||
@@ -29,7 +29,7 @@
|
||||
<%= u.admin ? %{<span class="fs1" aria-hidden="true" data-icon=""}.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"}%>
|
||||
<%= link_to "Edit", "#", {:onClick => "javascript:openEditModal(#{u.id});", :role => "button", :style => "width:70px", :class => "btn btn-inverse", "data-toggle" => "modal"}%>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -11,46 +11,46 @@
|
||||
</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>
|
||||
|
||||
</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>
|
||||
|
||||
<%= 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 %>
|
||||
@@ -60,10 +60,10 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$('#submit_button').click(function() {
|
||||
$('#submit_button').click(function() {
|
||||
var valuesToSubmit = $("#account_edit").serialize();
|
||||
$("#editAcct").modal('hide');
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: "/admin/" + <%= @user.user_id %> + "/update_user.json",
|
||||
data: valuesToSubmit,
|
||||
@@ -76,12 +76,12 @@ $('#submit_button').click(function() {
|
||||
$('#failure').show(500).delay(1500).fadeOut();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
$('#delete_button').click(function() {
|
||||
$('#delete_button').click(function() {
|
||||
$("#editAcct").modal('hide');
|
||||
|
||||
|
||||
$.ajax({
|
||||
url: "/admin/" + <%= params[:admin_id] %> + "/delete_user.json",
|
||||
type: "POST",
|
||||
@@ -93,7 +93,7 @@ $('#delete_button').click(function() {
|
||||
$('#failure').show(500).delay(1500).fadeOut();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
@@ -16,26 +16,26 @@ end
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<% if current_user %>
|
||||
<%= render "layouts/shared/header" %>
|
||||
<% 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>
|
||||
</div>
|
||||
<%= render "layouts/shared/footer" %>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
//Dropdown
|
||||
$('.dropdown-toggle').dropdown();
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<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">
|
||||
@@ -22,7 +22,7 @@
|
||||
</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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<% flash.each do |name, msg| %>
|
||||
<% if name == :error %>
|
||||
<% if name == :error %>
|
||||
<div class="alert alert-error">
|
||||
<a class="close" data-dismiss="alert" href="#">×</a>
|
||||
<%= content_tag :div, msg, :id => "flash_notice" %>
|
||||
|
||||
@@ -94,17 +94,17 @@
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
{
|
||||
mainnav.removeClass('open');
|
||||
ul.slideUp(250);
|
||||
} else
|
||||
} else
|
||||
{
|
||||
mainnav.addClass('open');
|
||||
ul.slideDown(250);
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$('#show_creds_btn').click(function(event) {
|
||||
$('#show_creds_btn').click(function(event) {
|
||||
event.preventDefault();
|
||||
$("#modal_div").load(<%= credentials_tutorials_path.inspect.html_safe %>);
|
||||
$("#modal_div").modal("show");
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
A3 XSS
|
||||
<% end %>
|
||||
</li>
|
||||
|
||||
|
||||
<li id="insecure_dor">
|
||||
<%= link_to insecure_dor_tutorials_path do %>
|
||||
<div class="icon">
|
||||
@@ -108,7 +108,7 @@
|
||||
</li>
|
||||
<li id="logic_flaws">
|
||||
<%= link_to "Logic Flaws", logic_flaws_tutorials_path %>
|
||||
</li>
|
||||
</li>
|
||||
<li id="metaprogramming">
|
||||
<%= link_to "Meta- Programming", metaprogramming_tutorials_path %>
|
||||
</li>
|
||||
@@ -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>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<div class="accordion-inner">
|
||||
<p>
|
||||
Within app/models/user.rb
|
||||
</p>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
auth = nil
|
||||
@@ -44,12 +44,12 @@
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
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).
|
||||
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.
|
||||
@@ -70,7 +70,7 @@
|
||||
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
||||
<p>
|
||||
Within app/models/user.rb:
|
||||
</p>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.authenticate(email, password)
|
||||
<span style="background-color: yellow">user = find_by_email(email) || User.new(:password => "")</span>
|
||||
@@ -78,15 +78,15 @@
|
||||
return user
|
||||
else
|
||||
raise "Incorrect username or password"
|
||||
end
|
||||
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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,7 +102,7 @@
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
Timing is everything. Authenticating is important too.
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -60,7 +60,7 @@
|
||||
<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>
|
||||
<p><b>Lack of Password Complexity - SOLUTION</b></p>
|
||||
<p class="desc">
|
||||
This regular expression validates the password has the following requirements:
|
||||
@@ -68,14 +68,14 @@
|
||||
<li>1 lowercase alphabet</li>
|
||||
<li>1 uppercase alphabet</li>
|
||||
<li>1 special character</li>
|
||||
</p>
|
||||
</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>
|
||||
@@ -91,7 +91,7 @@ validates :password, :presence => true,
|
||||
<div class="accordion-inner">
|
||||
<p class="desc">
|
||||
I wonder how strong the administrator's password is?
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
<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
|
||||
@@ -46,7 +46,7 @@
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
end
|
||||
else
|
||||
raise "#{email} doesn't exist!"
|
||||
end
|
||||
@@ -73,10 +73,10 @@
|
||||
|
||||
end
|
||||
</pre>
|
||||
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
|
||||
<p> On line 5 you see the exception message object "e" is created. On line 11, the message is displayed. </p>
|
||||
<p class="desc">
|
||||
One of these messages indicates the email address (username) doesn't exist on the system. The other indicates that the password is incorrect. Although the application will render both error messages, either one of the error messages would be harmful by itself. This type of information can be used by an attacker to harvest email addresses or usernames. Once that list is gathered, passwords can be guessed for each account. If the username being enumerated is actually an email address, a phishing campaign could ensue with emails made to look like they are originating from the vulnerable site.
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -131,7 +131,7 @@
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
</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">
|
||||
<table class="table table-striped table-hover table-bordered pull-left" id="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
@@ -25,20 +25,20 @@
|
||||
</th>
|
||||
<th>
|
||||
API Key
|
||||
</th>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="word-wrap:break-word;">
|
||||
admin@metacorp.com
|
||||
admin@metacorp.com
|
||||
</td>
|
||||
<td>
|
||||
admin1234
|
||||
admin1234
|
||||
</td>
|
||||
<td>
|
||||
1-01de24d75cffaa66db205278d1cf900bf087a737
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="word-wrap:break-word;">
|
||||
@@ -82,15 +82,15 @@
|
||||
</td>
|
||||
<td>
|
||||
5-4af604a848ca212cfa3935352aabe9522cf89fdc
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="row-fluid">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
@@ -100,14 +100,14 @@
|
||||
<button id="understood" class="btn btn-primary" aria-hidden="true">
|
||||
I understand
|
||||
</button>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<!-- End Modal -->
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$('#understood').click(function() {
|
||||
$('#understood').click(function() {
|
||||
$("#creds_hidden").show();
|
||||
});
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
# Our security guy keep talking about sea-surfing, cool story bro.
|
||||
# protect_from_forgery
|
||||
</pre>
|
||||
|
||||
|
||||
</p>
|
||||
<p> application.html.erb </p>
|
||||
<p>
|
||||
@@ -64,7 +64,7 @@
|
||||
</p>
|
||||
<p>
|
||||
<pre class="ruby">
|
||||
<%=
|
||||
<%=
|
||||
%{
|
||||
<html>
|
||||
<body>
|
||||
@@ -77,17 +77,17 @@
|
||||
</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...
|
||||
<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>
|
||||
@@ -97,8 +97,8 @@
|
||||
</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>
|
||||
</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>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,7 +51,7 @@
|
||||
<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">
|
||||
<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])
|
||||
@@ -69,14 +69,14 @@
|
||||
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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -121,7 +121,7 @@
|
||||
Connection: close
|
||||
|
||||
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</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)
|
||||
@@ -52,21 +52,21 @@
|
||||
auth = user
|
||||
else
|
||||
raise "Incorrect Password!"
|
||||
end
|
||||
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>
|
||||
@@ -88,7 +88,7 @@
|
||||
<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">
|
||||
<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>
|
||||
@@ -97,14 +97,14 @@
|
||||
"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>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"***-**-" << self.decrypt_ssn[-4,4]
|
||||
end
|
||||
</pre>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,11 +65,11 @@
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def encrypt_ssn
|
||||
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
|
||||
@@ -87,7 +87,7 @@
|
||||
aes.iv = iv if iv != nil
|
||||
aes.update(self.encrypted_ssn) + aes.final
|
||||
end
|
||||
|
||||
|
||||
def key
|
||||
raise "Key Missing" if !(KEY)
|
||||
KEY
|
||||
@@ -101,7 +101,7 @@
|
||||
def cipher_type
|
||||
'aes-256-cbc'
|
||||
end
|
||||
</pre>
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Also within the WorkInfo model, we add the following line of code...
|
||||
</p>
|
||||
@@ -109,12 +109,12 @@
|
||||
before_save :encrypt_ssn
|
||||
</pre>
|
||||
<p class="desc">
|
||||
The remaining pieces are:
|
||||
The remaining pieces are:
|
||||
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
|
||||
<li> 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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
# SEED DATA
|
||||
work_info.each do |wi|
|
||||
@@ -133,7 +133,7 @@
|
||||
elsif Rails.env.development?
|
||||
KEY = "123456789101112123456789101112123456789101112"
|
||||
end
|
||||
</pre>
|
||||
</pre>
|
||||
<pre class="ruby">
|
||||
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
|
||||
<%= CGI.unescapeHTML("<td class="ssn"><%= @user.work_info.last_four %></td>") %>
|
||||
@@ -147,7 +147,7 @@
|
||||
work_info.build_key_management(:iv => SecureRandom.hex(32))
|
||||
performance.build(POPULATE_PERFORMANCE.shuffle.first)
|
||||
end
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</p>
|
||||
<p>
|
||||
Within app/controllers/benefits_controller.rb:
|
||||
</p>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def upload
|
||||
<span style="background:yellow">file = params[:benefits][:upload]</span>
|
||||
@@ -46,7 +46,7 @@
|
||||
<span style="background:yellow">Benefits.save(file, params[:benefits][:backup])</span>
|
||||
else
|
||||
flash[:error] = "Something went wrong"
|
||||
end
|
||||
end
|
||||
redirect_to user_benefit_forms_path(:user_id => current_user.user_id)
|
||||
end
|
||||
</pre>
|
||||
@@ -71,11 +71,11 @@
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
</pre>
|
||||
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,7 +92,7 @@
|
||||
<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>
|
||||
</p>
|
||||
<pre class='ruby'>
|
||||
POST /upload HTTP/1.1
|
||||
Host: railsgoat.dev
|
||||
@@ -124,16 +124,16 @@
|
||||
</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.
|
||||
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">
|
||||
</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>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -37,7 +37,7 @@
|
||||
</p>
|
||||
<p>
|
||||
Within app/controllers/users_controller.rb
|
||||
</p>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def update
|
||||
message = false
|
||||
@@ -55,7 +55,7 @@
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,7 +92,7 @@
|
||||
</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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
POST /users/5.json HTTP/1.1
|
||||
Host: railsgoat.dev
|
||||
@@ -110,7 +110,7 @@
|
||||
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>
|
||||
</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/>
|
||||
@@ -119,7 +119,7 @@
|
||||
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]
|
||||
@@ -136,7 +136,7 @@
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
user = User.find(:first, :conditions => ["user_id = ?", "#{params[:user][:user_id]}"])
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -153,7 +153,7 @@
|
||||
I wonder who else's account needs updating?
|
||||
</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=""></span> A1 - SQL Injection - ActiveRecord Scope
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> A1 - SQL Injection - ActiveRecord Scope
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget-body">
|
||||
@@ -21,10 +21,10 @@
|
||||
</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>
|
||||
</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>
|
||||
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>
|
||||
@@ -53,7 +53,7 @@
|
||||
</pre>
|
||||
<p class="desc">
|
||||
Additionally, within app/controllers/admin_controller.rb:
|
||||
</p>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def analytics
|
||||
if params[:field].nil?
|
||||
@@ -72,7 +72,7 @@
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -89,7 +89,7 @@
|
||||
<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>
|
||||
</p>
|
||||
<pre>
|
||||
GET /admin/1/analytics?ip=127.0.0.1&field%5B*%20from%20users--%5D= HTTP/1.1
|
||||
Host: railsgoat.dev
|
||||
@@ -100,9 +100,9 @@
|
||||
Cookie:[redacted]
|
||||
Connection: keep-alive
|
||||
</pre>
|
||||
<p class="desc">
|
||||
<p class="desc">
|
||||
Essentially we are changing the intended SQL query from:
|
||||
</p>
|
||||
</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>
|
||||
@@ -111,11 +111,11 @@
|
||||
</p>
|
||||
<pre>
|
||||
SELECT * from users-- FROM "analytics" WHERE "analytics"."ip_address" = '127.0.0.1' ORDER BY id DESC
|
||||
</pre>
|
||||
</pre>
|
||||
<p><b>SQL Injection - SOLUTION</b></p>
|
||||
<p 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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def self.parse_field(field)
|
||||
valid_fields = ["ip_address", "referrer", "user_agent"]
|
||||
@@ -146,7 +146,7 @@
|
||||
render "layouts/admin/_analytics"
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
<p class="desc">
|
||||
Effectively, we've changed any malicious data provided by the user into the number '1' by leveraging the above code.
|
||||
</p>
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -42,7 +42,7 @@
|
||||
} %>
|
||||
</pre>
|
||||
<p class="desc">
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -73,13 +73,13 @@ function snippetPopup(content) \{
|
||||
<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>
|
||||
</p>
|
||||
<pre>
|
||||
<%= "http://localhost:3000/tutorials/injection#</title></head><script>alert(1)</script>" %>
|
||||
</pre>
|
||||
</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>
|
||||
<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:
|
||||
|
||||
@@ -47,12 +47,12 @@
|
||||
end
|
||||
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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,7 +70,7 @@
|
||||
<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>
|
||||
<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).
|
||||
@@ -78,12 +78,12 @@
|
||||
<pre class="ruby">
|
||||
def index
|
||||
<span style="background-color:yellow">@user = current_user</span>
|
||||
if !(@user) || @user.admin
|
||||
if !(@user) || @user.admin
|
||||
<span style="background-color:yellow">flash[:error] = "Apologies, looks like something went wrong"</span>
|
||||
redirect_to home_dashboard_index_path
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def valid_api_token
|
||||
authenticate_or_request_with_http_token do |token, options|
|
||||
@@ -49,7 +49,7 @@
|
||||
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
|
||||
@@ -60,12 +60,12 @@
|
||||
(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
|
||||
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="")
|
||||
@@ -80,22 +80,22 @@
|
||||
def extrapolate_user
|
||||
<span style="background-color:yellow">@user = User.find_by_id(@clean_token.split("-").first)</span>
|
||||
end
|
||||
</pre>
|
||||
</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>
|
||||
</pre>
|
||||
<p class="desc">
|
||||
However, the user actually enters:
|
||||
</p>
|
||||
<pre>
|
||||
Authorization: Token token=2%0a1-01de24d75cffaa66db205278d1cf900bf087a737
|
||||
</pre>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,7 +111,7 @@
|
||||
<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:
|
||||
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
|
||||
@@ -120,7 +120,7 @@
|
||||
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>
|
||||
<span style="background-color:yellow">Authorization: Token token=2-050ddd40584978fe9e82840b8b95abb98e4786dc</span>
|
||||
Content-Length: 4
|
||||
</pre>
|
||||
<p class="desc">
|
||||
@@ -135,11 +135,11 @@
|
||||
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>
|
||||
</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>
|
||||
@@ -150,12 +150,12 @@
|
||||
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
|
||||
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>
|
||||
</p>
|
||||
<pre>
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=utf-8
|
||||
@@ -165,7 +165,7 @@
|
||||
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",
|
||||
@@ -180,7 +180,7 @@
|
||||
"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>
|
||||
</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.
|
||||
@@ -196,7 +196,7 @@
|
||||
(id && hash) ? true : false
|
||||
check_hash(id, hash) ? true : false
|
||||
end
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -54,7 +54,7 @@
|
||||
decoded = Base64.strict_decode64("#{val}")
|
||||
aes.update("#{decoded}") + aes.final
|
||||
end
|
||||
</pre>
|
||||
</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:
|
||||
@@ -62,14 +62,14 @@
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def show
|
||||
respond_to do |format|
|
||||
@@ -79,16 +79,16 @@
|
||||
</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>
|
||||
</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>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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>
|
||||
@@ -45,17 +45,17 @@
|
||||
</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>
|
||||
</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",
|
||||
User.new(:email => "email@email.com",
|
||||
:admin => "true",
|
||||
:password => "h4xx0r",
|
||||
:first_name => "Captain",
|
||||
:last_name => "Crunch"
|
||||
)
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,8 +72,8 @@
|
||||
<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">
|
||||
</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
|
||||
@@ -87,12 +87,12 @@
|
||||
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>
|
||||
</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>
|
||||
</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>
|
||||
@@ -114,18 +114,18 @@
|
||||
</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>
|
||||
<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{
|
||||
<%= %q{
|
||||
class User < ActiveRecord::Base
|
||||
attr_accessible :email, :password, :password_confirmation, :first_name, :last_name
|
||||
} %>
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -144,7 +144,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,7 +37,7 @@
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
def download
|
||||
begin
|
||||
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'
|
||||
@@ -48,7 +48,7 @@
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,10 +65,10 @@
|
||||
<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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
http://railsgoat.dev/download?name=Health_n_Stuff.pdf&type=File
|
||||
</pre>
|
||||
</pre>
|
||||
<p>
|
||||
Change the name parameter to something a little more fun like:
|
||||
</p>
|
||||
@@ -77,10 +77,10 @@
|
||||
</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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
../../../../../../../etc/passwd&type=File
|
||||
</pre>
|
||||
</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:
|
||||
@@ -89,14 +89,14 @@
|
||||
# More secure version
|
||||
def download
|
||||
<span style="background-color:yellow">file_assoc = {"1" => "Health_n_Stuff.pdf", "2" => "Dental_n_Stuff.pdf"}</span>
|
||||
begin
|
||||
begin
|
||||
<span style="background-color:yellow">if file_assoc.has_key?(params[:name].to_s)</span>
|
||||
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)
|
||||
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
|
||||
@@ -107,7 +107,7 @@
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,7 +124,7 @@
|
||||
It can be very helpful for employees to download benefit forms.
|
||||
</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
|
||||
@@ -73,7 +73,7 @@
|
||||
</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>
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</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>
|
||||
|
||||
@@ -39,9 +39,9 @@
|
||||
<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">
|
||||
<pre class="ruby">
|
||||
def create
|
||||
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
|
||||
<span style="background-color:yellow">path = params[:url].present? ? params[:url] : home_dashboard_index_path</span>
|
||||
begin
|
||||
# Normalize the email address, why not
|
||||
user = User.authenticate(params[:email].to_s.downcase, params[:password])
|
||||
@@ -54,13 +54,13 @@
|
||||
redirect_to path
|
||||
else
|
||||
# Removed this code, just doesn't seem specific enough!
|
||||
# flash[:error] = "Either your username and password is incorrect"
|
||||
# flash[:error] = "Either your username and password is incorrect"
|
||||
flash[:error] = e.message
|
||||
render "new"
|
||||
end
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -77,18 +77,18 @@
|
||||
<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>
|
||||
<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
|
||||
path = home_dashboard_index_path
|
||||
begin
|
||||
if params[:url].present?
|
||||
path = URI.parse(params[:url]).path
|
||||
end
|
||||
rescue
|
||||
rescue
|
||||
end
|
||||
</pre>
|
||||
<p class="desc">
|
||||
@@ -112,7 +112,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,10 +56,10 @@
|
||||
</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>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
Railsgoat::Application.config.session_store :cookie_store, key: '_railsgoat_session'<span style="background-color:yellow">, :secure => true
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,11 +36,11 @@
|
||||
<pre class="ruby">
|
||||
<%= %q{
|
||||
class AdminController < ApplicationController
|
||||
|
||||
|
||||
skip_before_filter :has_info
|
||||
} %>
|
||||
</pre>
|
||||
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,34 +57,34 @@
|
||||
<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:
|
||||
</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
|
||||
current_user.admin if current_user
|
||||
end
|
||||
|
||||
|
||||
def administrative
|
||||
if not is_admin?
|
||||
reset_session
|
||||
redirect_to root_url
|
||||
end
|
||||
end
|
||||
</pre>
|
||||
</pre>
|
||||
<p>
|
||||
Then add the following line within app/controllers/admin_controller.rb
|
||||
</p>
|
||||
</p>
|
||||
<pre class="ruby">
|
||||
class AdminController < ApplicationController
|
||||
|
||||
<span style="background-color:yellow">before_filter :administrative</span>
|
||||
skip_before_filter :has_info
|
||||
</pre>
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<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 victim’s 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 victim’s 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>
|
||||
@@ -36,8 +36,8 @@
|
||||
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>");
|
||||
@@ -50,13 +50,13 @@
|
||||
\} catch(err) \{
|
||||
\}
|
||||
//document.write("</select>");
|
||||
</script>
|
||||
}
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -71,14 +71,14 @@
|
||||
<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">
|
||||
<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>
|
||||
</pre>
|
||||
<p><b> Stored Cross-Site Scripting SOLUTION:</b></p>
|
||||
<p>
|
||||
<p>
|
||||
Leverage the Hogan function for escaping (found in the application.js file) to escape user input:
|
||||
</p>
|
||||
<pre class="javascript">
|
||||
@@ -97,10 +97,10 @@
|
||||
\}
|
||||
//document.write("</select>");
|
||||
</script>
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
%>
|
||||
</pre>
|
||||
</pre>
|
||||
</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>
|
||||
<p class="desc">
|
||||
Coincidentally, HTML safe is not safe from HTML Injection or "XSS" attacks. The name is deceiving. Some folks believe the raw() helper to be different than the html_safe String method. raw() is actually a wrapper for html_safe and essentially ensures exceptions are handled when the expected value is nil.
|
||||
Coincidentally, HTML safe is not safe from HTML Injection or "XSS" attacks. The name is deceiving. Some folks believe the raw() helper to be different than the html_safe String method. raw() is actually a wrapper for html_safe and essentially ensures exceptions are handled when the expected value is nil.
|
||||
<pre class="ruby">
|
||||
# Psuedo-code to help conceptualize
|
||||
def raw(dirty_string)
|
||||
dirty_string.to_s.html_safe
|
||||
end
|
||||
</pre>
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,7 +66,7 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
<!--[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>
|
||||
</div>
|
||||
<%= render "layouts/shared/footer" %>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span8">
|
||||
<!-- Begin Message Draft Content-->
|
||||
<!-- Begin Message Draft Content-->
|
||||
<%= form_for @message, :url => user_messages_path, :method => :post, :html => {:id => "send_message"} do |f|%>
|
||||
<%= f.hidden_field :creator_id, :value => current_user.id %>
|
||||
<%= f.hidden_field :read, :value => '0' %>
|
||||
@@ -79,21 +79,21 @@
|
||||
<%= 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 %>
|
||||
<!-- End Message Draft Content-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +101,7 @@
|
||||
</div>
|
||||
<!-- End Row -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -111,7 +111,7 @@ $("#submit_button").click(function(event) {
|
||||
var valuesToSubmit = $("#send_message").serialize();
|
||||
event.preventDefault();
|
||||
$.ajax({
|
||||
url: <%= "/users/#{current_user.user_id}/messages.json".inspect.html_safe %>,
|
||||
url: <%= "/users/#{current_user.user_id}/messages.json".inspect.html_safe %>,
|
||||
data: valuesToSubmit,
|
||||
type: "POST",
|
||||
success: function(response) {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
<script type="text/javascript">
|
||||
function makeActive(){
|
||||
$('li[id="messages"]').addClass('active');
|
||||
};
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||
@@ -78,14 +78,14 @@
|
||||
<% end %>
|
||||
<div class="clearfix">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<!-- End WB-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End DP-->
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
@@ -130,7 +130,7 @@ function makeActive(){
|
||||
|
||||
|
||||
$('#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 %>,
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||
@@ -37,33 +37,33 @@
|
||||
<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"} %>
|
||||
<%= 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>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Row-Fluid for Inputs-->
|
||||
|
||||
|
||||
<!-- ###################-->
|
||||
<!-- Begin Dynamic Table ColSpan Table -->
|
||||
<div class="row-fluid">
|
||||
@@ -79,7 +79,7 @@
|
||||
<!-- 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">
|
||||
<table class="table table-condensed table-striped table-hover table-bordered pull-left" id="data_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:30%">
|
||||
@@ -102,7 +102,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="clearfix">
|
||||
@@ -114,7 +114,7 @@
|
||||
</div
|
||||
<!-- End Dynamic Table ColSpan Table -->
|
||||
<!-- ###################-->
|
||||
|
||||
|
||||
<!-- Begin Row-Fluid for Decryption Input -->
|
||||
<div class="row-fluid">
|
||||
<div class="span9">
|
||||
@@ -128,16 +128,16 @@
|
||||
<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"} %>
|
||||
<%= 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>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -157,7 +157,7 @@
|
||||
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>'
|
||||
'</i></a>'
|
||||
return link
|
||||
};
|
||||
|
||||
@@ -175,7 +175,7 @@ function parseDirectDepostInfo(response){
|
||||
buildDeleteLink(val.id)
|
||||
] );
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -203,11 +203,11 @@ function populateTable() {
|
||||
function createDataTable(){
|
||||
$('#data_table').dataTable({
|
||||
"sPaginationType": "full_numbers"
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
This function doesn't really work right now but is supposed to offer the user a
|
||||
This function doesn't really work right now but is supposed to offer the user a
|
||||
"delete confirmation" message
|
||||
*/
|
||||
$('.delete-row').click(function () {
|
||||
@@ -291,7 +291,7 @@ function makeActive(){
|
||||
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(),
|
||||
|
||||
@@ -66,7 +66,7 @@ function drawChart2() {
|
||||
]);
|
||||
|
||||
var options = {
|
||||
min: 1,
|
||||
min: 1,
|
||||
max: 5,
|
||||
width: 'auto',
|
||||
height: '160',
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<hr/>
|
||||
<p>
|
||||
MetaCorp is dedicated to its employees and this service is just one way of showing it!
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- End of span-->
|
||||
@@ -78,7 +78,7 @@
|
||||
function makeActive(){
|
||||
$('li[id="retirement"]').addClass('active');
|
||||
};
|
||||
|
||||
|
||||
$(document).ready(makeActive)
|
||||
|
||||
</script>
|
||||
@@ -36,16 +36,16 @@
|
||||
</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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<% end %>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@
|
||||
</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>
|
||||
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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</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>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<div class="dashboard-wrapper">
|
||||
<div class="main-container">
|
||||
<div class="main-container">
|
||||
<h1> Welcome to RailsGoat </h1>
|
||||
<h3> Tutorial Guide </h3>
|
||||
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div class="widget">
|
||||
@@ -23,7 +23,7 @@
|
||||
</div>
|
||||
<hr/>
|
||||
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-1.png")%> />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget">
|
||||
@@ -49,7 +49,7 @@
|
||||
</div>
|
||||
<hr/>
|
||||
<img alt="800x400" style="width: 800px; height: 400px;" src=<%= image_path("step-2.png")%> />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="widget">
|
||||
@@ -65,19 +65,19 @@
|
||||
HACK!
|
||||
</h1>
|
||||
<p class="desc">
|
||||
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="span12"> <!-- Begin Span12-->
|
||||
<%= render :partial => "layouts/tutorial/misconfig/misconfig_second"%>
|
||||
</div> <!-- End Span12-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<div id="failure" style="display: none;" class="alert alert-block alert-error fade in">
|
||||
@@ -29,7 +29,7 @@
|
||||
<div class="widget">
|
||||
<div class="widget-header">
|
||||
<div class="title">
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Profile Settings
|
||||
<span class="fs1" aria-hidden="true" data-icon=""></span> Profile Settings
|
||||
<span class="mini-title">
|
||||
Edit your account details
|
||||
</span>
|
||||
@@ -42,42 +42,42 @@
|
||||
<%= 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>
|
||||
|
||||
<%= javascript_include_tag ('validation.js')%>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
$("#submit_button").click(function(event) {
|
||||
@@ -102,6 +102,6 @@ $("#submit_button").click(function(event) {
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<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>
|
||||
@@ -26,7 +26,7 @@
|
||||
<%= 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>
|
||||
@@ -36,7 +36,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
|
||||
<tr>
|
||||
<td><%= "#{@user.first_name} #{@user.last_name}" %></td>
|
||||
<td><%= @user.work_info.income %></td>
|
||||
@@ -33,7 +33,7 @@
|
||||
<!-- End Secure Version -->
|
||||
<td><%= @user.work_info.DoB %></td>
|
||||
</tr>
|
||||
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -41,7 +41,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -49,14 +49,14 @@
|
||||
function maskSSN(){
|
||||
var fullSSN = $("td.ssn").html().replace(/\d{3}.*?\d{2}/, "*****");
|
||||
$("td.ssn").html(fullSSN);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function makeActive(){
|
||||
$('li[id="employee_info"]').addClass('active');
|
||||
};
|
||||
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
maskSSN(),
|
||||
|
||||
Reference in New Issue
Block a user