Can't get the user session to be reset when CSRF token authenticity is not verified - csrf

After a couple of weeks playing with rails app and completing the rails tutorial, I wanted to learn about some common web attack in web applications.
I managed to perform a CSRF attack type using the below chunk of code in a .html file.
<form action="http://localhost:3000/users/2" method="post">
<input type="hidden" name="_method" value="delete">
<div>
<input type="submit" value="Delete">
</div>
</form>
Being logged in as an admin, I run the attack against my own code that was based on same session mechanism as Railtutorial and I succeed deleting the user where it should have been stopped as the authenticity token was missing.
The default behavior should be a session reset thus preventing the user being deleted outside of the web application.
I can see the 'Can't verify CSRF token authenticity' in the log, but the session is not reset.
Overriding the handle_unverified_request method, that should by default reset the session, with
def handle_unverified_request
raise(ActionController::InvalidAuthenticityToken)
end
the error get raised properly.
Running the attack on Railstutorial git code from railstutorial/sample_app_2nd_ed freshly install, I faced the exact same issue: the session is not reset as it should be. That mean the tutorial app is vulnerable to that kind of attack.
I dig a bit deeper into the code (http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-reset_session) but cannot figure out why in the context Railstutorial it doesn't seem to work.
Could anyone verify, If the tutiral is indeed vulnerable to CSRF attck? if yes would the solution be to rewrite the handle_unverified_request to do proper signout in that case? Finally why is it not working as it should be?
Thanks for your help.

I figure out how to force the signout of the user if the CSRF token can't be verify.
It seems related to the fact that we empty the session but not the remember token of the cookie.
Override handle_unverified_request in your application (so that all controller benefit from it) and add the sign_out method to clean the remember token.
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
def handle_unverified_request
sign_out
super
end
end

Related

Gorilla CSRF - Forbidden - CSRF token invalid - fails when there are two forms

I'm adding in CSRF token validation and I'm running into a problem where I have two forms on a page and one of them will submit successfully and the other will not. I'm not doing AJAX requests, I'm simply using hidden input fields. If I submit the form when it is the only one on the page, it submits without issue. If I submit it on a page with more than one form, it fails.
Below is the template code for my two forms
{{if .IsAuthenticated}}
<form action='/admin/logout' method='POST'>
<button>Logout</button>
{{.CsrfField}}
</form>
{{end}}
<form action='/admin/stuff/create' method='POST'>
{{with .Form}}
<div>
<label>Title:</label>
<input type='text' name='title' value='{{.Get "title"}}'>
</div>
<div>
<input type='submit' value='Publish stuff'>
</div>
{{end}}
{{.CsrfField}}
</form>
And this is what the generated HTML looks like. Both appear to be valid.
When I click the "Logout" button though, I get the Forbidden - CSRF token invalid error, but clicking the create input value in the second form always works.
The logout button is correctly validated when I attempt to use it on the home page which is "/admin/" but it does not work on any of the other pages "/admin/snippet/:id" or "/admin/snippet/create". The Logout button is part of a base template, so it appears on every page, so there shouldn't be anything different in how it appears on any page.
I've read other SO posts about multiple forms & CSRF tokens on a page and I understand there should be no issue with multiple forms with the same information as long as you have each one in it's own form, it should be fine. So I am not sure where I am going wrong.
I found the issue. Currently the way that gorilla/csrf works, it does not like creating the masked token from one path and then sending that token off to another path. So in my situation, going from /admin/snippet/create to /admin/logout threw an error because it was expecting the path for the token to be /admin/snippet/<something> and so it threw an error.
This issue has been addressed in this PR: https://github.com/gorilla/csrf/pull/147 and essentially the solution is to set the default path yourself to something which all of your routes will contain, so in my case that was /admin
This is what my CSRF declaration looks like now in main.go
var csrfMiddleWare = csrf.Protect(
[]byte("<put your 32 character key here>"),
csrf.Path("/admin"),
csrf.Secure(false),
)
A note, if you had this issue and then apply this fix and it doesn't resolve the problem, trying testing in a separate browser as there may be some caching issues.

yii2 CSRF not working properly

I copied create form code from view source when user use to logged in and then I created html file from copied code then I logged out and then logged in again. when i submited html file then system accept it witout any csrf checking. not sure where issue is. csrf is enabled also _csrf code also avaiable in view source too
<input type="hidden" name="_csrf" value="ttPy-NP-8FUCQxKczEWgkl66JQfb3JfJHwUOSsi9wjTxkp_LgKqxFFYBQu2iL8T2LIxpQ7Xuzo0ucEYfjPSUBg==">
It resolved reason being it happend becuase csrf validation via cookie so it not clear cookie properly when user logged out. so easy solution for this is do changes in main.php
'request' => [
'enableCsrfCookie' => false,
],

Performing actions with objects in REST (subscribe / mark as read / etc.)

I am reading over Creating an efficient REST API and I understand and agree with a lot of what the document has to say.
I'm currently implementing a Twitter clone and each tweet object in the database has a list of readers (user IDs).
According to the document, it seems like the request for this would be something like:
PATCH /tweet
{read: true}
However read is not an actual property of the tweet. Instead this would actually update the list of readers on the tweet with the currently authenticated user. Is this correct?
This also means that the user could potentially perform other operations on the tweet using this same API route, but users cannot actually update/PATCH tweets in any way other than to mark them as read or not.
Additionally, only an authenticated user should be able to do this and it should only update the list of readers for them. The document says that you should not handle state in your API which means no sessions -- however authentication is obviously necessary. It seems to say that you should send the authentication/authorization token each time, but I don't understand how this is substantially different from a session cookie in some cases. Is the implication that the request should actually be:
PATCH /tweet
<Authentication-Header>
{userId: userId, read: true}
i.e. should the API itself not try to use information from the session and force requests to provide logged-in user information? Is there a better way to structure this?
Depends on what level of authentication is necessary.
Keep it simple. No need to complicate things.
Do you really need a "state"? You have a database where you have to check the user authentication. Why not use the database record?
If the API will be used by the client on their web server, then you just have to check the IP address of the request.
The IP address cannot be spoofed and therefore is a very good authentication factor. Add username and you have multi-factor authentication
Instruction for client using PHP
$user = 'joe';
$param = 1;
file_get_contents("http://apisite.com/api/function/?id=$user&param=$param");
An HTML form, maybe you'd have to restrict access but if they name name the page something not easy to guess, then you have it.:
<form action="http://apisite.com/api/function/">
<input type="textarea" name="tweet"/>
<input type="hidden" name="user" value="joe" />
<input type="hidden" name="param" value="1" />
</form>
If the API were to be done in PHP:
PHP
<?php
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: max-age=0');
$ip = $_SERVER['REMOTE_ADDR'];
$results = mysqli_query("SELECT COUNT(*),`user`,`state` FROM `Profile` WHERE `ip` LIKE '$ip' ");
$row = mysql_fetch_array($results, MYSQL_NUM);
if ($row[0] == 1 && $row[1]==$_GET['user']) && $row[2]==1){
$tweet = trim($_GET['tweet']);
$param = $_GET['param'];
include('tweet.php');
echo "Sent\n";
}
else{
echo "Not Sent\n";
}
?>
The problem with using cookies is that you are moving state outside of the URI and into the cookie.
RESTful APIs should have all the state necessary in the URI. Believe it or not but this does make things a lot easier as you don't have to mess around with handling cookies if you are creating a new client library.
Not using cookies also makes for really nice integration with other devices. Say I am viewing a RESTful webpage on my browser, and then I want to show someone something on my account. I can email them the URI /some-uri?authToken=1234 and they will be able to view my account without me giving them my password!!
Once the token expires, they will no longer be able to access the account.
EDIT
If you put the token in the POST body, a user won't be able to visit a page by typing in the address bar. They will keep being told they are not authorised. So yes, you need it in the URI. You can use a cookie if you really want, but except that it is a workaround.
You'd get the login token by exposing a login resource. To login you would send your username and password to:
POST /login
This would create a /login/{token} resource. You can use this token (or even the full URI if you want) to authenticate with the server.
When you want to log out, you call
`DELETE /login/{token}`
If you try to use the token now, the API should return with 'not authenticated' as the token no longer exists.

mule facebook - flow variable

I have a mule flow with facebook connector(authorize); Before calling it I try to set some flow and session variables; When facebook authorize returns back to flow - these previously set variables do not present in flow and session anymore.
Why is it so and how can I make my variables stay in message after facebook authorize?
This is a known issue: http://www.mulesoft.org/jira/browse/MULE-6847
Also the fact that http://www.mulesoft.org/jira/browse/CLDCONNECT-185 has been closed Unresolved with a Usage Issue status makes me wonder if doing anything but crafting an HTTP response in the flow that contains <facebook:authorize /> is actually discouraged.
You may want to upvote/watch/comment/re-open these issues if you think you have a strong case for it.

Post form, signed_request and session in Facebook Canvas

I'm developing a Facebook app that will run in canvas. This is the scenario (I think it's very common). This is all server-side implementation.
Being:
APP_URL: https://apps.facebook.com/xxxxxx/
CANVAS_URL: https://myexample.com/facebookApp/
Step 1 (index of the app) has a form. It has action="CANVAS_URL/step2" (note that is not the app Url). In order to use signed_request in the next step, it has an hidden field <input type="hidden" name="signed_request" value="<?php echo $_POST['signed_request'] ?>" />
Step 2: it receives the info of the form and stores it in a Session, then parses the signed_request. This works OK. I store it in the Session because I want to save it to a database after the user is authenticated. If user was logged on to the app, I redirect him to APP_URL/step3; if not, I redirect him to login dialog, with &redirect_uri=APP_URL/step3 . In both cases, note that the step 3 is APP_URL/step3 (as I need signed_request again to check if user has authenticated and another data). All redirections are made with JavaScript: <script type="text/javascript">window.top.location = "URL";</script>
Step 3: now I want to save the data previously stored in the Session. BUT as the user is navigating through FB canvas, the session data is not available.
I tried several combinations. If the form is sent to APP_URL/step2 (instead of CANVAS_URL/step2, in order to create the session for APP_URL), I can't retrieve the posted data (because it is sent to FB, not to the CANVAS_URL).
I thought about using the session_id to recreate the Session in APP_URL, but I'm afraid that it isn't a very secure approach. I'm sure that there must be a better workaround.