Why do we secure our web applications? It isn’t for style points, that’s for sure; ensuring that our applications are secure means we keep data secure, gain the trust of our users, and protect our organizations from legal and financial repercussions that might result from a security breach.
You won’t need anyone to mention how security in web applications is a hugely important aspect of modern software development. With cyber threats becoming more sophisticated and common, security needs to be reviewed and scrutinized at every step of the development lifecycle—where neglecting it can result in devastating consequences, including data breaches, unauthorized access to sensitive information, and compromised user privacy.
When it comes to building web applications with Ruby, Sinatra stands out as a lightweight and flexible framework. Sinatra’s minimalist approach, provides developers with a framework that allows for flexibility and simplicity, but this simplicity should not be mistaken for being secure by default. As with any level of application development, there needs to be an understanding for implementing necessary security measures to protect web applications from potential vulnerabilities and a range of different attacks.
The most recent CVE specific to Sinatra was discovered in version 2.0 before 2.2.3 and 3.0 before 3.0.4 and was published under CVE-2022-45442. This was found to be related to ingesting user input when allowing users to add custom file names, leading to the possibility of a Reflected File Download attack, mitigated by upgrading Sinatra to 2.2.3 and 3.0.4 at a minimum. Sinatra doesn’t receive a lot of CVEs published against it, but this is only an indication of issues in the framework itself, not how it is used.
In any application which accepts user input, validating and sanitizing user input is crucial to prevent common vulnerabilities like XSS and SQL Injection. Using Ruby Sinatra, you can implement input validation within routes and parameters using built-in features or relevant gems/extensions. For instance, the following example uses regular expression (where =~
compares a regex to a string in Ruby) to enforce a string pattern and rejects any strings not matching the standard “name@email-domain.com” format.
post '/submit_form' do
unless params['email'] =~ /\A\S+@\S+\.\S+\z/
halt 400, 'Invalid email address'
end
# Process form submission
end
Implementing authentication and authorization mechanisms is critical in web applications to control access to resources securely. In Ruby Sinatra, Middleware or extensions like warden-sinatra can be used for user authentication. Below is an example of authenticating users and enforcing access control using the built-in Rack sessions:
get '/admin' do
authenticate!
authorize_admin!
# Display admin dashboard
end
def admin?
session[:user_id] && session[:admin]
end
def authorize_admin!
halt erb_response :not_found unless admin?
end
Another common approach to securing user authentication in web applications is the use of password hashing and salting. By storing hashed passwords instead of plain text passwords, you can protect user credentials even if the database is compromised. Additionally, using a unique salt for each user enhances the security of the stored passwords.
One of the most effective ways to prevent SQL injection attacks is to use parameterized queries. By using placeholders for user input and binding the values to those placeholders, you can ensure that user input is treated as data and not executable code.
A popular Ruby Gem Database Toolkit called “Sequel” lets us use Object-Relational Mapping (ORM) to create parameterized queries to handle user input, letting developers use a syntax more similar to Object-Oriented Programming (OOP) principles, the example below allows searching for a user ID:
get '/users/:id' do
user = DB[:users].where(id: params[:id]).first
# Handle user data securely
end
Denial of Service (DoS) attacks aim to attack a service’s availability by overwhelming the applications resources and capabilities to respond to requests. In Ruby Sinatra, methods to mitigate this can be by implementing the rack-attack package for request throttling, the process for this is fairly straightforward, where in this example we configure rack-attack to throttle each IP to a maximum of 5 requests per minute. This prevents too many requests coming from the same source which might indicate a concentrated effort to disturb the availability of the service:
# app.rb
require 'sinatra'
require 'rack/attack'
# Configure Rack::Attack for request throttling
use Rack::Attack
# Define your Sinatra routes and application logic here
# Configure Rack::Attack for request throttling
Rack::Attack.throttle('req/ip', limit: 5, period: 1.minute) do |req|
req.ip # Throttle requests based on IP address
end
Finding resources and tools to get a better understanding of secure coding in Ruby Sinatra, and any programming language for that matter, can be difficult. SecureFlag have been busy adding more virtual labs concerned with securing web application frameworks to our existing catalog. Our labs let developers experience securing their code in real-life environments without risking impacting code already in production.
Secure Code Training plays a massive role in improving the overall security of developing applications and catching any issues before they hit production. Our own data shows a reduction in security rework of 24% after implementing a secure training program, which you can read more about in our guide.
Securing your Ruby Sinatra applications is a continuous process much like any other application, requiring various security measures to protect your web applications against common security vulnerabilities and protect your users’ data and privacy.
As commonly mentioned in the industry, security is not a one-time effort but an ongoing commitment. By making security a priority from the beginning and integrating it into every aspect of your development process, you can build robust and secure Ruby Sinatra applications that can survive in today’s landscape.