Ruby on Rails Telegram Notification. Is it secure? - ruby-on-rails

I'm trying to use tele_notify gem:
Tele Notify
This Gem use Webhook, so I set it with Telegram:
https://api.telegram.org/bot<TOKEN>/setWebHook?url=https://<EXAMPLE.COM>/<TOKEN>
{"ok":true,"result":true,"description":"Webhook was set"}
Then in Application Controller:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
#IMPORTANT! THESE TWO LINES MUST COME AFTER protect_from_forgery!
skip_before_filter :verify_authenticity_token, :only => :webhook
include TeleNotify::Controller
#other code...
end
And finally the routes:
#config/routes.rb
Rails.application.routes.draw do
post '/<your token>' => 'application#webhook'
end
Is this code secure? Anyone experienced with this gem?
It is a problem to skip authenticity token?
skip_before_filter :verify_authenticity_token, :only => :webhook
Thank you very much!

Yes, it's quite secure. Intentionally or not, but you seem to be following official recommendations:
If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. https://www.example.com/<token>. Since nobody else knows your bot‘s token, you can be pretty sure it’s us.
As for skipping authenticity token check, it must be done, because telegram servers have no way of knowing the token. (it is precisely the idea behind the token and the check: remote servers, not knowing the token, can't make requests. But here you want them to be able to hit this certain endpoint).

Related

Devise and Rails: How to secure login URL

I'm trying figure out a way to secure the login URL using Devise. I want to change the default route for users/sign_in. I don't want someone to stumble across the URL and gain access to the app.
Changing the following in the routes.rb file should give me the solution I'm looking for. However, I'm not sure if this is the best path to follow.
devise_scope :user do
get "/login" => "devise/sessions#new"
end
or
as :user do
get "/login" => "devise/sessions#new"
end
Using the following may be easier
devise_for :users, :path => '', :path_names => {:sign_in => 'login', :sign_out => 'logout'}
"But I haven't gotten it to work"
I'm using a simple install of devise without pundit or any other type of authorization. The other thought I had was to implement a role and add the current users to that role. Therefore, blocking access to the app by default. But that would require me to add every new legitimate user to that role as they sign up. Don't really want to do that.
I would question your logic here - using a different url is simply security by obscurity. Even if you had your users login with /gobeligook its pretty trivial for any dedicated attacker to figure that out by sniffing traffic for example.
However you might want to change the path for various reasons - but don't fool yourself that you are adding any real security benefits by doing so.
Also you need to separate the concerns of authentication - which what Devise does and authorization. Authentication is verifying that the users is who he/she claims to be. Authorization is who gets to do what.
If you want to lock down your site to users that are vetted that is a authorization concern and there are a few ways to solve it based on your requirements:
Disable signups
The most basic way to do this would be to disable signups and only allow users to be created by admins. Its relatively secure but really tedious for admins and pretty draconian. In this case your authentication would simply be to lock down everything save for the sign in unless the user is authenticated.
Thats where before_action :authenticate_user! comes in.
Invitation only
You can use something like the DeviseInvitable module to invite users by email and then override the sign up method to require an invitation token.
Walled garden approach
What you may want is users to be able to sign up - but they are really only allowed to access anything when they have been vetted by an admin or a peer.
This is a basic sketch of how something like this could be setup:
class Approval
belongs_to :user,
belongs_to :vetting_user, class_name: 'User'
end
class User
# ...
has_many :approvals, dependent: :destroy
has_many :granted_approvals,
class_name: 'Approval',
source: :vetting_user,
dependent: :destroy
def approved?
approvals.any?
end
end
class ApplicationController
before_action :authenticate_user!
before_action :authorize_user!, unless: :devise_controller?
def authorize_user!
redirect_to user_signup_path, unless current_user.approved?
end
end
For breivity this does not include stuff like the controller where peers or admins vet users - but thats the simple part.
Although I would seriously consider using a purpose built authentication library like Pundit instead of rolling your own.
Adding before_filter :authenticate_user! to controllers should force anyone to go through the login process if the session is not authenticated. That's the whole purpose of using Devise and/or any other gem of that type.
Its all up to you, the ways you listed are correct, please refer the devise-wiki : How-To:-Change-the-default-sign_in-and-sign_out-routes
And if you want user's authentication for most of your application then use
# application controller
before_action :authenticate_user!
# And then you can skip this before_action filter in your other controllers something like this
skip_before_action :require_login, only: [:new, :create]
And these are two things don't be confused in this.
authentication
Devise is a flexible authentication solution for Rails based on Warden
authorization
Pundit provides a set of helpers which guide you in leveraging regular
Ruby classes and object oriented design patterns to build a simple,
robust and scaleable authorization system.
Devise will help you in user authentication and if you want to authorization users based on some criteria then you should use Pundit

Get rid of X-CSRF-Token in Rails

I need to design a RESTful API for Rails, which will enable login from web browser, smart phone, tablet, etc. When I do login it always require X-CSRF-Token, so everytime I need to use session or cookie info. However the REST api should be stateless, which means shouldn't use cookies. Is there a way to get rid of that? Any suggestion for that?
Here's how I dealt with this in an app that responds with both HTML and JSON. I want the CSRF check except if it's an API call from a trusted source, so
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
# has to come after the protect_from_forgery line
skip_before_filter :verify_authenticity_token, :if => :api_request?
# but don't just accept any URL with a .json extension, you need something else to
# ensure your caller is trusted (in this case I test for a valid API key being passed)
before_filter :api_key_valid?, :if => :api_request?
def api_request?
request.format == 'application/json'
end
# ... etc
end

Rails / Devise Session Destroyed When Redirected From Paypal

Using Paypal payment standard. When the user is redirect the back to the app after paying on paypal.com, the logged in user becomes signed out. Any help is appreciated.
If you post to a rails app without providing the correct CSRF parameters, your session gets deleted. This sounds like what is happening. One way to solve this is to disable the CSRF meta protection for the paypal post action
In Rails3 you can disable the csrf token in your controller for particular methods:
In your controller:
1. protect_from_forgery :except => :create
or
2. skip_before_filter :verify_authenticity_token
or
to disable it for everything except a few methods:
3. skip_before_filter :verify_authenticity_token, :except => [:update, :create]

How do I secure this post request to Rails?

I'm trying to work out security for my AJAX calls. I've got a jQuery post call which deletes a note. From what I've read, it seems that I need to use protect_from_forgery to ensure that the post is coming from a valid user.
This is what I have so far
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
...
index.html
$.post('../delete_note',{id:$('#note_id').val()}, function(data) {
});
note_controller.rb
def delete_note
y params
render :text => "success"
end
At the moment, the post request gets run by Rails, even though I'm not sending any security token with it. What do I need to do secure the call?
I'm using Rails 3.0.1 and devise for user management.
You probably want to ensure the user is signed in, using in your note controller something like:
before_filter :authenticate_member!, :except => [:index]
Additionally, check if the user has the rights to delete the note, for that you want to use a authorization solution like cancan.

Receive POST from External Form

I have a form on another website (using a different backend) that I want to be able to POST to my Rails application (on a different domain).
How do I generate a valid authenticity token for the external form so that my Rails app will accept it?
Assuming I can do the answer to the above question--is there anything else special I need to do to make this work? Apart from the authenticity token, the rest of it seems pretty straightforward to me...
Thanks for the help!
You can't generate an autenticity token from outside your Rails app.
What you can do, is to disable the token protection only for this action and use a custom implementation based on a before_filter.
skip_before_filter :verify_authenticity_token, :only => :my_action
before_filter :verify_custom_authenticity_token, :only => :my_action
def verify_custom_authenticity_token
# checks whether the request comes from a trusted source
end
You could just remove the check by adding a filter like:
skip_before_filter :verify_authenticity_token, :only => :action_name

Resources