My Sinatra Project
SECURITY
Something that became very important during the building of my Sinatra project was SECURITY. It is important that once your application is deployed that you ensure that you are taking proper precautions to make sure that your application is secure from cyber attacks. While building my first Sinatra project, I learned a lot about some of the most basic and simple ways to make sure that your project is secure.
1. gitignore
Some may not agree, but I feel like every project on Github should have a .gitignore
file. A .gitignore
file allows us to "hide" certain files by preventing those files from being pushed/tracked by git. It is very simple to use, just create a file named .gitignore
and then add the name of the file that you want to ignore. So for example, I had a notes file that I wanted to keep for myself and I did not want the world/github to know about. I created my .gitignore file and then created a file named secret_notes.txt.
Within my .gitignore
file, I added the following:
secret_notes.txt
It was that simple.
If you recall from my previous post , when building a Sinatra project, your environment will have a "SINATRA_ENV" which defines your deployment environment, configures our database, and requires all the files in our app.require a password. My project was no different, my environment looked like this:
ENV['SINATRA_ENV'] ||= "development"
if ENV['SINATRA_ENV'] == "development"
require_relative "../secrets"
end
require 'bundler/setup'
Bundler.require(:default, ENV['SINATRA_ENV'])
ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database => "db/#{ENV['SINATRA_ENV']}.sqlite"
)
The above requires my "../secrets"
, but when you go to my Github repo the secrets file is not available, as you may have guessed, the file was placed in my gitignore
file and although you can not see it I will tell you that I placed a secure sessions password and api keys in the secrets.rb
file.
2. Securing Users Password
One way to protect the users interacting with your application is to protect their passwords. An easy way to do this is to use the bcrypt-ruby gem. The gem essentially takes a users password and encrypts it by using a hash algorithm.
I used bcrypt in my application by adding the gem to my gemfile by running: bundle add bcrypt
The bcypt Ruby gem provides you with has_secure_password
method. The has_secure_password method encrypts passwords by hashing and salting the passwords and generating a password_digest
. bcrypt requires that you use password_digest
as your attribute name in your user migrations table. I did that in my users table, like this:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :username
t.string :email
t.string :location
t.integer :age
t.string :password_digest
t.timestamps
end
end
end
Then within my users model, I made sure add the has_secure_password
macro inside of my User class, like this:
class User < ActiveRecord::Base
has_secure_password
has_many :watchlists
has_many :stocks, through: :watchlists
validates :username, uniqueness: true
validates :username, presence: true
validates :email, presence: true
end
2. Sessions & User Authentication
A great way to ensure that someone is who they claim to be when they sign up and login is to use cookie and session based authentication.
What is cookie and session based authentication?
Cookies and Sessions are used to store information. Cookies are only stored on the client-side machine, while sessions get stored on the client as well as a server.
What that means is that once you sign up a cookie is saved on the browser and a session is stored on the server. From then on every time you you log in or make future request, some of data stating that it is you gets sent with your HTTP request, confirming that you are an authenticated user.
To set up my sessions I enabled sessions and created a sessions secret to my applications_controller.
enable :sessions
set :session_secret, ENV['SESSION_PASSWORD']
3. Secure password
How did I create a secure sessions password for my environment? I generated a secured random number by using the sysrandom gem. It is recommended that everyone use a gem similar to this to generate secure passwords. Simply do the following in your terminal to get a secure password:
First install the sysrandom:
gem install sysrandom
Then run the following, which will generate a random string of hexadecimals:
ruby -e "require 'sysrandom/securerandom'; puts SecureRandom.hex(64)"
It should return something similar to this:
5ac1ac3c2ec64ef76ac91018059f541b7e8f437fbda1ccddb4f2c56a9ccf1e75
I used that number to require it in my environment, I also required my api numbers provided by the IEX CLOUD api.
My secrets.rb
file looked like this:
ENV['SECRET_TOKEN_ID'] = "sk_oiherbnbdlfoiuhejkwbf9ubkjdhfuihf"
ENV['PUBLISHABLE_TOKEN_ID'] = "pk_kjdhfewuohjwni23yhuijkebiwr"
ENV['SESSION_PASSWORD'] = "5ac1ac3c2ec64ef76ac91018059f541b7e8f437fbda1ccddb4f2c56a9ccf1e75"
3. Sanitizing User Input
Simply put, when a user is going to your website, they may not have the best intentions. For me, I wanted a user to come to my website and create a Watchlist name through a form like this:
<h3>Create a New Watchlist</h3>
<form action="/watchlists" method="POST">
<input type="text" name="watchlist[name]" id="watchlist_name" placeholder="Watchlist Name"><br>
<input type="submit" value="Create Watchlist">
</form>
But what if instead of the user inputting text, they inserted a piece of javascript that generated a pop up box on the application, something like this:
<script>
function myFunction() {
alert("Hello! I am an alert box!");
}
</script>
What if the user created a script that jeopardized my entire application and created a pop up box for all users using my application. There are ways to prevent this and I learned two different ways to avoid this kind of attack while building this project.
Using The Sanitize Gem
The first way I learned was to use the sanitize gem to avoid SQL injection, cross-site scripting (XSS), and remote file inclusion (RFI).
The gem allows us to Sanitize.fragment(html)
to
I chose to use Sanitize.fragment(html)
which uses its strictest settings by default, which means it will strip all HTML and leave only safe text behind. This gem is helpful because you can personalize it to allow certain html elements in the input from users. Feel free to look at the gem docs for more examples.
Personally, I used the gem in my post '/watchlists'
within my watchlist_controller after a user creates a new name for a watchlist, it will Sanitize the user input and save it after it is Sanitized, like this --
post '/watchlists' do
authenticate
if params[:watchlist][:stock] == nil
@watchlist = current_user.watchlists.new(name: Sanitize.fragment(params[:watchlist][:name]), user_id: params[:user_id])
if @watchlist.save
flash[:message] = "You have successfully created a watchlists."
redirect "/watchlists"
Using .Gsub
When you google grep and gsub you'll see something like this:
Grep search for matches to argument pattern within each element of a character vector: they differ in the format of and amount of detail in the result and .gsub replaces all occurrences. If replacement contains backreferences which are not defined in pattern the result is undefined (but most often the backreference is taken to be "")
To grasp a better understanding, I collaborated with some of my classmates and learned how to prevent a user from inputting inappropriate data into my application while signing up. One way is by using grep, regex and .gsub.
Within my signup controller you will see that the post signup
has the following information:
post '/signup' do
@u = User.new(:username => params[:username].gsub(/[\<\>\/]/, ""),
:password => params[:password],
:email => params[:email].gsub(/[\<\>\/]/, ""),
:location => params[:location].gsub(/[\<\>\/]/, ""),
:age => params[:age].gsub(/[\<\>\/]/, ""))
if @u.save
session[:user_id] = @u.id
redirect "/watchlists"
else
erb :'sessions/signup'
end
end
The .gsub
searches for matches in my argument pattern (/[\<\>\/]/, "")
and if it matches it will result in an undefined/blank answer. So if someone signs up and puts <h1> Hello</h1>
as there username. It will result in a username of h1helloh1
removing the <>
tags and essentially not effecting the application at all, but instead gives the user a ridiculous username.
I learned a lot about how to protect my application and the individuals using my application while building this application and look forward to learning more ways to protect future projects that I build.
Resources
Password_digest column in User migration table
Add Authentication to Your Rails App With bcrypt
Feel free to use my Sinatra Project and contribute to it.
Top comments (0)