Posted anti-CSRF token is not the same as stored one

103 views
Skip to first unread message

Yoeri Nijs

unread,
Oct 29, 2017, 12:48:09 PM10/29/17
to f3-fra...@googlegroups.com
For the first time, I am trying to implement the anti-CSRF protection of Fatfree. However, the posted token differs from the stored one, while I am in the same session without any change. Does anyone know what I am doing wrong?

In index.php, before run(), I create a new session. With this session variable, I store a new CSRF token:

// Set up the base class
$f3
= Base::instance();

// Start session, create token agains CSRF attacks
// and store token
$session
= new Session();
$f3
->set('CSRF', $session->csrf());

// Run the application
$f3
->run();

Next, after authentication, I store the created CSRF token in a session variable, like this:

       if(password_verify($password, $user->password)) {
            $this
->logLastLogin($user);
            $this
->f3->set('SESSION.username', $user->username);
            $this
->f3->set('SESSION.id', $user->id);
            $this
->f3->set('SESSION.role', $user->role);
            $this
->f3->set('SESSION.rolename', $this->getRoleName());
            $this
->f3->set('SESSION.avatar', $user->avatar);
            $this
->f3->set('SESSION.name', $user->name);
            $this
->f3->set('SESSION.email', $user->email);
            $this
->f3->set('SESSION.class', $user->class_id);
            $this
->f3->set('SESSION.forget', $isForgetEnabled);
            $this
->f3->copy('CSRF','SESSION.csrf'); // Save actual token against CSRF in session
            $this
->f3->reroute('/');
       
} else {
            $this
->f3->reroute('/login');
       
}

Then, I have a form with a hidden input field, which is required. The field has the name 'token' and the value of 'CSRF':

        <form method="POST" action="{{ @BASE }}/edit_course_general?course={{ @admin_course['id'] }}" enctype="multipart/form-data">
           
<input type="hidden" name="token" value="{{ @CSRF }}/>
           
<div class="br-full-div-form-area">
               
<div class="form-group row">
                   
<label for="name" class="col-sm-2 col-form-label">{{ @messages.edit_course_shortcode }}</label>
                   
<div class="col-sm-10">
                       
<input type="text" class="form-control" name="shortcode" placeholder="{{ @admin_course['shortcode'] }}" value="{{ @admin_course['shortcode'] }}">
                   
</div>
               
</div>
               
<div class="form-group row">
                   
<label for="name" class="col-sm-2 col-form-label">{{ @messages.edit_course_name }}</label>
                   
<div class="col-sm-10">
                       
<input type="text" class="form-control" name="name" placeholder="{{ @admin_course['name'] }}" value="{{ @admin_course['name'] }}">
                   
</div>
               
</div>
               
<div class="form-group row">
                   
<label for="role" class="col-sm-2 col-form-label">{{ @messages.edit_course_teacher }}</label>
                   
<div class="col-sm-10">
                       
<select class="form-control" name="teacher">
                           
<repeat group="{{ @admin_teacher }}" value="{{ @teacher }}">
                               
<check if="{{ @teacher['id'] == @admin_course['teacher_id'] }}">
                                   
<true>
                                       
<option>{{ @teacher['username'] }} ({{ @messages.edit_course_current_teacher }})</option>
                                   
</true>
                                   
<false>
                                       
<option>{{ @teacher['username'] }}</option>
                                   
</false>
                               
</check>
                           
</repeat>
                       
</select>
                   
</div>
               
</div>
               
<div class="form-group row">
                   
<label for="name" class="col-sm-2 col-form-label">{{ @messages.edit_course_upload_select }}</label>
                   
<div class="col-sm-10">
                       
<input type="file" name="fileToUpload" id="fileToUpload">
                   
</div>
               
</div>
           
</div>
           
<div class="br-full-div-action-area">
               
<button type="submit" class="btn btn-primary">{{ @messages.edit_course_btnlabel }}</button>
           
</div>
       
</form>

After the post, I capture the current token and compare it with the stored one:

        $storedToken = $this->f3->get('SESSION.csrf');
        $postedToken
= $this->f3->get('POST.token');
       
if($storedToken!=$postedToken) {
            echo $storedToken
;
            echo
"\n\n";
            echo $postedToken
;
       
} else {
            echo
"woohoo";
       
}

The result, is however, that tokens are not the same:

1cpdjtpdvqf4s.39i1wmif918g8 1cpdjtpdvqf4s.3q7ydqzo55s0/>

I am not sure what I am doing wrong. I enabled cache, so that could not be the problem...


ved

unread,
Oct 29, 2017, 2:09:26 PM10/29/17
to Fat-Free Framework
Not sure if its the issue but since you're using "new Session()", which is the cache based session handler, are you setting your CACHE variable?


Yoeri Nijs

unread,
Oct 30, 2017, 2:27:21 PM10/30/17
to f3-fra...@googlegroups.com
Not sure whether I am understand you correctly, but I have a config.ini file that contains 'CACHE = true'. The ini file is loaded in the same index.php as $f3->run() by adding this line (before run()):

// Set up the default configuration
$f3
->config('config.ini');

Op zondag 29 oktober 2017 19:09:26 UTC+1 schreef ved:

Yoeri Nijs

unread,
Oct 30, 2017, 2:45:47 PM10/30/17
to Fat-Free Framework
Well, I discovered what the problem was. First, html was not correct. One " was missing... Next, I had to use SESSION.csrf instead of CSRF.

Thanks for the help nevertheless!

Op maandag 30 oktober 2017 19:27:21 UTC+1 schreef Yoeri Nijs:

Yoeri Nijs

unread,
Nov 11, 2017, 10:42:48 AM11/11/17
to Fat-Free Framework
Still, I am wrestling with the CSRF validation. In the application itself, it works now. However, when initializing the application, and after trying to login, the token does always differ. It is quite frustrating.

So, as aforementioned, I create a CSRF token before running the application with $f3->run(). Next, I add {{ @CSRF }} as hidden input to my login form. So far, so good. However, when I perform a post and try to validate the initial csrf value and the posted value, both values differ always. I do not understand what I am doing wrong and the documentation is lacking some practical examples as well, in my opinion. Please, can one help me?

Op maandag 30 oktober 2017 19:45:47 UTC+1 schreef Yoeri Nijs:

ikkez

unread,
Nov 13, 2017, 7:21:47 AM11/13/17
to f3-fra...@googlegroups.com
It's hard to tell without fully understanding the application and frontend.. the csrf token is used to verify a form.. so you should put SESSION.csrf into your login form, and use
$this->f3->copy('CSRF','SESSION.csrf');

in the method where you're rendering that form. That way you can simply compare the posted value with the SESSION.csrf value. (notice that you have to compare that values BEFORE you're overwriting it with the copy(CSRF,SESSION.csrf) thing )
You shouldn't rely on CSRF directly, because you've placed that in the index.php and that value is changed with EVERY request... that means, even a missing image or css file could trigger a 404, but that error is also generated by the framework (everything not existing reroutes to index.php) which then could alter your CSRF and lead to weird problems.

Reply all
Reply to author
Forward
0 new messages