Ruby On Rails

Outil

Brakeman

Brakeman est un scanner de vulnérabilité pour application Ruby On Rails.

Utilisation:

$ brakeman /path/to/rubyapp

ressource: https://github.com/presidentbeef/brakeman

Structure d'un projet RoR

.
├── Dockerfile #software version, hardcoded credentials
├── Gemfile #software versions
├── Gemfile.lock #software versions
├── app
│   ├── assets # images, video etc
│   ├── controllers #all application logic located here
│   │   ├── admin_controller.rb
│   │   ├── users_controller.rb
│   ├── helpers #helper - method that is (mostly) used to share reusable code
│   │   ├── admin_helper.rb
│   ├── mailers #allows send emails from your application using mailer classes
│   │   └── user_mailer.rb
│   ├── models #Ruby class that is used to represent data 
│   │   ├── user.rb
│   └── views # HTML templates
│       ├── admin
│       │   ├── get_all_users.html.erb
├── config #app configuration, should be reviewed because developers can disable security features
│   ├── application.rb #application configuration
│   ├── boot.rb
│   ├── database.yml #database config, may contain hard-coded creds
│   ├── environment.rb
│   ├── environments
│   │   ├── development.rb #application configuration
│   ├── initializers
│   │   ├── constants.rb #hardcoded credentials
│   │   ├── filter_parameter_logging.rb #logging
│   │   ├── html_entities.rb #Enables or disables the escaping of HTML entities in JSON serialization
│   │   ├── key.rb #hardcoded credentials
│   │   ├── secret_token.rb #cookie signing
│   │   ├── session_store.rb #how session store is organized
│   ├── locales
│   │   └── en.yml #hardcoded credentials
│   ├── routes.rb #First thing to investigate, application routing
│   ├── secrets.yml #Is credentials/secrets encrypted?
│   └── secrets2.yml #Is credentials/secrets encrypted?
├── db
│   ├── schema.rb #database schema
│   └── seeds.rb #database data, may contain hard-coded creds
├── lib #extended modules
│   ├── encryption.rb #encryption
├── log #log files
├── public #static files and compiled assets
│   ├── 404.html
│   └── robots.txt
├── script
├── spec #for testing purposes
└── vendor #third-party code

Points d'interet

/config/database.yml -  Peut contenir des identifiants de production
/config/initializers/secret_token.rb - Contient les secrets utilisés pour hash les cookies
/db/seeds.rb - Peut contenir des données de départ, y compris l'utilisateur administrateur bootstrap.
/db/development.sqlite3 -  Peut contenir des données sensibles

Routing

Permet de mapper des routes avec ses gestionnaires pour comprendre la structure API de l'application. Le routeur détermine quel type de contrôleur et d'action doit réellement exécuter le code. Le routage des applications est décrit dans le fichier /config/routes.rb.

ressources:

Controllers

Views

Les vues sont stockées dans le chemin de modèle suivant:

app/views/[controller]/[view_name].html.erb

Une vue est une simple page HTML gérée par le moteur de modèle ERB, qui affiche les valeurs renvoyées par le contrôleur.

ressource: https://guides.rubyonrails.org/layouts_and_rendering.html

Models

Un modèle est une classe Ruby utilisée pour représenter des données. De plus, les modèles peuvent interagir avec la base de données de l'application via une fonctionnalité de Rails appelée Active Record.

Mauvaises pratiques

SSL forcé désactivé

#config/environments/production.rb
config.force_ssl = true

Crypto

Cookies

La bonne pratique est:

Rails.application.config.action_dispatch.signed_cookie_digest = "SHA256"

Env

Les environnements doivent utiliser une clé aléatoire présente dans config/credentials.yml.enc et la clé doit être chiffrée.

Mass assignement

Whitelist_attributes doit être sur false:

config.active_record.whitelist_attributes=false

Tout paramètre peut être utilisé lors d’un appel de mass assignment lorsque .permit! est utilisé.

Exemple:

def create
  user = User.new(user_params)
...
...

def user_params
  params.require(:user).permit!
end

Permet ceci,

User.new(
  :email => "email@email.com",
  :admin => "true", # Or la valeur de admin ne devrait pas être modifiable
  :password => "password",
  :first_name => "John",
  :last_name => "Doe"
)

XSS

Si ActiveSupport::escape_html_entities_in_json = false :

  • to_json() = potentielle XSS

RoR échape le code html par défaut mais certaines méthode dépréciées permettent d'outrepasser cette protection:

  • raw

  • html_save

  • content_tag

Exemples

  • html = "<div>#{name}</div>".html_safe

  • content_tag :p, "Hello, #{name}”

  • raw @user.name

Contourner le moteur de template:

  • ERB.new("<div>#{@user.name}</div>").result

  • render inline: "<div>#{@user.name}</div>”

  • render text: "<div>#{@user.name}</div>”

Variables explicitements unescaped:

  • <%= name.html_safe %>

  • <%= content_tag :p, "Hello, #{name}" %>

  • <%= raw @user.name =>

  • <%== @user.name %>

Template placé à un endroit dangereux:

  • <div class=<%= classes %></div>

  • <a href="<%= link %>"></a>

  • <%= link_to "Here", @link %>

  • <script>var name = <%= name %>;</script>

OS injection

eval("ruby code here")
system("os command here")
`ls -al /` # (backticks contain os command)
exec("os command here")
spawn("os command here")
open("| os command here")
URI#open from open-uri
Process.exec("os command here")
Process.spawn("os command here")
IO.binread("| os command here")
IO.binwrite("| os command here", "foo")
IO.foreach("| os command here") {}
IO.popen("os command here")
IO.read("| os command here")
IO.readlines("| os command here")
IO.write("| os command here", "foo")
syscall
%x() %x %x{} %x-os-
popen<n>
exec
Open3.popen3()
fork()
PTY.spawn()
constantize

SQLi

La concaténation de l'entrée utilisateur avec le paramètre de requête SQL peut entraîner une injection SQL.

Exemple:

User.where("name = '#{params[:name]}'") #Vulnérable

#Parametrized
User.where(["name = ?", "#{params[:name]}"]) #Pas vulnérable
User.where({ name: params[:name] }) #Pas vulnérable

SSTI

ERB.new("<div>#{@user.name}</div>").result

Insecure deserialization

Vérifier que Marshal.load n'est pas utilisé pour déserialiser des données apportées par les utilisateurs.

Dernière mise à jour