Recently I've created a controller, which will do some insert into my database whenever I access the url.
My route config for it :
routes.MapRoute(
"SCRoute",
"SC/{pdate}",
new
{
controller = "SC",
action = "Index",
pdate = DateTime.Today.Date.ToString("yyyyMMdd")
});
My question is if I don't expose this url, will it be detected? Of course since there is no credential log-in, anyone with the link can access the page, but if I can keep it for my self, will it be safe?
There will be no internal links to this controller also. It won't be mentioned anywhere except in my mind and the route config!
but if I can keep it for my self
Its not safe, no matter how you hide it. You can accidently execute this URL or other developer can do that while understanding the code. Lots of things can take place to get it run.
If you want to insert something in DB, why don't you create a script and execute it. It would be a controlled change in any environment.
UPDATE - After comment from user
You can work out a solution on following lines:
Define an action filter.
Check for a unique value in the URL e.g. IP Address (Example) or write an algorithm to generate a hash.
When you want to run the action from anywhere, generate the hash and supply in the URL. The defined filter will verify the hash validity. A request with valid hash will only be served.
Hope this help.
Related
Generally the url from my report page looks like this:
http://test-account.peter:3000/offices/7/reports/index
However, sometimes it looks like this:
http://test-account.peter:3000/offices/7-peters-office/reports/index
Why does this happen?
It was not really a problem until we changed the controller action from a GET to a POST and renamed it. We had to do this so we could pack more parameters in to the ajax request. Users still have this section of the site bookmarked and it throws errors all day long.
I have tried to redirect the route:
get '/offices/*all/reports/index' => 'offices#show'
get '/offices/:office_id/reports/index' => 'offices#show'
get '/offices/:office_name/reports/index' => 'offices#show'
Is there a way to catch the name? Or do I have to prevent the name from being added to the url in the first place?
In the controller, you would be able to parse the parameter to get just the first character and check if its an integer. However, it would be much better to debug how the parameter is getting assigned to different values and ensure only the id is used. If you're linking to that route in a view, check what is being passed in the link and confirm the value is what you expect it to be.
Rails does routing it does not look in your database for matched data. So without looking at data, your three routes are exactly the same, the variable (office_id & office_name) is just named different. If you get a request on example /offices/:office_name/reports/index, rails will just match the first one since both routes match the request.
You need something in the path that indicates its a name or id. If you will really never have a name and id with the same search, then you could just have one route and try to match a id or name from the DB in the controller.
I am using Ruby on Rails 4.1.1 and I am thinking to accept parameters (through URL query strings) that are passed directly to the url_for method, this way:
# URL in the browser
http://www.myapp.com?redirect_to[controller]=users&redirect_to[action]=show&redirect_to[id]=1
# Controller
...
redirect_to url_for(params[:redirect_to].merge(:only_path => true))
Adopting the above approach users can be redirected after performing an action. However, I think people can enter arbitraryparams that can lead to security issues...
Is it safe to accept URL parameters for populating the url_for method? What are pitfalls? What can happen in the worst case?
By logging params during requests to my application I noted Rails adds always :controller and action parameters. Maybe that confirms url_for can be used the above way since it is protected internally and works as-like Rails is intended to.
This it is safe internally as Ruby On Rails will only be issuing a HTTP redirect response.
As you are using only_path this will protect you from an Open redirect vulnerability. This is where an email is sent by an attacker containing a link in the following format (say your site is example.com).
https://example.com?foo=bar&bar=foo&redirect=http://evil.com
As the user checks the URL and sees it is on the example.com domain they beleive it is safe so click the link. However, if there's an open redirect then the user ends up on evil.com which could ask for their example.com password without the user noticing.
Redirecting to a relative path only on your site fixes any vulnerability.
In your case you are giving users control of your controller, action and parameters. As long as your GET methods are safe (i.e. no side-effects), an attacker could not use this by creating a crafted link that the user opens.
In summary, from the information provided I don't see any risk from phishing URLs to your application.
Rails redirect_to sets the HTTP status code to 302 Found which tells the browser to GET the new path as you defined it by url_for. GET is a considered a safe method in contrast to
... methods such as POST, PUT, DELETE and PATCH [which] are intended for
actions that may cause side effects either on the server, or external
side effects ...
The only problem would have been if someone could gain access to methods such as create and destroy. Since these methods use HTTP methods other than GET (respectively POST and DELETE) it should be no problem.
Another danger here is if you go beyond CRUD methods of REST and have a custom method which responses to GET and changes the database state:
routes.rb
resources something do
member do
get :my_action
end
end
SomethingController
def my_action
# delte some records
end
For future ref:
Rails has a number of security measurements which may also interest you.
It's not exactly an answer, just wanted to point out that you shouldn't use something like
url_for(params)
because one could pass host and port as params and thus the url could lead to another site and it can get worse if it gets cached or something.
Don't know if it threatens anything, but hey, it's worth pointing out
I need to pass some parameters to callback action. Judging from the source code, OmniAuth should add query string to callback URL but strangely it does not. When I open
/auth/facebook?from=partner
...and get redirected to Facebook, return_url is just
/auth/facebook/callback
...without any parameters.
After struggling with all the above answers, I figured out what to do regarding Facebook, which by default does not display the params in request.env["omniauth.auth"].
So -- If you are using a query string for the callback, similar to something like this:
"/auth/facebook?website_id=#{#website.id}"
The only way to get that website_id param is by using request.env["omniauth.params"]. NOTE: MAKE SURE YOU USE omniauth.params and not omniauth.auth -- this one tripped me up for a while.
Then, to test this out, you can inspect it within your controller action (notice the RAISE line...):
def create
raise request.env["omniauth.params"].to_yaml
# the rest of your create action code...
end
You should see your parameter there. Great. Now, go back to your controller and remove that RAISE line. Then, you can access the param as follows in your controller action:
params = request.env["omniauth.params"]
website_id = params["website_id"]
NOTE: in params["website_id"] you need to use quotes and NOT a symbol.
I guess the cookie thing works but why do all that when you can use the state variable as documented here: https://github.com/mkdynamic/omniauth-facebook
This is how I used it:
when creating the url you can just add state in the Query String and it will be available in the callback url as well.
user_omniauth_authorize_path(:facebook, :display => 'page', :state=>'123') %>
now the callback url will be
http://localhost:3000/users/auth/facebook/callback?state=123&code=ReallyLongCode#_=_
Now in the callback handler you can process the state
You can use the :params options, as in
omniauth_authorize_path(:user, :facebook, var: 'value', var2: 'value2' )
and later in the callback you can access request.env['omniauth.params'] to get the hash! :)
(copied from this answer)
What you want to do is dynamically set your callback to include the partner name in the url (not the url parameters), on a per authentication transaction basis, depending on which partner was involved. This means setting the callback url dynamically, for each authentication request. See this blog post to get started. The callback url automatically drops the url parameters, as you've noticed, so doing this with parameters won't work.
So, if instead of trying to pass the partner name/id in as a parameter (which is dropped), you structured your routes so that the partner_id and OmniAuth provider were part of the callback url, then you'd have something like:
/auth/:omniauth_provider/callback/:partner_id
...where a valid callback would be something like
/auth/facebook/callback/123456
...then you would know that a given callback came in from facebook, with partner id 123456
OmniAuth already has a built-in way to know where the user was, it's called "origin" as documented here:
https://github.com/intridea/omniauth/wiki/Saving-User-Location
You know, I think I might be trying to solve this the hard way.
Cookies might be the answer. I think you can solve this by having your login action store a cookie, and then redirecting to the proper /auth/:provider path for authentication, and when the callback is triggered (in SessionsController#create), you just read the cookie back to know where to redirect them to.
So, right now, your "login with facebook" link (or whatever you have you in your app) probably goes to /auth/facebook. Instead if you created a custom action like
POST /partner_auth
...and called it with the url...
POST example.com/partner_auth?from=partner&provider=facebook
Then you might have a controller like:
class PartnerAuth < ApplicationController
def create
cookies[:from] = params[:from] # creates a cookie storing the "from" value
redirect_to "auth/#{params[:provider]"
end
end
Then in the SessionsController#create action, you would have...
def create
...
destination = cookies[:from]
cookies[:from].delete
redirect_to destination # or whatever the appropriate thing is for your
# app to do with the "from" information
end
I tried to build a demo app to accomplish what I'd outlined in the other answer, but you're right - it was too complicated to try to dynamically inject a custom callback into the OmniAuth code. There is a configuration option to override the default callback, but it doesn't appear to be easy to set it dynamically.
So, it dawned on me that cookies would be way simpler, user-specific, and since you theoretically only need to store this from information for a very short time (between when the user tries to authenticate, and when the callback is triggered), it's no big deal to create a cookie, and then delete it when the callback gets hit.
Use the 'state' Variable. Facebook allows the user to set a STATE variable.
Here is how I did it, I appended the AUTH URL with ?state=providername
http://localhost/users/auth/facebook?state=providername
This param is returned to me at Callback as params['providername']
I devised the solution from the original Omniauth Path Method
user_omniauth_authorize_path(:facebook, :display => 'page', :state=>'123') %>
I've created a task management app that consists of lists and tasks. Users can only view their own lists and tasks. I would like to add the ability for a user to share a list if they like. Here are the steps I would like to accomplish:
User clicks a link from /list/show to share the list
User receives a secret URL to share: myapp.com/lists/1/23534512345234523 or whatever.
Secret URL redirects to a view other than /lists/show. Something like /lists/1/23534512345234523 which would be routed to /lists/secret_show or whatev.
Only users who have that url can see the information on that page.
Hope that is making sense. I imagine I would have to update the list record with a unique token to list.token. Then I would some how have to recieve the incoming URL and through a new action
lists#secret_share
def secret_share
...
end
Where I filtered for the list record by list.token and routed to secret_share. Then perhaps in the view I could simply restrict the view by the presence of the token in the URL.
Thoughts?
Whatever "secret URL" you hand out should not redirect to the real URL or you're going to create all kinds of opportunities for information leakage. It should be a strictly alternate URL.
Using routing for this seems like a good idea instead of using a separate controller. In your route you might want to pass an additional parameter to indicate this is a "secret" URL, like :secret => true where the value in question is something that cannot be submitted by the user to fake things out. User parameters are always strings, for instance, so using true should be a safe alternative.
This special parameter might disable access checking on your controller so that the page can be viewed by people that don't normally have access. You could also show a different layout using the layout method in your controller.
I am new to RoR and started working on a typical 'has_many' association (ie. a user has many friends). I have everything working correctly, but I don't like having the ids exposed in the url. I find that I need to add extra validation in my controller to make sure the ids represent valid associations in case the user manually entered different ids.
Personally I would like to see the ids out of the url and passed via some other means but that is not always possible. Shallow nesting of resources will help reduce the number of ids I need to validate at least.
What is the RoR philosophy on this? I have not seen anything specific to this issue.
Thanks
the URL has parameters if it is a GET url.
Try using POST parameters, which means your url will no longer be cluttered. Note that a malicious user can still send a made-up POST request using curl.
My approach to this is implementing proper authorization. If the user requests information for an object he is not permitted to read, this should be handled by an authorization framework.
With CanCan or Declarative Authorization you can define rules that replace your "manual" (and error-prone) checks in controllers.
I like the IDs being in the URL. That is what REST is about. Getting information for specific Resources, which have to be identified with an ID.
You can use Friendly ID in order to replace the integer ID by a slug (e.g. users/tollbooth instead of users/42).
basically ror routes by default takes id as key to generate urls. If you are not fan of id based urls then you can always override urls by using to_param inside model.
def to_param
# make sure this field is always present & unique
username
end
then by default you will start seeing username instead of id inside urls
How to find object inside controller actions
User.find_by_username(params[:id])
If you dont want to do this manually make use of slug gems like friendly id