UpCloud
Effortless global cloud infrastructure for SMBs
Introducing
If you’re interested in what we have to offer, contact sales or fill out a contact form.
Our support live chat is available for our customers 24/7. You can also email our support team.
Send us an email to give feedback or to say hello.
Start a new journey
Why Partner with UpCloud?
I’ve been passionate about the hosting industry since 2001. Before founding UpCloud, my first company grew to become one of Finland’s largest shared web hosting providers, serving over 30,000 customers. Along the way, I faced the same challenges many of you know well—24/7 on-call responsibilities, solving technical issues, and managing customer inquiries.
At UpCloud, we’ve designed a platform that solves these challenges, offering reliability, scalability, and unparalleled support. We understand the pressures you face because we’ve been there too. Partner with us, and let’s help you focus on growing your business while we handle the rest.
Sincerely, Joel Pihlajamaa CTO, Founder
Login
Sign up
Posted on 8.4.2025
Ruby on Rails (RoR) is a robust, open-source web development framework designed to help developers build applications efficiently. Written in the Ruby programming language, this framework provides a well-defined structure that organizes an application’s code, databases, and logic for seamless development. It follows the Model-View-Controller (MVC) architectural pattern, which separates the business logic, user interface, and controller—the component that acts as an intermediary between the user interface and business logic.
Despite its structured architecture, built-in security features, and rapid development capabilities, securing Ruby on Rails applications is crucial to protect them from potential vulnerabilities and cyber threats. Applications may become vulnerable to exploitation, cyberattacks, and data breaches without proper security measures.
In this Tutorial, we’ll walk you through setting up a secure Ruby on Rails environment, while digging into the best security tricks for today’s web apps. From configuration and authentication to encryption and monitoring, you’ll pick up key skills to shield your Rails app from threats.
Security vulnerabilities remain a major concern across all web applications. Many applications are built without proper security assessments and best practices, making them easy targets for exploitation. Even those built with Ruby on Rails aren’t immune to attacks if safeguards aren’t in place.
Thankfully, Rails provides built-in security features like CSRF protection, strong parameters, and automatic input sanitization to help defend against common threats. Let’s explore a few of the most critical threats and how Rails helps address them.
SQL injection occurs when an attacker injects malicious SQL statements into a query, manipulating the database into executing unintended commands. For example, an improperly sanitized input field could allow an attacker to bypass authentication and gain access to sensitive user data.
Prevention in Rails:
User.where("email = ?", params[:email])
Cross-site scripting (XSS) is an attack where malicious scripts are injected into a webpage. When unsuspecting users visit the page, the injected script runs in their browser, making it possible for their credentials, session tokens, or other sensitive information to be stolen. Sometimes, attackers modify the page content to mislead users into taking unintended actions.
<%= sanitize(user_input) %>
CSRF attacks trick users into performing unintended actions while authenticated in a web application. An attacker can embed a hidden malicious request into a seemingly harmless webpage, tricking the victim into unknowingly submitting a form, changing account settings, or even transferring funds. Since the user’s session authenticates the request, the server assumes it’s legitimate and processes it.
protect_from_forgery
class ApplicationController < ActionController::Base protect_from_forgery with: :exception end
Now that you’ve been exposed to the threats and security measures in Rails let’s look at how to set up a secure Ruby on Rails environment on an UpCloud server.
This section walks through setting up a secure Ruby on Rails environment on an UpCloud server running Ubuntu 24.04 LTS.
Before we begin, ensure you have:
Step 1: Connect to Your Server
First, SSH into your UpCloud server as root:
ssh root@your-upcloud-ip
Step 2: Update and Secure the System
Update packages and reboot to apply changes:
apt update && apt upgrade -y reboot # Reconnect after reboot
Step 3: Create a Secure Non-Root User.
Using a non-root user enhances security. Create a user ruby, grant it sudo access, and switch to it. Why? Running applications as root is a security risk. This ensures Rails runs with limited privileges.
ruby
sudo useradd -m -s /bin/bash -G sudo ruby && echo "ruby:your-preferred-password" | sudo chpasswd su - ruby
Step 4: Install Ruby 3.4.2 with rbenv
Installing the latest stable version of Ruby, like 3.4.2, is a best practice for security and performance. Newer versions include critical security patches, improved efficiency, and compatibility with the latest Rails version (8.0.2).
4.1. Install dependencies:
sudo apt install -y git curl libssl-dev libreadline-dev zlib1g-dev autoconf bison build-essential libyaml-dev libncurses5-dev libffi-dev libgdbm-dev
4.2. Set up rbenv to manage Ruby versions:
rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc echo 'eval "$(rbenv init -)"' >> ~/.bashrc source ~/.bashrc
4.3. Install ruby-build plugin and Ruby 3.4.2:
ruby-build
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build rbenv install 3.4.2 rbenv global 3.4.2
Step 5: Install Node.js and Yarn
Rails requires JavaScript runtime support, so install Node.js 20.x and Yarn using the following command:
curl -sL https://deb.nodesource.com/setup_20.x | sudo bash - sudo apt install -y nodejs sudo npm install -g yarn
Step 6: Configure Your UpCloud PostgreSQL Database
UpCloud offers two secure connection options for accessing your managed PostgreSQL database:
Choose the approach that fits your deployment environment. We’ll cover both.
6.1 Option 1: Secure with UpCloud’s Utility Network (Private Connection)
From your UpCloud DBaaS Overview page:
This setup allows only servers inside your UpCloud account and within the same zone to communicate with the database over a private internal network. Traffic stays inside UpCloud’s internal infrastructure—safer and faster than routing over the public internet.
6.2 Option 2: Secure Public Access (if not using Utility Network)
If you’re not using private networking, and instead want to connect over the public internet:
This method requires extra care. Always enforce SSL and limit access to trusted IPs only.
6.3: Take Note of Your DB Credentials
Still in the Overview section:
Go to the Private connection section (or Public connection, based on your setup)
Scroll to the Auth section
Tip: You can also create your own database user instead of using the default.Go to the Users tab → click Create User → enter your desired username and password.
This lets you set up custom access control for different environments (e.g., development, staging, production).
6.4 Create Separate Databases for Development and Test
In the Databases tab:
This allows Rails to manage separate environments securely and cleanly.
6.5 Install PostgreSQL Development Libraries
Install the necessary system packages for Rails to connect with PostgreSQL:
sudo apt install -y libpq-dev
These libraries are required for the pg gem to compile and work correctly.
Step 7: Install Rails and Create a New Application
7.1 Run the command below to install the latest stable version of Rails:
gem install rails
7.2. Create a new Rails application with PostgreSQL as the database:
rails new secure_blog --database=postgresql cd secure_blog
Step 8: Configure Rails Database Credentials
8.1. Edit the database.yml file to match PostgreSQL credentials:
database.yml
nano config/database.yml
8.2. replace development and test sections with:
development: adapter: postgresql database: secure_blog_development username: <%= ENV['DB_USERNAME'] %> password: <%= ENV['DB_PASSWORD'] %> host: <%= ENV['DB_HOST'] %> port: <%= ENV['DB_PORT'] %> test: adapter: postgresql database: secure_blog_test username: <%= ENV['DB_USERNAME'] %> password: <%= ENV['DB_PASSWORD'] %> host: <%= ENV['DB_HOST'] %> port: <%= ENV['DB_PORT'] %>
Click on ctrl x, y, and enter to save.
Note: Hardcoding credentials in config files can be a security risk. Instead, use environment variables:
8.3. Set these in the terminal:
export DB_USERNAME=your_upcloud_DB_username export DB_PASSWORD=your_upcloud_DB_password export DB_HOST=your_upcloud_DB_hostname # Use private or public hostname export DB_PORT=your_upcloud_DB_port
Step 9: Initialize Database and Start Rails Server
Run the following commands to set up the database and start the Rails app:
rails db:migrate rails server --binding=0.0.0.0
Your Rails app should now be accessible at: http://your-server-ip:3000
With the application successfully running, let’s take a look at some best practices for securing your Rails application, covering authentication, encryption, and vulnerability protection.
Securing a Ruby on Rails application starts at the configuration level. Misconfigured settings can expose sensitive data, create vulnerabilities, or allow attackers to manipulate the system. Let’s explore key measures to fortify your Rails application’s configuration.
As seen in Step 8.2, we avoided hardcoding database credentials in database.yml. While hardcoding these values may not pose an immediate risk in a local development environment, it is a critical vulnerability in production. Exposing this file in a .git repository automatically leaks sensitive data, increasing the risk of unauthorized access.
To enhance security, Rails replaced secrets.yml with config/credentials.yml.enc, encrypting secrets by default. Instead of manually setting credentials via export commands, store them in credentials.yml.enc for added security.
secrets.yml
config/credentials.yml.enc
credentials.yml.enc
Run the following command to edit encrypted credentials:
EDITOR=nano rails credentials:edit
Add your sensitive data inside:
secret_key_base: your_random_key_here db_password: securepassword
Then, reference it securely in database.yml:
database.yml:
production: adapter: postgresql database: secure_blog_production username: blog_user password: <%= Rails.application.credentials.db_password %>
The config/credentials.yml.enc file, even when committed to a Git repository, remains secure because it is encrypted. It can only be decrypted using the config/master.key file, which is never committed to Git and should always be stored securely.
config/master.key
Unrestricted user input is one of the most overlooked security risks in web applications. Attackers can exploit input vulnerabilities to modify data, escalate privileges, or inject malicious code. Rails enforce Strong Parameters to mitigate this risk, preventing mass assignment vulnerabilities.
In 2012, GitHub suffered a breach when an attacker injected unauthorized parameters, allowing them to modify user account settings. This happened because version 3 of Rails, which was used then, did not restrict which parameters could be updated; this allowed attackers to manipulate data at will.
Rails 4 to 8.0.2 prevents this by enforcing strong parameters:
# app/controllers/posts_controller.rb class PostsController < ApplicationController def create @post = Post.new(post_params) if @post.save redirect_to @post, notice: "Post created!" else render :new end end private def post_params params.require(:post).permit(:title, :content) end end
require(:post):
permit(:title, :content):
Without strong parameters, an attacker could send:
{ "post": { "title": "Hacked Post", "is_admin": true } }
If Rails didn’t enforce parameter whitelisting, this could grant admin privileges, manipulate account settings, or alter sensitive fields.
Rails 8.0.2 strictly enforces mass-assignment security, ensuring that unpermitted attributes raise an error instead of silently overriding database records.
A common mistake in web security is relying on blacklisting (e.g., blocking specific fields like admin). However, blacklists fail when new fields are introduced, allowing attackers to bypass restrictions.
Instead, Rails enforces a whitelisting approach, processing only explicitly permitted fields.
def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end
If an attacker attempts:
{ "user": { "name": "Attacker", "admin": true } }
Rails will automatically discard the admin field, preventing unauthorized privilege escalation.
For complex forms, such as posts with tags, Rails allows structured whitelisting to prevent arbitrary nested input.
def post_params params.require(:post).permit(:title, :content, tags: [:name]) end
This ensures that only title, content, and tags[:name] are accepted—anything else is ignored.
Authentication verifies who users are, while authorization dictates what they can do. Getting these right is non-negotiable for a secure Rails app. Proper systems and tools must be in place to ensure applications adhere to industry security standards. Techniques like Role-Based Access Control (RBAC) and Multi-Factor Authentication (MFA) are crucial in securing modern applications. Let’s explore some.
Authentication is used to confirm user identities. Ruby provides reusable authentication packages (Ruby gems) to streamline this process. Popular options include:
Devise is one of the most popular authentication solutions for Rails. It provides an out-of-the-box system for:
To integrate Devise into your Rails application:
cd ~/secure_blog nano Gemfile
Add Devise to the Gemfile:
gem 'devise'
Then install the gem:
bundle install rails generate devise:install
This automates authentication, saving developers time while ensuring secure user handling.
Basic password hashing is not enough to prevent brute-force attacks. Bcrypt enhances security by:
Devise already depends on bcrypt, so no additional setup is required when using it. However, if manually implementing authentication, install bcrypt separately:
gem 'bcrypt', '~> 3.1.7'
While authentication ensures users can log in, authorization determines what they can access. Implementing RBAC prevents unauthorized actions by restricting user roles.
A least-privilege system ensures users only have access to what they need:
Using Policy Objects, we can define rules, conditions, and logic to manage user permissions in Rails.
Example: Implementing a Policy Object in Rails:
# app/policies/post_policy.rb class PostPolicy attr_reader :user, :post def initialize(user, post) @user = user @post = post end # Can the user view the post? def show? true # All roles can view posts end # Can the user create a post? def create? user.editor? || user.admin? # Editors and admins can create posts end # Can the user edit a post? def update? (user.editor? && post.author == user) || user.admin? # Editors can edit their posts; admins can edit any post end # Can the user delete a post? def destroy? user.admin? # Only admins can delete posts end end
This decouples business logic from controllers, keeping authorization clean and scalable.
Credential leaks and phishing attacks can still compromise accounts, even with strong passwords. 2FA adds an extra security layer, requiring users to verify their identity with an additional authentication factor (e.g., a one-time password (OTP) or authenticator app).
To integrate 2FA with Devise, use the devise-two-factor gem:
gem 'devise-two-factor'
Then, configure it within your User model:
class User < ApplicationRecord devise :two_factor_authenticatable end
Note: You’ll need a database tweak—like an otp_secret column and some setup for OTP generation.
otp_secret column
Encryption is essential for securing applications and websites, whether data is in transit or at rest. HTTPS is non-negotiable in modern workloads as it encrypts communication between a user’s browser and the server, protecting sensitive data from interception.
Rails provides the force_ssl configuration to ensure the applications always run under HTTPS.
force_ssl
To enforce encryption in production environments, enable the force_ssl flag in config/environments/production.rb:
config/environments/production.rb:
config.force_ssl = true
To enforce HTTPS across all environments, enable force_ssl in config/application.rb
config/application.rb
However, enforcing HTTPS at the application level is not enough—the server itself must be configured correctly. For deployments within UpCloud servers, this involves:
Installing Nginx to handle incoming traffic:
sudo apt install nginx
Setting up SSL certificates with Let’s Encrypt (Certbot):
sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Configuring Nginx to redirect HTTP to HTTPS and enable SSL/TLS:
erver { listen 80; server_name yourdomain.com www.yourdomain.com; return 301 https://$host$request_uri; } server { listen 443 ssl; server_name yourdomain.com www.yourdomain.com; ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; }
Data transmitted between a client (browser, API, or external service) and a server must be encrypted in transit to prevent man-in-the-middle (MITM) attacks.
HTTPS uses TLS (Transport Layer Security) to encrypt all communication, ensuring:
For data stored in the database, application-level encryption is crucial. Rails provides Active Record Encryption, allowing developers to declare specific attributes to be encrypted. It means that;
Example of encrypting fields in a Rails model:
class User < ApplicationRecord encrypts :email, :phone_number end
With this setup:
Hardcoding API keys poses a serious security risk, especially if the codebase is exposed publicly. API keys must be stored securely to prevent unauthorized use.
One approach is using environment variables with the dotenv-rails gem:
dotenv-rails
Install dotenv-rails by adding it to the Gemfile:
gem 'dotenv-rails'
Create a .env in the root directory and store your variables
.env
API_KEY=up15281cloud
Access the key inside your Rails app:
ENV['API_KEY']
Always add .env to .gitignore to prevent secret leaks in version control:
.gitignore
After setting up an application, proper logging and monitoring are essential for ensuring its health and smooth operation. Developers rely on logs to pinpoint issues, troubleshoot errors, and track security events. Fortunately, Rails has built-in logging and monitoring capabilities that can be enhanced using gems like:
Rails allows custom event logging using ActiveSupport::Notifications:
ActiveSupport::Notifications:
Rails.logger.info "Suspicious login attempt from IP: #{request.remote_ip}"
Hosting Rails on an UpCloud cloud server provides real-time infrastructure monitoring, tracking:
Using these tools security threats can be spotted, watch out for:
A slow application leads to poor user experience, high bounce rates, and lost revenue. Users are more likely to switch to an alternative if a competing app loads even a few seconds faster.
Building a Ruby on Rails application on UpCloud’s high-performance infrastructure is a great start, but proper optimization techniques ensure:
Two essential techniques for performance and scalability are caching and CDN integration.
Caching stores frequently accessed data in memory to reduce server load and improve response times. Rails have built-in caching mechanisms, which includes:
Example:
<% cache @article do %> <%= render @article %> <% end %>
If this article was loaded before, Rails fetches it from cache instead of reloading it from the database.
ActiveRecord::Base.cache do Post.find(1) # Runs a database query Post.find(1) # Retrieves result from cache, no new query end
The second query fetches the result from memory instead of hitting the database, reducing load time.
A Content Delivery Network (CDN) improves application performance by caching and serving content from servers closer to users. This reduces:
Securing a Ruby on Rails application goes beyond just writing clean code—it requires a proactive approach to security, performance, and scalability. You can avoid vulnerabilities and protect your workload from threats by adhering to best practices like configuring secure environment files, enforcing HTTPS, implementing strong authentication, encrypting sensitive data, and monitoring logs.
Plus, you can ensure your application remains fast and scalable by using performance optimizations like caching, CDNS, and UpCloud’s high-performance servers and infrastructures.
However, security is a continuous task. Regularly updating dependencies, applying security patches, and monitoring security advisories are crucial to staying immune to potent risk. Building a secure and scalable Rails application is about continuous improvement—stay informed, implement best practices, and keep your application resilient and future-ready.
Your email address will not be published. Required fields are marked *
Comment *
Name *
Email *
Website
Save my name, email, and website in this browser for the next time I comment.
Δ
See all tutorials