Skip to content

joevin-slq-docto/IDORails

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

IDORails

This README will you make able to rebuild this app, without source code, by following this tutorial.

IDORs has been left on articles#show and articles#destroy.

Create a basic application

rails new IDORails
cd IDORails

bin/rails generate controller Articles index --skip-routes
bin/rails generate model Article title:string body:text

bin/rails db:migrate

# append in config/routes.rb
Rails.application.routes.draw do
  root "articles#index"

  resources :articles
end

# append in app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
  def index
    @articles = Article.all
  end

  def show
    @article = Article.find(params[:id])
  end

  def new
    @article = Article.new
  end

  def create
    @article = Article.new(article_params)

    if @article.save
      redirect_to @article
    else
      render :new, status: :unprocessable_entity
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])

    if @article.update(article_params)
      redirect_to @article
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def destroy
    @article = Article.find(params[:id])
    @article.destroy

    redirect_to root_path, status: :see_other
  end

  private
    def article_params
      params.require(:article).permit(:title, :body)
    end
end

# append in app/models/article.rb
class Article < ApplicationRecord
  validates :title, presence: true
  validates :body, presence: true, length: { minimum: 5 }
end

# append in app/views/articles/index.html.erb
<h1>Articles</h1>

<ul>
  <% @articles.each do |article| %>
    <li>
      <%= link_to article.title, article %>
    </li>
  <% end %>
</ul>

<%= link_to "New Article", new_article_path %>

# create file and append in app/views/articles/show.html.erb
<h1><%= @article.title %></h1>

<p><%= @article.body %></p>

<ul>
  <li><%= link_to "Edit", edit_article_path(@article) %></li>
  <li><%= link_to "Destroy", article_path(@article), data: {
                    turbo_method: :delete,
                    turbo_confirm: "Are you sure?"
                  } %></li>
</ul>

# create file and append in app/views/articles/new.html.erb
<h1>New Article</h1>

<%= render "form", article: @article %>

# create file and append in app/views/articles/edit.html.erb
<h1>Edit Article</h1>

<%= render "form", article: @article %>

# create and append in app/views/articles/_form.html.erb
<%= form_with model: article do |form| %>
  <div>
    <%= form.label :title %><br>
    <%= form.text_field :title %>
    <% article.errors.full_messages_for(:title).each do |message| %>
      <div><%= message %></div>
    <% end %>
  </div>

  <div>
    <%= form.label :body %><br>
    <%= form.text_area :body %><br>
    <% article.errors.full_messages_for(:body).each do |message| %>
      <div><%= message %></div>
    <% end %>
  </div>

  <div>
    <%= form.submit %>
  </div>
<% end %>

Execute bin/rails server and check that everything works.

Installing Devise

# More info -> https://github.com/heartcombo/devise
bundle add devise

rails generate devise:install
rails generate devise user

bin/rails db:migrate

# append this in app/controllers/articles_controller.rb
before_action :authenticate_user!

# set this in config/initializers/devise.rb
config.sign_out_via = :get

# append this body of app/views/layouts/application.html.erb
  <body>
    <p style="color: green"><%= notice %></p>
    <p style="color: red"><%= alert %></p>
    <hr />
    <div>
      <%= link_to "Home", root_path, :class => 'navbar-link'  %> |
      <% if user_signed_in? %>
        Logged in as <strong><%= current_user.email %></strong>.
        <%= link_to "Logout", destroy_user_session_path, :class => 'navbar-link'  %>
      <% else %>
        <%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link'  %> |
        <%= link_to "Sign in", new_user_session_path, :class => 'navbar-link'  %>
      <% end %>
    </div>
    <hr />
    <%= yield %>
  </body>

# add one-to-many relation between articles and users
rails g migration AddUserRefToArticles user:references
bin/rails db:reset ; bin/rails db:migrate

# modify models/article.rb
belongs_to :user

# modify models/user.rb
has_many :articles

# add in app/controllers/articles_controller.rb#create, after `@article = Article.new(article_params)`:
@article.user = current_user

# add in app/controllers/articles_controller.rb#update, after `@article = Article.find(params[:id])`:
@article.user = current_user

Execute bin/rails server and check that everything works.

Installing Pundit

# More info -> https://github.com/varvet/pundit
bundle add pundit

# replace app/controllers/application_controller.rb content with this
class ApplicationController < ActionController::Base
  include Pundit::Authorization
  after_action :verify_authorized, except: :index, unless: :devise_controller?
  after_action :verify_policy_scoped, only: :index
  
  rescue_from Pundit::NotAuthorizedError, with: :pundishing_user

  private

  def pundishing_user
    flash[:alert] = "You are not authorized to perform this action."
    redirect_to articles_path
  end
end

# run thoses commands
rails g pundit:install
rails g pundit:policy article

# replace app/policies/application_policy.rb content with this
class ApplicationPolicy
  class Scope
    attr_reader :user, :scope

    def initialize(user, scope)
      @user = user
      @scope = scope
    end
  end
end

# replace app/policies/article_policy.rb content with this
class ArticlePolicy < ApplicationPolicy
  attr_reader :user, :article

  def initialize(user, article)
    @user = user
    @article = article
  end

  def index?
    true
  end

  def show?
    true # @article.user_id == @user.id
  end

  def create?
    true
  end

  def new?
    create?
  end

  def update?
    @article.user_id == @user.id
  end

  def edit?
    update?
  end

  def destroy?
    @article.user_id == @user.id
  end

  class Scope < Scope
    def resolve
      scope.where(articles: {user_id: user.id})
    end
  end
end

# inside app/controllers/articles_controller.rb#index, replace '@articles = Article.all' by:
@articles = policy_scope(Article)
# inside app/controllers/articles_controller.rb, for show, new, create, edit, update and destroy methods, append:
authorize @article


# append this in db/seeds.rb
user = User.create! :email => 'first@user.fr', :password => 'secret', :password_confirmation => 'secret'
user = User.create! :email => 'second@user.fr', :password => 'secret', :password_confirmation => 'secret'

10.times do |i|
  Article.create({title: "Post #{i + 1}", body: 'test body', user_id: i < 5 ? 1 : 2})
end

# run this
bin/rails db:reset

Execute bin/rails server and check that everything works.

About

Basic Rails application with IDOR vulnerabilities

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors