An Auditor’s Checklist for Securing Ruby Backends on OVH
I. Network Perimeter Hardening on OVH
Auditing Ruby backend security on OVH necessitates a rigorous examination of the network perimeter. This begins with the OVH Control Panel’s firewall configuration, often referred to as “IP Firewall” or “Network Firewall.” The default configuration is typically too permissive. We must ensure that only essential ports are exposed and that traffic is restricted to known, trusted IP ranges where feasible.
For a typical Ruby on Rails application, this means:
- SSH (Port 22): Restrict access to specific bastion host IPs or a very limited set of administrative IPs. Avoid opening SSH to the entire internet.
- HTTP/HTTPS (Ports 80/443): These are generally open to the internet, but application-level security is paramount.
- Database Ports (e.g., 5432 for PostgreSQL, 3306 for MySQL): These should NEVER be exposed directly to the internet. Access should be strictly limited to the application servers within the same private network or via a secure VPN/SSH tunnel.
- Other application-specific ports: Document and justify the necessity of any other open ports.
The OVH IP Firewall rules can be managed via the Control Panel. A common mistake is relying solely on the server’s local firewall (e.g., iptables or ufw) without leveraging the OVH network-level controls. This provides a crucial defense-in-depth layer.
II. Server-Level Security Configuration
Once the network perimeter is secured, we audit the individual server configurations. This involves hardening the operating system and ensuring secure deployment practices for the Ruby application.
A. SSH Access Control
SSH is a primary attack vector. The sshd_config file must be meticulously reviewed.
1. Disabling Root Login and Password Authentication
Enforce key-based authentication and disable direct root login. This significantly reduces brute-force attack surface.
# /etc/ssh/sshd_config PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes ChallengeResponseAuthentication no UsePAM yes
After modifying sshd_config, always test SSH connectivity with a non-root user and a valid SSH key before restarting the SSH service. Then, restart the service:
sudo systemctl restart sshd
2. Limiting SSH Access by User and Group
Use AllowUsers or AllowGroups directives to restrict who can log in via SSH.
# /etc/ssh/sshd_config AllowGroups sshusers deployers
B. Application Deployment Security
The deployment process itself is a critical security checkpoint. Avoid deploying directly as the root user. Use dedicated, unprivileged deployment users.
1. User and Group Management
Ensure the Ruby application runs under a dedicated, non-privileged user. For example, a user named appuser.
sudo adduser --system --group appuser sudo chown -R appuser:appuser /var/www/my_ruby_app sudo chmod -R 750 /var/www/my_ruby_app
2. File Permissions
Strict file permissions are essential. The web server user (e.g., www-data for Nginx/Apache) should only have read access to most application files, and write access only to specific directories like tmp/, log/, and public/uploads/.
# Assuming your app runs as 'appuser' and web server as 'www-data'
sudo chown -R appuser:appuser /var/www/my_ruby_app
sudo find /var/www/my_ruby_app -type d -exec chmod 750 {} \;
sudo find /var/www/my_ruby_app -type f -exec chmod 640 {} \;
sudo chmod -R g+w /var/www/my_ruby_app/tmp
sudo chmod -R g+w /var/www/my_ruby_app/log
sudo chmod -R g+w /var/www/my_ruby_app/public/uploads
C. Web Server Configuration (Nginx Example)
Nginx is commonly used as a reverse proxy for Ruby applications. Its configuration must be hardened.
1. Limiting Request Methods and Headers
Restrict HTTP methods to those actually used by the application. Sanitize or reject overly large headers that could be used for DoS attacks.
# /etc/nginx/sites-available/my_ruby_app
server {
listen 80;
server_name example.com;
# Limit request methods
if ($request_method !~ ^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)$) {
return 405;
}
# Limit header size
client_max_body_size 10m; # Adjust as needed
large_client_header_buffers 2 16k; # Adjust as needed
# Deny access to hidden files
location ~ /\. {
deny all;
}
# ... other configurations for proxy_pass, SSL, etc.
}
2. SSL/TLS Configuration
Ensure strong SSL/TLS configurations. Use modern ciphers and disable older, vulnerable protocols.
# /etc/nginx/sites-available/my_ruby_app (within server block) ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 8.8.4.4 valid=300s; # Use your preferred DNS resolvers resolver_timeout 5s;
Regularly test your SSL configuration using tools like SSL Labs’ SSL Test (https://www.ssllabs.com/ssltest/).
III. Ruby Application Security Best Practices
The application code itself is a primary area of concern. This section focuses on common Ruby/Rails vulnerabilities and their mitigation.
A. Dependency Management and Vulnerability Scanning
Outdated gems are a frequent source of security flaws. Implement a robust process for managing and updating dependencies.
1. Bundler Audit
Use the bundler-audit gem to scan your application’s dependencies for known vulnerabilities.
# Add to your Gemfile gem 'bundler-audit', require: false # Then run: bundle install bundle exec bundler-audit --update bundle exec bundler-audit --scan
Integrate this into your CI/CD pipeline to prevent vulnerable code from being deployed.
2. Regular Updates
Establish a schedule for updating gems, especially those with security implications. Prioritize updates based on vulnerability severity.
B. Input Validation and Sanitization
This is fundamental to preventing injection attacks (SQL, XSS, Command Injection).
1. SQL Injection Prevention
ActiveRecord’s query interface generally protects against basic SQL injection when used correctly. Avoid constructing SQL queries with string interpolation.
# Vulnerable:
User.find_by_sql("SELECT * FROM users WHERE username = '#{params[:username]}'")
# Secure:
User.find_by(username: params[:username])
User.where("username = ?", params[:username])
2. Cross-Site Scripting (XSS) Prevention
Rails’ ERB templating automatically HTML-escapes output by default. However, be cautious when using .html_safe or when rendering user-provided content directly.
# In a Rails view: <%= user.comment %> # Automatically escaped # Potentially dangerous if user.comment contains HTML: <%= user.comment.html_safe %> # If you MUST render HTML from a trusted source: <%= raw user.trusted_html_content %>
For complex scenarios or when dealing with rich text editors, consider using libraries like sanitize.
3. Command Injection Prevention
Avoid executing shell commands with user-supplied input. If absolutely necessary, use methods that properly escape arguments.
# Vulnerable:
system("ls #{params[:directory]}")
# Safer (using backticks with explicit arguments):
system("ls", params[:directory]) # If params[:directory] is just a path component
# Even safer, use Ruby's built-in file system operations:
Dir.glob(File.join(safe_directory_path, "*"))
C. Authentication and Authorization
Robust authentication and authorization are critical. Avoid common pitfalls.
1. Secure Password Storage
Use strong, modern hashing algorithms like bcrypt. Rails’ default Devise gem handles this well.
# Example using bcrypt gem directly
require 'bcrypt'
password_hash = BCrypt::Password.create("my secret password")
# => "$2a$12$..."
# Verification
BCrypt::Password.new(password_hash) == "my secret password"
# => true
2. Session Management
Ensure session cookies are set with appropriate flags (e.g., HttpOnly, Secure, SameSite). Rails handles this by default for production environments.
3. Authorization Checks
Implement authorization checks at every relevant point in the application, not just at the UI level. Use tools like Pundit or CanCanCan, or implement custom logic.
# Example using Pundit
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
after_action :verify_authorized, except: [:index, :new, :create]
after_action :verify_policy_scoped, only: [:index]
def show
authorize @post
# ...
end
def update
authorize @post
if @post.update(post_params)
redirect_to @post
else
render :edit
end
end
private
def set_post
@post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :body)
end
end
D. Sensitive Data Handling
Sensitive data (API keys, database credentials, PII) must be protected.
1. Environment Variables
Never hardcode secrets in the application code or commit them to version control. Use environment variables. For local development, use tools like dotenv. For production, leverage your deployment platform’s secrets management.
# Accessing environment variables in Rails ENV['DATABASE_URL'] ENV['STRIPE_SECRET_KEY']
2. Encryption
Encrypt sensitive data at rest (e.g., PII in the database) using libraries like attr_encrypted or database-level encryption features.
# Example using attr_encrypted class User < ApplicationRecord attr_encrypted :credit_card_number, key: ENV['CREDIT_CARD_ENCRYPTION_KEY'] end
IV. Logging, Monitoring, and Auditing
Effective logging and monitoring are crucial for detecting and responding to security incidents.
A. Comprehensive Logging
Ensure your application and server logs capture sufficient detail for security analysis. This includes:
- Web server access logs (requests, IPs, user agents, status codes).
- Application logs (errors, warnings, security-relevant events like failed logins, authorization failures).
- System logs (auth logs, kernel logs).
Configure log rotation to prevent disk space exhaustion.
B. Centralized Logging and SIEM
For production environments, centralize logs using tools like Logstash, Fluentd, or Filebeat, and send them to a Security Information and Event Management (SIEM) system (e.g., ELK Stack, Splunk, Graylog) for analysis and alerting.
C. Intrusion Detection Systems (IDS) / Intrusion Prevention Systems (IPS)
Consider deploying host-based IDS/IPS solutions (e.g., OSSEC, Wazuh) on your servers. These can monitor log files for suspicious patterns and alert on potential intrusions.
D. Regular Audits
Schedule regular security audits of your OVH infrastructure and Ruby application. This checklist serves as a starting point for such audits.
V. Database Security
The database is often a treasure trove of sensitive information and a prime target.
A. Network Access Control
As mentioned in Section I, database ports should never be exposed directly to the internet. Access should be restricted to application servers via OVH’s private network or secure tunnels.
B. User Privileges
The database user account used by the Ruby application should have the minimum necessary privileges. Avoid using superuser accounts for the application.
C. Encryption
Implement encryption for sensitive data at rest within the database. This can be done at the column level (as shown in Section III.D.2) or using full-disk encryption for the underlying storage.
D. Regular Backups and Testing
Ensure regular, encrypted backups are taken and, critically, that these backups are periodically tested for integrity and restorability. This is vital for disaster recovery and ransomware resilience.