CVE-2021-3921: Cross-Site Request Forgery (CSRF) (CWE-352)
value: firefly-iii is vulnerable to Cross-Site Request Forgery (CSRF)
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 4.3
Published: 2021-11-13T09:15Z
Repository: https://github.com/firefly-iii/firefly-iii
Language: Other
Files changed: resources/views/v1/auth/login.twig, resources/views/v1/auth/passwords/email.twig, resources/views/v1/auth/passwords/reset.twig, resources/views/v1/auth/register.twig, resources/views/v1/partials/menu-sidebar.twig, resources/views/v1/profile/index.twig, resources/views/v2/auth/register.twig, resources/views/v2/partials/layout/navbar.twig, routes/web.php
Commit date: 2021-11-09 18:46:49 +0100
Commit message: Fix huntr.dev logout issue.

Diff: diff --git a/resources/views/v1/auth/login.twig b/resources/views/v1/auth/login.twig
index 3a7b3366317..d9d7c98adcf 100644
--- a/resources/views/v1/auth/login.twig
+++ b/resources/views/v1/auth/login.twig
@@ -59,11 +59,11 @@
     {% endif %}
 
     <div class="login-box-body">
         <p class="login-box-msg">{{ 'sign_in_to_start'|_ }}</p>
 
-        <form action="{{ URL.to('/login') }}" method="post">
+        <form action="{{ route('login') }}" method="post">
             <input type="hidden" name="_token" value="{{ csrf_token() }}"/>
 
             <div class="form-group has-feedback">
                 {% if config('firefly.authentication_guard') == 'web' %}
                     <input type="email" autocomplete="email" name="email" value="{% if not IS_DEMO_SITE %}{{ email }}{% else %}{{ DEMO_USERNAME }}{% endif %}" class="form-control" placeholder="{{ trans('form.email') }}"/>
@@ -86,13 +86,13 @@
                     <button type="submit" class="btn btn-primary btn-block btn-flat">{{ 'sign_in'|_ }}</button>
                 </div>
             </div>
         </form>
         {% if allowRegistration %}
-            <a href="{{ URL.to('/register') }}" class="text-center">{{ 'register_new_account'|_ }}</a><br>
+            <a href="{{ route('register') }}" class="text-center">{{ 'register_new_account'|_ }}</a><br>
         {% endif %}
         {% if allowReset %}
-            <a href="{{ URL.to('/password/reset') }}">{{ 'forgot_my_password'|_ }}</a>
+            <a href="{{ route('password.reset.request') }}">{{ 'forgot_my_password'|_ }}</a>
         {% endif %}
     </div>
 {% endblock %}
 
diff --git a/resources/views/v1/auth/passwords/email.twig b/resources/views/v1/auth/passwords/email.twig
index 90eebad93c5..748949fcfef 100644
--- a/resources/views/v1/auth/passwords/email.twig
+++ b/resources/views/v1/auth/passwords/email.twig
@@ -25,11 +25,11 @@
                 {{ session('status') }}
             </p>
         {% else %}
             <p class="login-box-msg">{{ 'reset_password'|_ }}</p>
 
-            <form role="form" method="POST" action="{{ URL.to('/password/email') }}">
+            <form role="form" method="POST" action="{{ route('password.email') }}">
                 <input type="hidden" name="_token" value="{{ csrf_token() }}"/>
 
                 <div class="form-group has-feedback">
                     <input type="email" class="form-control" name="email" placeholder="{{ trans('form.email') }}"/>
                 </div>
@@ -41,11 +41,11 @@
                 </div>
 
             </form>
 
         {% endif %}
-        <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a><br>
+        <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a><br>
         {% if allowRegistration %}
             <a href="{{ route('register') }}" class="text-center">{{ 'register_new_account'|_ }}</a><br>
         {% endif %}
 
     </div>
diff --git a/resources/views/v1/auth/passwords/reset.twig b/resources/views/v1/auth/passwords/reset.twig
index e0622ca8f05..82512ed5fbe 100644
--- a/resources/views/v1/auth/passwords/reset.twig
+++ b/resources/views/v1/auth/passwords/reset.twig
@@ -38,11 +38,11 @@
                 </div>
             </div>
 
         </form>
 
-        <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a><br>
+        <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a><br>
         {% if allowRegistration %}
             <a href="{{ route('register') }}" class="text-center">{{ 'register_new_account'|_ }}</a><br>
         {% endif %}
     </div>
 
diff --git a/resources/views/v1/auth/register.twig b/resources/views/v1/auth/register.twig
index ff9e5b16e22..873870cf42e 100644
--- a/resources/views/v1/auth/register.twig
+++ b/resources/views/v1/auth/register.twig
@@ -13,11 +13,11 @@
         </div>
     {% endif %}
     <div class="register-box-body">
         <p class="login-box-msg">{{ 'register_new_account'|_ }}</p>
 
-        <form id="register" method="POST" action="{{ URL.to('/register') }}">
+        <form id="register" method="POST" action="{{ route('register') }}">
             <input type="hidden" name="_token" value="{{ csrf_token() }}">
 
             <div class="form-group has-feedback">
                 <input type="email" name="email" value="{{ email }}" class="form-control" placeholder="{{ trans('form.email') }}"/>
             </div>
@@ -39,12 +39,12 @@
                     <button class="btn btn-primary pull-right btn-flat">{{ 'button_register'|_ }}</button>
                 </div>
             </div>
         </form>
 
-        <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a><br>
-        <a href="{{ URL.to('/password/reset') }}">{{ 'forgot_my_password'|_ }}</a>
+        <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a><br>
+        <a href="{{ route('password.reset.request') }}">{{ 'forgot_my_password'|_ }}</a>
     </div>
 
     {% include 'v1.partials.password-modal' %}
 
 {% endblock %}
diff --git a/resources/views/v1/partials/menu-sidebar.twig b/resources/views/v1/partials/menu-sidebar.twig
index 636f5be0d58..b9de1279cc0 100644
--- a/resources/views/v1/partials/menu-sidebar.twig
+++ b/resources/views/v1/partials/menu-sidebar.twig
@@ -219,13 +219,18 @@
                     </a>
                 </li>
             {% endif %}
         </ul>
     </li>
+
+
     {% if 'remote_user_guard' != authGuard or '' != logoutUri %}
+        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
+            <input type="hidden" name="_token" value="{{ csrf_token() }}" />
+        </form>
         <li>
-            <a href="{{ route('logout') }}">
+            <a href="{{ route('logout') }}" onclick="event.preventDefault();document.getElementById('logout-form').submit();">
                 <span class="fa fa-sign-out fa-fw"></span>
                 <span>{{ 'logout'|_ }}</span>
             </a>
         </li>
     {% endif %}
diff --git a/resources/views/v1/profile/index.twig b/resources/views/v1/profile/index.twig
index d7709ee8342..e7077141ea6 100644
--- a/resources/views/v1/profile/index.twig
+++ b/resources/views/v1/profile/index.twig
@@ -50,11 +50,16 @@
                                         </li>
                                         <li>
                                             <a href="{{ route('profile.change-password') }}">{{ 'change_your_password'|_ }}</a>
                                         </li>
                                         {% endif %}
-                                        <li><a href="{{ route('logout') }}">{{ 'logout'|_ }}</a></li>
+
+                                        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
+                                            <input type="hidden" name="_token" value="{{ csrf_token() }}" />
+                                        </form>
+
+                                        <li><a href="{{ route('logout') }}" onclick="event.preventDefault();document.getElementById('logout-form').submit();">{{ 'logout'|_ }}</a></li>
 
                                         {% if true == isInternalAuth and true == isInternalIdentity %}
                                         <li>
                                             <a href="{{ route('profile.logout-others') }}">{{ 'logout_other_sessions'|_ }}</a>
                                         </li>
diff --git a/resources/views/v2/auth/register.twig b/resources/views/v2/auth/register.twig
index eecb4ec4297..c187cea454c 100644
--- a/resources/views/v2/auth/register.twig
+++ b/resources/views/v2/auth/register.twig
@@ -84,11 +84,11 @@
                                     <a data-toggle="modal" data-target="#passwordModal" href="#passwordModal">
                                         {{ 'what_is_pw_security'|_ }}
                                     </a>
                                 </li>
                                 <li>
-                                    <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a>
+                                    <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a>
                                 </li>
                                 <li>
                                     <a href="{{ route('password.reset.request') }}">{{ 'forgot_my_password'|_ }}</a>
                                 </li>
                             </ul>
diff --git a/resources/views/v2/partials/layout/navbar.twig b/resources/views/v2/partials/layout/navbar.twig
index fc7df198314..e00088aba4d 100644
--- a/resources/views/v2/partials/layout/navbar.twig
+++ b/resources/views/v2/partials/layout/navbar.twig
@@ -48,10 +48,13 @@
                 </a>
             </div>
         </li>
         <!-- Notifications Dropdown Menu -->
         <li class="nav-item dropdown">
+            <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
+                <input type="hidden" name="_token" value="{{ csrf_token() }}" />
+            </form>
             <a class="nav-link" data-toggle="dropdown" href="#">
                 <span class="far fa-user"></span>
             </a>
             <div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
                 <a href="{{ route('profile.index') }}" class="dropdown-item">
@@ -60,11 +63,11 @@
                 <div class="dropdown-divider"></div>
                 <a href="{{ route('preferences.index') }}" class="dropdown-item">
                     <span class="fas fa-fw fa-cogs mr-2"></span> {{ 'preferences'|_ }}
                 </a>
                 <div class="dropdown-divider"></div>
-                <a href="{{ route('logout') }}" class="dropdown-item">
+                <a href="{{ route('logout') }}" class="dropdown-item" onclick="event.preventDefault();document.getElementById('logout-form').submit();">
                     <span class="fas fa-sign-out-alt mr-2"></span> {{ 'logout'|_ }}
                 </a>
             </div>
         </li>
         <li class="nav-item dropdown">
diff --git a/routes/web.php b/routes/web.php
index 3c68a467a8b..6eb7ddb5143 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -79,11 +79,11 @@ static function () {
  */
 Route::group(
     ['middleware' => 'user-simple-auth', 'namespace' => 'FireflyIII\Http\Controllers'],
     static function () {
         Route::get('error', ['uses' => 'DebugController@displayError', 'as' => 'error']);
-        Route::any('logout', ['uses' => 'Auth\LoginController@logout', 'as' => 'logout']);
+        Route::post('logout', ['uses' => 'Auth\LoginController@logout', 'as' => 'logout']);
         Route::get('flush', ['uses' => 'DebugController@flush', 'as' => 'flush']);
         //Route::get('routes', ['uses' => 'DebugController@routes', 'as' => 'routes']);
         Route::get('debug', 'DebugController@index')->name('debug');
     }
 );

Vulnerable code:         <form action="{{ URL.to('/login') }}" method="post">
            <a href="{{ URL.to('/register') }}" class="text-center">{{ 'register_new_account'|_ }}</a><br>
            <a href="{{ URL.to('/password/reset') }}">{{ 'forgot_my_password'|_ }}</a>
            <form role="form" method="POST" action="{{ URL.to('/password/email') }}">
        <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a><br>
        <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a><br>
        <form id="register" method="POST" action="{{ URL.to('/register') }}">
        <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a><br>
        <a href="{{ URL.to('/password/reset') }}">{{ 'forgot_my_password'|_ }}</a>
            <a href="{{ route('logout') }}">
                                        <li><a href="{{ route('logout') }}">{{ 'logout'|_ }}</a></li>
                                    <a href="{{ URL.to('/login') }}">{{ 'want_to_login'|_ }}</a>
                <a href="{{ route('logout') }}" class="dropdown-item">
        Route::any('logout', ['uses' => 'Auth\LoginController@logout', 'as' => 'logout']);
Fixed code:         <form action="{{ route('login') }}" method="post">
            <a href="{{ route('register') }}" class="text-center">{{ 'register_new_account'|_ }}</a><br>
            <a href="{{ route('password.reset.request') }}">{{ 'forgot_my_password'|_ }}</a>
            <form role="form" method="POST" action="{{ route('password.email') }}">
        <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a><br>
        <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a><br>
        <form id="register" method="POST" action="{{ route('register') }}">
        <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a><br>
        <a href="{{ route('password.reset.request') }}">{{ 'forgot_my_password'|_ }}</a>


        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
            <input type="hidden" name="_token" value="{{ csrf_token() }}" />
        </form>
            <a href="{{ route('logout') }}" onclick="event.preventDefault();document.getElementById('logout-form').submit();">

                                        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                            <input type="hidden" name="_token" value="{{ csrf_token() }}" />
                                        </form>

                                        <li><a href="{{ route('logout') }}" onclick="event.preventDefault();document.getElementById('logout-form').submit();">{{ 'logout'|_ }}</a></li>
                                    <a href="{{ route('login') }}">{{ 'want_to_login'|_ }}</a>
            <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                <input type="hidden" name="_token" value="{{ csrf_token() }}" />
            </form>
                <a href="{{ route('logout') }}" class="dropdown-item" onclick="event.preventDefault();document.getElementById('logout-form').submit();">
        Route::post('logout', ['uses' => 'Auth\LoginController@logout', 'as' => 'logout']);