Convert file indentation to spaces

This commit is contained in:
James Espinosa
2014-07-05 20:17:27 -05:00
parent 68e6a01743
commit 7e4fad462b
88 changed files with 2915 additions and 2999 deletions
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseModelOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
</p>
<p class="desc">
The application's API returns a model object (user or users). Using respond_with, the API returns the full model object. It is simple but exposes information such as the user's password and other user attributes that you may wish to keep invisible.
</p>
</div>
</div>
</div>
@@ -32,51 +32,51 @@
</div>
<div class="accordion-body collapse" id="collapseModelTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
Within app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
<p class="desc">
Within app/controllers/api/v1/users_controller.rb:
</p>
<pre class="ruby">
def index
# We removed the .as_json code from the model, just seemed like extra work.
# dunno, maybe useful at a later time?
#respond_with @user.admin ? User.all.as_json : @user.as_json
respond_with @user.admin ? User.all : @user
end
respond_with @user.admin ? User.all : @user
end
def show
respond_with @user.as_json
end
</pre>
<p class="desc">
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
</p>
<pre class="ruby">
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
</pre>
<p class="desc">
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
X-Runtime: 0.051734
Connection: close
def show
respond_with @user.as_json
end
</pre>
<p class="desc">
The <i>as_json</i> method referenced in the comments section of the index action exists within the user model in order to override and safely protect our model from only rendering certain attributes. It is unused (commented out), app/models/user.rb:
</p>
<pre class="ruby">
# Instead of the entire user object being returned, we can use this to filter.
def as_json
super(only: [:user_id, :email, :first_name, :last_name])
end
</pre>
<p class="desc">
When utilizing the method that most tutorials describe or advocate when rendering model objects via JSON in an API (unsafe), the response looks like this:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "6b4caf343a20865de174b2b530b945dd"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c3b0a57861087c0b827aab231747ef0c
X-Runtime: 0.051734
Connection: close
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
Note that all attributes associated with this user are returned via the API.
</p>
{"admin":false,"created_at":"2014-01-23T16:17:10Z","email":
"jack@metacorp.com","first_name":"Jack","id":2,"last_name":"Mannino","password":
"b46dd2888a0904972649cc880a93f4dd","updated_at":"2014-01-23T16:17:10Z","user_id":2}
</pre>
<p class="desc">
Note that all attributes associated with this user are returned via the API.
</p>
</div>
</div>
</div>
@@ -91,41 +91,41 @@
<div class="accordion-body collapse" id="collapseModelThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Model Attributes Exposure - ATTACK</b></p>
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
<p><b>Model Attributes Exposure - SOLUTION</b></p>
<p class="desc">
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
</p>
<pre class="ruby">
respond_with @user.admin ? User.all.as_json : @user.as_json
</pre>
<p class="desc">
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
</p>
<pre class="ruby">
def as_json
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
end
</pre>
<p class="desc">
The response from the API should look like:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "2333488e856669ac637e37cb4cf09cb6"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: baa6a1c90004838793614e4c61633767
X-Runtime: 0.092768
Connection: close
<p class="desc"> Use the API and review the data returned. Additional information on exploiting the API available under the <i>Extras > Logic Flaws</i> Section.</p>
<p><b>Model Attributes Exposure - SOLUTION</b></p>
<p class="desc">
Uncomment the <i>as_json</i> method within the user model. Additionally, call <i>.as_json</i> on any User model object you would like to return via the API or other means. Example:
</p>
<pre class="ruby">
respond_with @user.admin ? User.all.as_json : @user.as_json
</pre>
<p class="desc">
Upon uncommenting the <i>as_json</i> method within the User model, the <i>as_json</i> method will ensure the API output only returns those attributes you have allowed in the following code:
</p>
<pre class="ruby">
def as_json
super(<span style="background-color:yellow">only: [:user_id, :email, :first_name, :last_name]</span>)
end
</pre>
<p class="desc">
The response from the API should look like:
</p>
<pre>
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-UA-Compatible: IE=Edge
ETag: "2333488e856669ac637e37cb4cf09cb6"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: baa6a1c90004838793614e4c61633767
X-Runtime: 0.092768
Connection: close
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
</pre>
{"email":"jack@metacorp.com","first_name":"Jack","last_name":"Mannino","user_id":2}
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseModelFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
@@ -16,15 +16,15 @@
</div>
<div class="accordion-body in collapse" id="collapseOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
<p class="desc">
The OWASP description - Many web applications do not properly protect sensitive data, such as credit cards, SSNs, and authentication credentials, with appropriate encryption or hashing. Attackers may steal or modify such weakly protected data to conduct identity theft, credit card fraud, or other crimes.
</p>
<p class="desc">
Railsgoat does hash user passwords. Unfortunately, it does so using an extremely weak algorithm (MD5). Generally speaking, a strong algorithm and per-user salt can greatly improve the security of a hashed value. Also important to note, hashing and encryption are not the same. Encryption is meant to be reversible using some secret information, hashing is not, hashing is a one-way function not meant to be reversible.
</p>
<p class="desc">
All that being said, there are groups within security organizations that devote themselves to threat models built around this topic so clearly, this description does not encompass all scenarios. However, our recommendation is better than hashing using MD5 <i class="icon-wink-2"></i>.
</p>
</p>
<p class="desc">
Railsgoat does hash user passwords. Unfortunately, it does so using an extremely weak algorithm (MD5). Generally speaking, a strong algorithm and per-user salt can greatly improve the security of a hashed value. Also important to note, hashing and encryption are not the same. Encryption is meant to be reversible using some secret information, hashing is not, hashing is a one-way function not meant to be reversible.
</p>
<p class="desc">
All that being said, there are groups within security organizations that devote themselves to threat models built around this topic so clearly, this description does not encompass all scenarios. However, our recommendation is better than hashing using MD5 <i class="icon-wink-2"></i>.
</p>
</div>
</div>
</div>
@@ -38,34 +38,34 @@
</div>
<div class="accordion-body collapse" id="collapseTwo" style="height: 0px;">
<div class="accordion-inner">
<p>
<p>
Within app/models/user.rb:
</p>
<pre class="ruby">
before_save <span style="background-color:yellow">:hash_password</span>
</p>
<pre class="ruby">
before_save <span style="background-color:yellow">:hash_password</span>
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
if user
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
def self.authenticate(email, password)
auth = nil
user = find_by_email(email)
if user
if user.password == <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
auth = user
else
raise "Incorrect Password!"
end
else
raise "#{email} doesn't exist!"
end
return auth
end
def hash_password
if self.password.present?
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
end
end
def hash_password
if self.password.present?
self.password = <span style="background-color:yellow">Digest::MD5.hexdigest(password)</span>
end
end
</pre>
</pre>
</div>
</div>
@@ -81,34 +81,34 @@
<div class="accordion-body collapse" id="collapseThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>Password Storage - ATTACK</b></p>
<p class="desc">
Using the passwords stored within db/seeds.rb file, create a wordlist and leverage a password cracking tool such as John The Ripper to crack those passwords.
</p>
<p><b>Password Storage - SOLUTION</b></p>
<p class="desc">
A simple solution here would be to enforce a per-user salt in creating a BCrypt hash. You would need to alter the db schema to add a password_salt and password_hash columns to the table.
</p>
<pre class="ruby">
def self.authenticate(email, password)
user = find_by_email(email)
if user and user.password_hash == <span style="background-color:yellow">BCrypt::Engine.hash_secret(password, user.password_salt)</span>
user
else
"Invalid Credentials Supplied"
end
end
<p class="desc">
Using the passwords stored within db/seeds.rb file, create a wordlist and leverage a password cracking tool such as John The Ripper to crack those passwords.
</p>
<p><b>Password Storage - SOLUTION</b></p>
<p class="desc">
A simple solution here would be to enforce a per-user salt in creating a BCrypt hash. You would need to alter the db schema to add a password_salt and password_hash columns to the table.
</p>
<pre class="ruby">
def self.authenticate(email, password)
user = find_by_email(email)
if user and user.password_hash == <span style="background-color:yellow">BCrypt::Engine.hash_secret(password, user.password_salt)</span>
user
else
"Invalid Credentials Supplied"
end
end
def hash_password
if self.password.present?
<span style="background-color:yellow">self.password_salt = BCrypt::Engine.generate_salt</span>
<span style="background-color:yellow">self.password_hash = BCrypt::Engine.hash_secret(self.password, self.password_salt)</span>
end
end
</pre>
def hash_password
if self.password.present?
<span style="background-color:yellow">self.password_salt = BCrypt::Engine.generate_salt</span>
<span style="background-color:yellow">self.password_hash = BCrypt::Engine.hash_secret(self.password, self.password_salt)</span>
end
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">
+100 -100
View File
@@ -16,9 +16,9 @@
</div>
<div class="accordion-body in collapse" id="collapseSSNOne" style="height: auto;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores and transmits Social Security Numbers insecurely.
</p>
<p class="desc">
The Railsgoat application stores and transmits Social Security Numbers insecurely.
</p>
</div>
</div>
</div>
@@ -32,18 +32,18 @@
</div>
<div class="accordion-body collapse" id="collapseSSNTwo" style="height: 0px;">
<div class="accordion-inner">
<p class="desc">
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
</p>
<p class="desc">
<p class="desc">
The Railsgoat application stores user's Social Security Numbers in plain-text within the database and because of this, it fails to adequately protect these numbers from theft. Additionally, the user's full SSN is sent back to the user within an HTTP response from the application.
</p>
<p class="desc">
The WorkInfo model (app/models/work_info.rb) is missing code to encrypt this data prior to storage. Additionally, while code exists to render only the last 4 numbers of an SSN (shown below), at no time is it used.
</p>
<pre class="ruby">
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
end
</pre>
</p>
<pre class="ruby">
# We should probably use this
def last_four
"***-**-" << self.decrypt_ssn[-4,4]
end
</pre>
</div>
</div>
@@ -58,100 +58,100 @@
</div>
<div class="accordion-body collapse" id="collapseSSNThree" style="height: 0px;">
<div class="accordion-inner">
<p><b>SSN Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
</p>
<p><b>SSN Storage - SOLUTION</b></p>
<p class="desc">
There is a lot of guidance on adequately protecting sensitive data at rest and using a layered defensive approach. Make no mistake, this should not be your sole means of securing sensitive data. That being said, there are at least four precautions that should be taken.
<li>The sensitive data is encrypted everywhere, including backups</li>
<li>Only authorized users can access decrypted copies of the data </li>
<li>Use a strong algorithm</li>
<li>Strong key is generated, protected from unauthorized access, and key change is planned for.</li><br/>
</p>
<p class="desc">
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
</p>
<pre class="ruby">
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
<p class="desc">
In the following code, we demonstrate switching from the storage of full SSN(s) in clear-text to storing them in the AES-256 encrypted format. The first thing to do is build the encrypt and decrypt functions. These can be found within app/models/work_info.rb.
</p>
<pre class="ruby">
def encrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.encrypt
aes.key = key
aes.iv = iv if iv != nil
self.encrypted_ssn = aes.update(self.SSN) + aes.final
self.SSN = nil
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def decrypt_ssn
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
aes.decrypt
aes.key = key
aes.iv = iv if iv != nil
aes.update(self.encrypted_ssn) + aes.final
end
def key
raise "Key Missing" if !(KEY)
KEY
end
def 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 iv
raise "No IV for this User" if !(self.key_management.iv)
self.key_management.iv
end
def cipher_type
'aes-256-cbc'
end
</pre>
<p class="desc">
Also within the WorkInfo model, we add the following line of code...
</p>
<pre class="ruby">
before_save :encrypt_ssn
</pre>
<p class="desc">
The remaining pieces are:
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
<li> For new user's who are registering, we create an initialization specific to their account</li>
</p>
<pre class="ruby">
# SEED DATA
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
</pre>
<pre class="ruby">
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
elsif Rails.env.development?
KEY = "123456789101112123456789101112123456789101112"
end
</pre>
<pre class="ruby">
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
<%= CGI.unescapeHTML("&lt;td class=&quot;ssn&quot;&gt;&lt;%= @user.work_info.last_four %&gt;&lt;/td&gt;") %>
</pre>
<pre class="ruby">
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
</pre>
def cipher_type
'aes-256-cbc'
end
</pre>
<p class="desc">
Also within the WorkInfo model, we add the following line of code...
</p>
<pre class="ruby">
before_save :encrypt_ssn
</pre>
<p class="desc">
The remaining pieces are:
<li> We "seed" the database with per-user initialization vectors (IV) and store them within the key_management table</li>
<li> Separate production and development encryption keys. Production keys should be stored in an HSM, environment variable, etc. but never within the source code. Development keys are irrelevant if not being used for real data</li>
<li> Change the view where SSNs are called and rendered to the user so that the "last_four" method is called instead</li>
<li> For new user's who are registering, we create an initialization specific to their account</li>
</p>
<pre class="ruby">
# SEED DATA
work_info.each do |wi|
list = [:user_id, :SSN]
info = WorkInfo.new(wi.reject {|k| list.include?(k)})
info.user_id = wi[:user_id]
info.build_key_management({:user_id => wi[:user_id], :iv => SecureRandom.hex(32) })
info.SSN = wi[:SSN]
info.save
end
</pre>
<pre class="ruby">
# SEPARATE PROD AND DEV KEYS (config/initializers/key.rb)
if Rails.env.production?
# Specify env variable/location/etc. to retrieve key from
elsif Rails.env.development?
KEY = "123456789101112123456789101112123456789101112"
end
</pre>
<pre class="ruby">
# CHANGE VIEW TO CALL LAST FOUR METHOD (app/views/work_info/index.html.erb)
<%= CGI.unescapeHTML("&lt;td class=&quot;ssn&quot;&gt;&lt;%= @user.work_info.last_four %&gt;&lt;/td&gt;") %>
</pre>
<pre class="ruby">
def build_benefits_data
build_retirement(POPULATE_RETIREMENTS.shuffle.first)
build_paid_time_off(POPULATE_PAID_TIME_OFF.shuffle.first).schedule.build(POPULATE_SCHEDULE.shuffle.first)
build_work_info(POPULATE_WORK_INFO.shuffle.first)
# Uncomment below line to use encrypted SSN(s)
work_info.build_key_management(:iv => SecureRandom.hex(32))
performance.build(POPULATE_PERFORMANCE.shuffle.first)
end
</pre>
</div>
</div>
</div>
<div class="accordion-group">
<div class="accordion-group">
<div class="accordion-heading">
<a style="background-color: rgb(181, 121, 158)" href="#collapseSSNFour" data-parent="#accordion1" data-toggle="collapse" class="accordion-toggle">
<i class="icon-aid icon-white">