Check out secure response headers and secure response body for more notes.
Table of Contents
Secure Coding
From the fragments of source code mentioned in the Advisory, I felt that with such coding style there should still be security issues remained in FTA if I kept looking. — Orange Tsai
- Verify for security early.
- Keep a consistent code style.
- Review code frequently. Security is never done.
- Security concerns over development convenience.
- Review dependencies’ security practices.
- Use redundant protection mechanism.
- Forbid everything by default. ie. whitelists, least privilege principle.
- Validate all input.
- Sanitize valid input.
- Interpolate sanitized input.
- Encrypt sensitive data. eg. User identifiable data, business classified data.
- Encode all output according to its type and delivery mechanism.
- Consider possible exceptions, errors, and bugs.
- Ensure exceptions don’t reveal too much as to create security holes.
- Rescue specific exceptions rather than generic ones.
- Don’t rely on implementation secrecy to keep security.
- Test Security.
- Add settings tests to prevent:
- Configuration tampering.
- Unexpected changes to dependency defaults.
- Mimic attacker actions.
- Add settings tests to prevent:
- Keep tools up-to-date for bug and security patches.
- Never commit private credentials to a repo.
- Change private credentials when team members leave.
- Use environments to maintain a single codebase.
Environments
- Never store production data in other environments.
- Use fake data.
- Scrub data from tests reproducing bugs.
- Store production credentials only in env vars.
- Manage environment credentials independently. Avoid grouping by environment.
- Never reuse credentials from any environment.
- Limit production environment access to white-listed IPs/users.
- Check no genuine transactions can be executed in staging/demo environments.
Assuming Bundler for gem management.
Gemfile
- Set runtime dependencies in the main body.
- Group & install other gems by environment.
- Configure the patch-level upgrade for each gem.
# Gemfile
gem "roda", "~> 3.0"
group :development do
gem "minitest", "~> 5.10"
end
Maintenance
Keep a maintenance schedule to:
- Audit the codebase.
- Audit dependencies.
- Update gems.
- update gems individually.
- run tests before and after an update.
- commit each gem update individually.
- Audit Logs.
- review own log files.
- review data captured by error monitoring services.
- purge old logs.
- DB backups.
- Renew private keys before expiration.
Input Handling
Proper input handling prevents code injection. In general:
- Ensure client validations are also done on the server.
- Validate input as soon as it’s received.
- Validate semantics.
- Presence.
- Length.
- Type.
- Format.
- Sanitize according to syntactical validations.
- Enforce structure.
- Whitelist safe elements.
- Encode special elements.
- Never escape special characters. It always depends on the output type.
Regex
When validating format:
- Use
\A
and\z
anchors to help prevent code injection.
greetings = "hello world\n<script>malicious_js();</script>"
/\Ahello world\z/.match? greetings # => false
/^hello world$/.match? greetings # => true
Unicode
Given unicode’s range of characters:
- Normalize input.
- Ensure canonical encoding. ie.
"\u00e9"
and"\u0065\u0301"
should be equivalent. - Handle invalid characters.
- Ensure canonical encoding. ie.
- Whitelist by character category. eg. CJK ideographs, Tibetan, etc.
- Whitelist individual characters.
XSS
Cross-Site Scripting attacks embed and trigger malicious scripts on either server or client. A sound XSS defence covers both input and output.
Check out secure response headers and secure response body for their respective XSS prevention practices.
- Encode all data controlled by end-users.
- Sanitize valid input according to its type. eg. HTML, CSS.
- Whitelist elements.
- Encode valid elements.
- Encode using security-focused gems to avoid pitfalls.
URLs
- Validate URL schemes. eg. HTTPS, FTPS.
- Escape special characters. eg.
URI.escape
. - Whitelist URLs meant to be used as
src
. - Sanitize
params
values. - Test for malicious
params
values. - Interpolate URL fragments rather than passing it in full.
Database
- Avoid saving null.
- Add DB constraints to prevent data corruption.
- Escape special characters before storing input.
- Prevent mass assignment. Whitelist mutable attributes.
SQLi
To prevent SQL injection:
- Interpolate input when building SQL queries.
- Type cast attribute values to match DB data types.
- Test common SQLi attacks to ensure basic coverage.
Test ORM for SQLi to prevent unexpected changes.
Files
- Ensure uploads to the app server are not allowed.
- Use security-focused gems and/or services that:
- Validate files whitelisting:
- Name’s character set.
- Names to prevent overwrites.
- File extensions.
- Validate MIME types.
- Scan files for viruses/malware.
- Upload directly to a storing server.
- Process media files asynchronously.
- Validate files whitelisting:
CMDi
Avoid passing input as system call arguments to prevent command injection.
# Kernel
exec
system
`echo 'backticks'`
%x{}
syscall
command
open
Whenever it can’t be avoided:
- Use security-oriented command wrapping gems.
- Add tests to ensure secure versions of system libraries.
- Keep an
allowed_commands
list.
Pass commands as string fragments rather than a single string. Optionally, pass env vars as a hash.
def run flag:
system { "ENV_VAR" => "value" },
"command", "argument", "--option #{allowed_flags[flag]}"
end
Paths
- Ensure this kind of input can’t be avoided.
- Turn paths into their canonical version to check their absolute path.
- Limit access to paths within the app.
- Whitelist safe paths.
- Interpolate path segments.
In ruby, consider using classes such as Pathname
, or methods such as Dir.chroot
before allowing users to pass paths as arguments.
Ruby dangerous methods
In Ruby, we can help prevent remote code execution when we avoid passing user controlled/generated input to:
Subprocesses
# Kernel
fork
spawn
Dynamic code loading methods
# Kernel
load
autoload
require
require_relative
Metaprogramming methods
# Object
send
__send__
public_send
# BasicObject
instance_eval
instance_exec
# Module
eval
class_eval
class_exec
module_eval
module_exec
alias_method
binding.eval
Furthermore, avoid passing input to any method in Fiddle
module.
Deserialization
Stick to YAML
, and JSON
when serializing data. To avoid arbitrary code execution, make sure to deserialize it using default settings.
require "yaml"
require "json"
YAML.safe_load
JSON.parse
HTTPS
The HTTPS protocol is used to securely exchange data with clients. The server must obtain a valid TLS (aka SSL) certificate to ensure others it can securely exchange data. We can get free certificates from Let’s Encrypt. However, how to setup HTTPS is beyond the scope of this cheat sheet.
Authentication & Authorization
- Enforce authentication using well tested gems.
- Test authentication for every restricted action.
- Grant each role enough privileges to get their job done.
- Test authorization scope. eg. Users can’t change each other’s details.
- Never send passwords through notification channels. eg. email.
Authentication Details
Rely on well designed gems that cover as many of these features as possible:
- Encourage high entropy passwords.
- Don’t limit character set.
- Configurable max length. Opt-in 128 characters length.
- Prevent empty passwords. Opt-in min. 10 characters.
- Prevent password reuse.
- Prevent using common passwords.
- Prevent using pwoned passwords.
- Password hashing using either:
- Argon2i
- Scrypt
- Bcrypt
- Manage session tokens to prevent session fixation.
- Password reset:
- Display generic message eg. we’ve sent more details to the registered email.
- Never lock users out immediately. Send a reset-or-ignore message.
- Send reset password message even when user doesn’t exist but w/link to create account.
- Limit amount of password resets per hr/days.
- Verify password before editing sensitive data.
- Prevent user enumeration and guessable accounts.
- Respond failed logins with status code
400
. - Use generic error message. ie. Authentication failed.
- If 2FA, pass 1st w/invalid token. Fail 2nd.
- cron emails eg. password reset, 2FA, so on.
- Respond failed logins with status code
- Prevent timing attacks.
- Increase report time with each failed login attempt.
- Limit the number of login attempts per second.
- Role based access control (RBAC).
- Opt-in store password hashes in a restricted table.
- Opt-in multi-factor authentication (FA).
- Opt-in timeout sessions.
- Opt-in limit simultaneous sessions per user.
Registration
- Require unique identifier, eg. username.
- Verify account.
- Opt-in multiple contact channels. ie. email, signal, wire.
- Opt-in multi-factor authentication.
- Opt-in notifications when user logs in from new IP.
User Privacy
- Avoid using identifiable user data, such as email, as user identifiers.
- Use indirect object references. eg. unique displayable id.
- Request email only for verification.
- Ask the least amount of identifiable data.
- Encrypt identifiable data before storing it.
Router
- Prevent open redirect attacks. Redirect only to:
- Internal paths, and services.
- Trusted 3rd party services.
- Sanitize
params
.- Whitelist keys.
- Validate & sanitize values.
- Turn off detailed error reports in production.
- Return
400
with a generic error page. - Remove sensitive data before logging errors.
- Return
- Throttle requests to prevent DDOS attacks.
- Help prevent CSRF validating requests headers:
Origin
.Referrer
.
Open redirection
If for legitimate reasons, we allow input-generated redirection:
- Whitelist sites to redirect to.
- Confirm forwarding. Clearly state destination.
- Add tests to:
- Prevent whitelist tampering.
- Refute
location
header can be an invalid url.
Cache
- Prevent web cache deception attacks.
- Only cache assets with a verifiable caching HTTP header.
- verify assets via
integrity
attribute.
- verify assets via
- Cache resource by contents, not extension.
- Only cache assets with a verifiable caching HTTP header.
- Store all static resources in a designated directory.
- Ensure when
/public/non-existent.css
is requested by authenticated user:- Never respond with
202
. - Never render a page, eg. home, as fallback.
- Return
404
response. - Redirection via
302
is ok.
- Never respond with
Robots
Robots usually categorize (index) websites and/or extract (crawl) their data.
Beware, robot rules are merely advisory. A robot can simply ignore them.
- Include index/crawling rules in
robots.txt
. - Include
robots.txt
in the root route. - Add authentication to prevent index/crawling:
- Private, and sensitive data.
- Hosted non-production environments.
- Restricted areas.
- For granular rules use:
- the
X-Robots-Tag
response header. - meta-tags.
- the
A robots.txt
which doesn’t allow any robot to index/crawl a site looks like:
User-agent: *
Disallow: /
Logs
- Filter before logging:
- Identifiable data.
- Private data.
- Restricted actions.
- Ensure logging services only get filtered data.
Gems & Tools
Code quality:
- flog. Code complexity analyzer.
- flay. Code structure analyzer.
- debride. Dead code detector.
- reek. Code-smell detector.
- rubocop. Ruby style analyzer.
- bundler-audit. Gem security audit helper.
Security test:
- OWASP Zed Attack Proxy. Automated security scanner.
- Gauntlt. Cucumber-based security testing framework.
- Nikto. Web server scanner.
Encryption:
Credential Management:
- git secrets. Prevent committing AWS private credentials.
Input handling:
- Sanitize. Whitelist-based HTML and CSS sanitizer.
- Loofah. HTML/XML manipulation and sanitzation gem.
- Pathname. Ruby standard library for handling paths.
HTTPS scanners:
- Pshtt. HTTPS best practices scanner.
- SSL Server Test. Free TLS configuration analysis.
Rack solutions to generate/renew Let’s Encrypt certificates:
Rack compatible authentication & authorization gems:
Logging:
Recommendations
Although outside the scope of this cheat sheet, consider:
- Disable compressed responses to prevent compression based attacks.
- Require latest browser versions.
- Require browser never accepts 3rd party cookies to prevent Heist attack.
Resources
Guides & Cheat Sheets
- Input Validation Cheat Sheet.
- Cross-Site Scripting Prevention Cheat Sheet.
- Password Storage Cheat Sheet.
- OWASP Top Ten Cheat Sheet.
- Code Review Guide.
- OWASP Testing Project.
- Proactive Controls.
- Session Management Cheat Sheet.
- Authentication Cheat Sheet.
Articles
- Secure Input and Output Handling.
- Gemfile Docs.
- Ruby Style Guide.
- The SaaS CTO Security Checklist.
- Ruby CVEs in the NVD.
- Command Injection.
- HTTP Response Splitting.
- How to Migrate to HTTPS.
- Sakurity Research.
- Test for User Enumeration.
- Nvisium Blog.
- Secure Password Storage.
- The Twelve-Factor App Configuration.
- User Enumeration.
- NIST Digital Identity Guidelines.
- Passwords Evolved.
- Wordlists for Random Passphrases.
- Resilient Software Engineering.
- US-CERT Coding Practices.
- Role-based Access Control.
- Insecure Direct Object References.
- The Principle of Least Privilege.
- About /robots.txt.
- Robots meta tag.
- The ultimate guide to the meta robots tag.
- HEIST: HTTP Encrypted Information Stolen through TCP-windows.
- Rack::Attack: Rate limits against DDoS and abusive users.
- Security by Design Principles.
- Web Cache Deception Attack