Filtering route parameters - ruby-on-rails

So I have a route in routes.rb like:
get "example/:token" => "example#example"
and even though I have config.filter_parameters += [:token] in production.rb, I still get a log output like:
Started GET "/example/fjiaowevnieninr3"
Parameters: {"token"=>"[FILTERED]"}
where as you can see "token" is filtered in the Parameters, but still appears in the URL path. I'd always assumed filtered_parameters would filter it there as well but I guess not. Is there an official way to filter out named route parameters like this from the logs?

In general it depends on why do you need this kind of behaviour. allenbrkn answer is correct you can send it through query string and it will be filtered from the query string. That said there are more things to consider when you are trying to do this.
There are more type of logs
In the production environment you use some webserver like Apache or Nginx, they have their own access logs, in which they log some headers, paths with query strings and so on (it is configurable).
It means that even if you filter out URL token from the rails log, they will probably appear in the webserver access logs.
Also don't forget these parameters can be sent to external services eg. exception tracking or performance tracking softwares.
Things in the URL are public
Tokens in the URL should not be considered as a secret. Your user can see them manipulate them, send the url to anyone or randomly show it to someone.
I think there two main reasons to put the token into the URL
Hard to guess URL
User authorisation
Hard to guess URL
In this case token is always the same. It is not changed with every access of the URL. It can be usually used as an ID of some resource or something.
For example we are using it as public URLs for invoices so we can send just a link to our clients and they can download the PDF from our site and there are some things to help them with the payments. In the URL is some token so they cannot guess the URL and access invoices of other clients. The token is always the same, so they can access the invoice from the email several times. And the URL is still in the rails log and server access log and we are fine with it because we know the tokens anyway - they are part of the invoice ID.
In these cases it also helps you with the debugging. If some exception kicks in or if there is some issue with the resource it will be really hard to find out why.
User authorisation
This is a bit more complicated. When it comes to authorisation you shouldn't put your tokens into the URL. They should always be in the body and filtered out of the log or in the authorisation headers. Unfortunately sometimes you don't have much of a choice if you need to use it during the GET requests eg:
Single sign on (redirect flow)
Password reset
etc
(Of course you can use request body even with GET requests but you are risking to loose the data eg if the user puts in the URL manually etc)
In these cases you should make possible exposition to valid tokens as short as you can:
Always issue new token on demand never reuse same tokens
Work with very short expiration times
Every token should be invalidated immediately after its use
With these rules it should not matter whether they appear in the logs or not.
For example we have single sign on implemented in our application. The valid token is issued only on demand with 1 minute expiration time and it is invalidated immediately when it is used. In this case it can appear in the log because at the time of appearance it is already invalid and useless.
With password reset it can be a bit more complicated you need token to be valid at least tens of minutes probably and it will appear in the log before it is invalidated. But there are probably not too many things you can do about it - btw if someone has some good ideas I will be very happy to read it.
Conclusion
You can filter the tokens from rails logs but they still probably appear in other logs or even other services if you use them. You should work with your tokens in the URL as if they can appear there and make it as safe as possible for you if they do. Rails log is just one piece of puzzle you have to consider.

As far I know, there is no in-built way of filtering the URL like GET /example/[FILTERED]. But you can create your own method to do that.
After a little examining of https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/filter_parameters.rb file, I found that filtering params will also filter the query_string of the path.
For example: In your case above, if you make a request to /example/fjiaowevnieninr3?token=fjiaowevnieninr3, the result in logs would be:
Started GET "/example/fjiaowevnieninr3?token=[FILTERED]"
Parameters: {"token"=>"[FILTERED]"}
So I would suggest you to send your token as a query parameter and do something like this:
In your routes add:
get "example/auth" => "example#example"
Then you can make a request like /example/auth?token=123456
You can catch this token in controller with params[:token]
This way your logs will show:
Started GET "/example/auth?token=[FILTERED]"
Parameters: {"token"=>"[FILTERED]"}
Also, config.filter_parameters is moved from config/application.rb to it's own file at config/initializers/filter_parameter_logging.rb file.
Add the token symbol to your config/initializers/filter_parameter_logging.rb:
Rails.application.config.filter_parameters += [:password, :token]
Also, don't forget to restart the server.

Related

Forging a Cross Site Request Forgery (CSRF) token

I had a look at Rails' ActionController::RequestForgeryProtection module and couldn't find anything related to using secrets. Basically, it uses secure PRNG as a one time pad, xors, computes Base64 and embeds into HTML (form, tags). I agree that it is impossible for an attacker to guess what a PRNG generates, but nevertheless I can generate (or forge if you like) a similar token, embed it into my "evil" form and submit. As far as understand Rails compares ( verifies) it on the backend. But I can't fully understand why it is secure. After all, I can generate my own token exactly like Rails does. Could someone clarify how the security is achieved?
You might misunderstand what this protects against, so let's first clarify what CSRF is, and what it is not. Sorry if this is not the point of confusion, might still be helpful for others, and we will get to the point afterwards.
Let's say you have an application that allows you to say transfer money with a POST request (do something that "changes state"), and uses cookie-based sessions. (Note that this is not the only case csrf might be possible, but by far the most common.) This application receives the request and performs the action. As an attacker, I can set up another application on a different domain, and get a user to visit my rogue application. It does not even have to look similar to the real one, it can be completely different, just having a user visit my rogue domain is enough. I as the attacker can then send a post to the victim application's domain, to the exact url with all the necessary parameters so that money gets transferred (the action will be performed). The victim user need not even know if this happens in xhr from javascript - or I can just properly post a form, the user gets redirected, but the harm is done.
This is affected by a few things, but the point is that cross-origin requests are not prevented by the same origin policy, only the response will not be available to the other domain - but in this case when server state changes in the victim application (like money gets transferred), the attacker might not care much about the response itself. All this needs is that the victim user that visits the attacker's page while still being logged in to the victim application. Cookies will be sent with the request regardless of the page the request is sent from, the only thing that counts is the destination domain (well, unless samesite is set for the cookie, but that's a different story).
Ok, so how does Rails (and similar synchronizer token solutions) prevent this? If you lok at lines 318 and 322 in the source, the token received from the user is compared to the one already stored in the session. When a user logs in, a random token is generated and stored for that particular user, for that particular session. Subsequent requests that change state (everything apart from GET) check if the token received from the client is the same that's stored in the session. If you (or an attcker) generate and send a new one, that will be different and the request will fail validation. An attacker on their own website cannot guess the correct token to send, so the attack above becomes impossible. An attacker on a different origin also cannot read the token of a user, because that is prevented by the same origin policy (the attacker can send a GET request to the victim app, but cannot read the response).
So to clarify, CSRF is not a protection against parameter tampering, which might have caused your confusion. In your own requests, if you know the token, you can change the request in any way, send any parameter, the CSRF token does not protect against this. It is against the attack outlined above.
Note that the description above is only scratching the surface, there is a lot of depth to CSRF protection, and Rails too does a little more, with some other frameworks doing a lot more to protect against less likely attacks.

OAuth 2.0 State Parameter

I am working with the eBay API using OAuth on my current Meteor project app.
There is a section of the app where I can create an eBay account profile, and assign custom values to the account (such as nick-naming it, etc.). This is where I initiate the OAuth sign-in redirect process.
My question is about the 'state' parameter in the token requests. I understand that it is for helping prevent CSRF, but do I HAVE to use it that way? 'state' does seem to be optional after all.
Let's say I wanted to pass another value into the request call such as the string 'eBay Seller', and expect that the same exact string be returned in the response. I want to use that value to help my app determine which account to assign the returned tokens to (based on which account profile initiated the redirect link).
Is 'state' a valid place to pass in a variable that I expect to be returned exactly as sent? I considered using Session variables to handle this scenario, but quickly realized that this would not work, since the OAuth process takes me outside of my project's domain.
Does OAuth support passing variables that are expected to be returned as sent? Is sending my variable as 'state' allowed or even recommended (or absolutely not recommended?) Is there a better way to achieve what I want to do that does not involve updating database values?
Thank you!
You can send what you want as state. You should try to make sure it's not guessable though, to mitigate against CSRF attacks.
If you want to return useful information like 'ebay seller' then include something for CSRF (e.g. hash of the session key id) and the text 'ebay seller' and delimit them e.g.
2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824|ebay seller
Now you have the best of both worlds: useful state info + CSRF protection.
Your redirect endpoint logic can check the hash of the session id matches and also confirm the account type from the initial request.

Box API OAuth2: multiple redirect_uris, long lasting refresh token

I have two questions about Box's Oauth2 API in a testing environment.
Is it possible to have multiple redirect_URI addresses? I'd like to use one address for production (e.g., https://my_site.com/box_redirects_here), one for ongoing development (http://localhost:8000/box_redirects_here) and one for automatic UI tests (http://localhost:8001/box_redirects_here). As far as I could see, the only way to do that would be to create three different Box applications - is there an easier way? BTW, both Dropbox and Google Drive do support multiple redirect URIs.
I have a set of automatic tests that I'd like to run a few times a day. The challenge I'm facing is that every time I run these tests, my refresh_token is invalidated, and I can't use it again - which means I can't run the same set of tests a few hours later without manually getting a new token. One solution would be to save the refresh token, for example in a file, so I could reuse it across testing sessions. But:
It's really cumbersome.
if different developers are running these tests from different machines with no common file system that doesn't really work.
Again, for whatever reason this doesn't seem to be an issue with Google Drive or with Dropbox.
This is not currently possible, and I agree that would be nice.
Your best option is to save the access/refresh token pair to a file or a database (in the event that there's no common filesystem.) The OAuth2 spec grants implementers wide latitude on how they issue refresh tokens, if they issue them at all (I don't think Dropbox does.) While Box's implementation makes integration testing a bit challenging, I think that it ultimately hews most closely to the spec's recommendations.
For your first question, you might be able to get close to what you want by using the redirect_uri query parameter. Although you won't be able to supply an arbitrary redirect URI, you can give one that has the same base URL as the redirect URI in your app console.
From the OAuth tutorial:
Wildcard redirect_uri values are also accepted in the request as long as the base url matches the URI registered in the application console. A registered redirect_uri of https://www.myboxapp.com can be dynamically redirected to https://www.myboxapp.com/user1234 if passed into the request redirect_uri parameter.
For your second question, John is right - Box invalidates a refresh token after it has been used. Although this can be annoying, it's also more secure.

How do I prevent double-clicking in ASP.NET MVC without using JavaScript?

Yes others have asked similar questions, however, the end solution was using JavaScript. I have that working, my question becomes what happens when the user has JavaScript turned off? I would hope only advanced users would be turning off JavaScript and thus know to know click once on a button and can tell that the server is working. On the off chance they don't, how do I make sure that button is only clicked once?
I should note that this is on a payment form.
I'm afraid without JavaScript there is no way to prevent this. If the click results in a POST request, then you can try to make it idempotent on the server side.
You cannot make sure the button is only clicked once, as you have no control over user's browser. What you can do, though, is to add a hidden field, a token to your forms so that if you see a token you've already seen, you'll be able to return an already-calculated answer.
Update: In case of payment processing, it's not even a technique for preventing double submission—it's a technique protecting your clients from fraud. Check out OWASP's A5: Cross-Site Request Forgery (CSRF):
Preventing CSRF requires the inclusion of a unpredictable token in the body or URL of each HTTP request. Such tokens should at a minimum be unique per user session, but can also be unique per request.
The preferred option is to include the unique token in a hidden field. This causes the value to be sent in the body of the HTTP request, avoiding its inclusion in the URL, which is subject to exposure.
The unique token can also be included in the URL itself, or a URL parameter. However, such placement runs the risk that the URL will be exposed to an attacker, thus compromising the secret token.
Basically, each time you receive a payment form, you want to make sure it's a legitimate response to the form you've shown. Handling double submission comes free with security—a rare case indeed! ;)
what happens when the user has JavaScript turned off?
The server is hit twice and there is not much you could do about it.
Now depending on what you are doing on the server there are different ways to react. For example in a RESTful application if you are using a POST verb which modifies some state on the server and is neither safe nor idempotent it is eventually the underlying data source that will detect the anomaly and simply throw an exception which will be gracefully reported to the user telling him that his request was already submitted.
For a simple and small ASP.NET MVC app, I find using the HttpRuntime.Cache is enough:
Function SomeAction() As ActionResult
Dim cachekey = "uniquecode"
If HttpRuntime.Cache(cachekey) IsNot Nothing Then
' wait until the other request is finished
Do
Threading.Thread.Sleep(1000)
Loop Until HttpRuntime.Cache(cachekey) Is Nothing
End If
HttpRuntime.Cache.Add(
cachekey, "", Nothing,
DateTime.Now.AddMinutes(5),
Cache.NoSlidingExpiration, CacheItemPriority.Low, Nothing)
Try
' do stuff
Finally
HttpRuntime.Cache.Remove(cachekey)
End Try
End Function
The cachekey must be the same for requests that you consider double, and must be different from all the other requests. If you check that the cachekey is already in the cache, tell the thread that is processing the request to wait (or throw error).

Good Method To Prevent Session Hijacking?

Scenario:
Upon starting a session on my site, I generate a rand token that is shown to the user that once. Say they “store” it away for later use.
I then, INSERT the md5(token) into SQL with timestamp.
When the user visits other pages like login, they would have to pass the token via URL as part of the validation process. I would check to see if the token exist and maybe UPDATE userid to this token.
So. Even if someone steals a user’s PHPSESSID cookie, wouldn’t it do ANY good to the hacker since they can’t access any of these pages without knowing the token?
You are right that they won't be able to access the pages without the token, but as an added point, sometimes I'd like to use IP tracking or browser tracking used concurrently as well.
The rationale being that even if someone gets a PHPSESSID cookie and the token, he would have to be coming from the same IP source as well as use the same browser. Then again these are just means of security by obscurity.
I recommend if you are really concerned about security, you can try looking at using a HTTPS connection. Hope it helped. Cheers!

Resources