]> git.mxchange.org Git - friendica.git/commitdiff
Some security against XSRF-attacks
authorTobias Hößl <tobias@hoessl.eu>
Mon, 12 Mar 2012 20:17:37 +0000 (20:17 +0000)
committerTobias Hößl <tobias@hoessl.eu>
Mon, 12 Mar 2012 20:17:37 +0000 (20:17 +0000)
13 files changed:
include/security.php
mod/profile_photo.php
mod/profiles.php
mod/settings.php
view/cropbody.tpl
view/profile_edit.tpl
view/profile_listing_header.tpl
view/profile_photo.tpl
view/settings.tpl
view/settings_addons.tpl
view/settings_connectors.tpl
view/settings_oauth.tpl
view/settings_oauth_edit.tpl

index 8c536b656aca95f3e18deb5f6cd5421a2c98f020..6ea515bffe41ff907b5bffb7ee1d237b0bc397f5 100755 (executable)
@@ -288,3 +288,49 @@ function item_permissions_sql($owner_id,$remote_verified = false,$groups = null)
 }
 
 
+/*
+ * Functions used to protect against Cross-Site Request Forgery
+ * The security token has to base on at least one value that an attacker can't know - here it's the session ID and the private key.
+ * In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes;
+ * or if the security token is used for ajax-calls that happen several times), but only valid for a certain amout of time (3hours).
+ * The "typename" seperates the security tokens of different types of forms. This could be relevant in the following case:
+ *    A security token is used to protekt a link from CSRF (e.g. the "delete this profile"-link).
+ *    If the new page contains by any chance external elements, then the used security token is exposed by the referrer.
+ *    Actually, important actions should not be triggered by Links / GET-Requests at all, but somethimes they still are,
+ *    so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types).
+ */ 
+function get_form_security_token($typename = "") {
+       $a = get_app();
+       
+       $timestamp = time();
+       $sec_hash = hash('whirlpool', $a->user["guid"] . $a->user["prvkey"] . session_id() . $timestamp . $typename);
+       
+       return $timestamp . "." . $sec_hash;
+}
+
+function check_form_security_token($typename = "", $formname = 'form_security_token') {
+       if (!x($_REQUEST, $formname)) return false;
+       $hash = $_REQUEST[$formname];
+       
+       $max_livetime = 10800; // 3 hours
+       
+       $a = get_app();
+       
+       $x = explode(".", $hash);
+       if (time() > (IntVal($x[0]) + $max_livetime)) return false;
+       
+       $sec_hash = hash('whirlpool', $a->user["guid"] . $a->user["prvkey"] . session_id() . $x[0] . $typename);
+       
+       return ($sec_hash == $x[1]);
+}
+
+function check_form_security_std_err_msg() {
+       return t('The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before subitting it.') . EOL;
+}
+function check_form_security_token_redirectOnErr($err_redirect, $typename = "", $formname = 'form_security_token') {
+       if (!check_form_security_token($typename, $formname)) {
+               $a = get_app();
+               notice( check_form_security_std_err_msg() );
+               goaway($a->get_baseurl() . $err_redirect );
+       }
+}
index e3dbdaf39c803006e4a609a67df47b6d59c08ac7..d1fd08eba68ed5fcb2f12c59d97c96fb8e194863 100755 (executable)
@@ -15,11 +15,13 @@ function profile_photo_init(&$a) {
 
 function profile_photo_post(&$a) {
 
-        if(! local_user()) {
-                notice ( t('Permission denied.') . EOL );
-                return;
-        }
-
+       if(! local_user()) {
+               notice ( t('Permission denied.') . EOL );
+               return;
+       }
+       
+       check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo');
+        
        if((x($_POST,'cropfinal')) && ($_POST['cropfinal'] == 1)) {
 
                // phase 2 - we have finished cropping
@@ -148,7 +150,9 @@ function profile_photo_content(&$a) {
                        notice( t('Permission denied.') . EOL );
                        return;
                };
-                       
+               
+               check_form_security_token_redirectOnErr('/profile_photo', 'profile_photo');
+        
                $resource_id = $a->argv[2];
                //die(":".local_user());
                $r=q("SELECT * FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' ORDER BY `scale` ASC",
@@ -203,6 +207,7 @@ function profile_photo_content(&$a) {
                        '$lbl_upfile' => t('Upload File:'),
                        '$title' => t('Upload Profile Photo'),
                        '$submit' => t('Upload'),
+                       '$form_security_token' => get_form_security_token("profile_photo"),
                        '$select' => sprintf('%s %s', t('or'), ($newuser) ? '<a href="' . $a->get_baseurl() . '">' . t('skip this step') . '</a>' : '<a href="'. $a->get_baseurl() . '/photos/' . $a->user['nickname'] . '">' . t('select a photo from your photo albums') . '</a>')
                ));
 
@@ -218,6 +223,7 @@ function profile_photo_content(&$a) {
                        '$image_url' => $a->get_baseurl() . '/photo/' . $filename,
                        '$title' => t('Crop Image'),
                        '$desc' => t('Please adjust the image cropping for optimum viewing.'),
+                       '$form_security_token' => get_form_security_token("profile_photo"),
                        '$done' => t('Done Editing')
                ));
                return $o;
index ccd7d54741af192aed688c4449182d6d158aaf42..b307a2d43bfe3af50b8c81f1b885e56ea93e62df 100755 (executable)
@@ -21,6 +21,9 @@ function profiles_post(&$a) {
                        notice( t('Profile not found.') . EOL);
                        return;
                }
+               
+               check_form_security_token_redirectOnErr('/profiles', 'profile_edit');
+               
                $is_default = (($orig[0]['is-default']) ? 1 : 0);
 
                $profile_name = notags(trim($_POST['profile_name']));
@@ -240,6 +243,8 @@ function profiles_content(&$a) {
                        goaway($a->get_baseurl() . '/profiles');
                        return; // NOTREACHED
                }
+               
+               check_form_security_token_redirectOnErr('/profiles', 'profile_drop', 't');
 
                // move every contact using this profile as their default to the user default
 
@@ -264,6 +269,8 @@ function profiles_content(&$a) {
 
 
        if(($a->argc > 1) && ($a->argv[1] === 'new')) {
+               
+               check_form_security_token_redirectOnErr('/profiles', 'profile_new', 't');
 
                $r0 = q("SELECT `id` FROM `profile` WHERE `uid` = %d",
                        intval(local_user()));
@@ -291,10 +298,13 @@ function profiles_content(&$a) {
                info( t('New profile created.') . EOL);
                if(count($r3) == 1)
                        goaway($a->get_baseurl() . '/profiles/' . $r3[0]['id']);
+               
                goaway($a->get_baseurl() . '/profiles');
-       }                
+       } 
 
        if(($a->argc > 2) && ($a->argv[1] === 'clone')) {
+               
+               check_form_security_token_redirectOnErr('/profiles', 'profile_clone', 't');
 
                $r0 = q("SELECT `id` FROM `profile` WHERE `uid` = %d",
                        intval(local_user()));
@@ -330,9 +340,11 @@ function profiles_content(&$a) {
                info( t('New profile created.') . EOL);
                if(count($r3) == 1)
                        goaway($a->get_baseurl() . '/profiles/' . $r3[0]['id']);
-       goaway($a->get_baseurl() . '/profiles');
-       return; // NOTREACHED
-       }                
+               
+               goaway($a->get_baseurl() . '/profiles');
+               
+               return; // NOTREACHED
+       }
 
 
        if(($a->argc > 1) && (intval($a->argv[1]))) {
@@ -371,6 +383,9 @@ function profiles_content(&$a) {
                $is_default = (($r[0]['is-default']) ? 1 : 0);
                $tpl = get_markup_template("profile_edit.tpl");
                $o .= replace_macros($tpl,array(
+                       '$form_security_token' => get_form_security_token("profile_edit"),
+                       '$profile_clone_link' => 'profiles/clone/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_clone"),
+                       '$profile_drop_link' => 'profiles/drop/' . $r[0]['id'] . '?t=' . get_form_security_token("profile_drop"),
                        '$banner' => t('Edit Profile Details'),
                        '$submit' => t('Submit'),
                        '$viewprof' => t('View this profile'),
@@ -460,7 +475,8 @@ function profiles_content(&$a) {
                        $o .= replace_macros($tpl_header,array(
                                '$header' => t('Edit/Manage Profiles'),
                                '$chg_photo' => t('Change profile photo'),
-                               '$cr_new' => t('Create New Profile')
+                               '$cr_new' => t('Create New Profile'),
+                               '$cr_new_link' => 'profiles/new?t=' . get_form_security_token("profile_new")
                        ));
 
 
index 2ef582fdfeec84b9f760a287716fd54208da98dd..f42fdb3973178d30a1f5740b546bff79cadc1647 100755 (executable)
@@ -53,6 +53,8 @@ function settings_post(&$a) {
        $old_page_flags = $a->user['page-flags'];
 
        if(($a->argc > 1) && ($a->argv[1] === 'oauth') && x($_POST,'remove')){
+               check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth');
+               
                $key = $_POST['remove'];
                q("DELETE FROM tokens WHERE id='%s' AND uid=%d",
                        dbesc($key),
@@ -63,6 +65,8 @@ function settings_post(&$a) {
 
        if(($a->argc > 2) && ($a->argv[1] === 'oauth')  && ($a->argv[2] === 'edit'||($a->argv[2] === 'add')) && x($_POST,'submit')) {
                
+               check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth');
+               
                $name           = ((x($_POST,'name')) ? $_POST['name'] : '');
                $key            = ((x($_POST,'key')) ? $_POST['key'] : '');
                $secret         = ((x($_POST,'secret')) ? $_POST['secret'] : '');
@@ -105,13 +109,18 @@ function settings_post(&$a) {
        }
 
        if(($a->argc > 1) && ($a->argv[1] == 'addon')) {
+               check_form_security_token_redirectOnErr('/settings/addon', 'settings_addon');
+               
                call_hooks('plugin_settings_post', $_POST);
                return;
        }
 
        if(($a->argc > 1) && ($a->argv[1] == 'connectors')) {
-
-               if(x($_POST['imap-submit'])) {
+               
+               check_form_security_token_redirectOnErr('/settings/connectors', 'settings_connectors');
+               
+               if(x($_POST, 'imap-submit')) {
+                       
                        $mail_server       = ((x($_POST,'mail_server')) ? $_POST['mail_server'] : '');
                        $mail_port         = ((x($_POST,'mail_port')) ? $_POST['mail_port'] : '');
                        $mail_ssl          = ((x($_POST,'mail_ssl')) ? strtolower(trim($_POST['mail_ssl'])) : '');
@@ -185,7 +194,8 @@ function settings_post(&$a) {
                return;
        }
 
-
+       check_form_security_token_redirectOnErr('/settings', 'settings');
+       
        call_hooks('settings_post', $_POST);
 
        if((x($_POST,'npassword')) || (x($_POST,'confirm'))) {
@@ -460,6 +470,7 @@ function settings_content(&$a) {
                if(($a->argc > 2) && ($a->argv[2] === 'add')) {
                        $tpl = get_markup_template("settings_oauth_edit.tpl");
                        $o .= replace_macros($tpl, array(
+                               '$form_security_token' => get_form_security_token("settings_oauth"),
                                '$tabs'         => $tabs,
                                '$title'        => t('Add application'),
                                '$submit'       => t('Submit'),
@@ -486,6 +497,7 @@ function settings_content(&$a) {
                        
                        $tpl = get_markup_template("settings_oauth_edit.tpl");
                        $o .= replace_macros($tpl, array(
+                               '$form_security_token' => get_form_security_token("settings_oauth"),
                                '$tabs'         => $tabs,
                                '$title'        => t('Add application'),
                                '$submit'       => t('Update'),
@@ -500,6 +512,8 @@ function settings_content(&$a) {
                }
                
                if(($a->argc > 3) && ($a->argv[2] === 'delete')) {
+                       check_form_security_token_redirectOnErr('/settings/oauth', 'settings_oauth', 't');
+               
                        $r = q("DELETE FROM clients WHERE client_id='%s' AND uid=%d",
                                        dbesc($a->argv[3]),
                                        local_user());
@@ -518,6 +532,7 @@ function settings_content(&$a) {
                
                $tpl = get_markup_template("settings_oauth.tpl");
                $o .= replace_macros($tpl, array(
+                       '$form_security_token' => get_form_security_token("settings_oauth"),
                        '$baseurl'      => $a->get_baseurl(),
                        '$title'        => t('Connected Apps'),
                        '$add'          => t('Add application'),
@@ -544,6 +559,7 @@ function settings_content(&$a) {
                
                $tpl = get_markup_template("settings_addons.tpl");
                $o .= replace_macros($tpl, array(
+                       '$form_security_token' => get_form_security_token("settings_addons"),
                        '$title'        => t('Plugin Settings'),
                        '$tabs'         => $tabs,
                        '$settings_addons' => $settings_addons
@@ -586,28 +602,28 @@ function settings_content(&$a) {
 
        $tpl = get_markup_template("settings_connectors.tpl");
                $o .= replace_macros($tpl, array(
+                       '$form_security_token' => get_form_security_token("settings_connectors"),
+                       
                        '$title'        => t('Connector Settings'),
                        '$tabs'         => $tabs,
 
-               '$diasp_enabled' => $diasp_enabled,
-               '$ostat_enabled' => $ostat_enabled,
-
-               '$h_imap' => t('Email/Mailbox Setup'),
-               '$imap_desc' => t("If you wish to communicate with email contacts using this service \x28optional\x29, please specify how to connect to your mailbox."),
-               '$imap_lastcheck' => array('imap_lastcheck', t('Last successful email check:'), $mail_chk,''),
-               '$mail_disabled' => (($mail_disabled) ? t('Email access is disabled on this site.') : ''),
-               '$mail_server'  => array('mail_server',  t('IMAP server name:'), $mail_server, ''),
-               '$mail_port'    => array('mail_port',    t('IMAP port:'), $mail_port, ''),
-               '$mail_ssl'             => array('mail_ssl',     t('Security:'), strtoupper($mail_ssl), '', array( ''=>t('None'), 'TLS'=>'TLS', 'SSL'=>'SSL')),
-               '$mail_user'    => array('mail_user',    t('Email login name:'), $mail_user, ''),
-               '$mail_pass'    => array('mail_pass',    t('Email password:'), '', ''),
-               '$mail_replyto' => array('mail_replyto', t('Reply-to address:'), '', 'Optional'),
-               '$mail_pubmail' => array('mail_pubmail', t('Send public posts to all email contacts:'), $mail_pubmail, ''),
-               '$mail_action'  => array('mail_action',  t('Action after import:'), $mail_action, '', array(0=>t('None'), 1=>t('Delete'), 2=>t('Mark as seen'), 3=>t('Move to folder'))),
-               '$mail_movetofolder'    => array('mail_movetofolder',    t('Move to folder:'), $mail_movetofolder, ''),
-               '$submit' => t('Submit'),
-
-
+                       '$diasp_enabled' => $diasp_enabled,
+                       '$ostat_enabled' => $ostat_enabled,
+
+                       '$h_imap' => t('Email/Mailbox Setup'),
+                       '$imap_desc' => t("If you wish to communicate with email contacts using this service \x28optional\x29, please specify how to connect to your mailbox."),
+                       '$imap_lastcheck' => array('imap_lastcheck', t('Last successful email check:'), $mail_chk,''),
+                       '$mail_disabled' => (($mail_disabled) ? t('Email access is disabled on this site.') : ''),
+                       '$mail_server'  => array('mail_server',  t('IMAP server name:'), $mail_server, ''),
+                       '$mail_port'    => array('mail_port',    t('IMAP port:'), $mail_port, ''),
+                       '$mail_ssl'             => array('mail_ssl',     t('Security:'), strtoupper($mail_ssl), '', array( ''=>t('None'), 'TLS'=>'TLS', 'SSL'=>'SSL')),
+                       '$mail_user'    => array('mail_user',    t('Email login name:'), $mail_user, ''),
+                       '$mail_pass'    => array('mail_pass',    t('Email password:'), '', ''),
+                       '$mail_replyto' => array('mail_replyto', t('Reply-to address:'), '', 'Optional'),
+                       '$mail_pubmail' => array('mail_pubmail', t('Send public posts to all email contacts:'), $mail_pubmail, ''),
+                       '$mail_action'  => array('mail_action',  t('Action after import:'), $mail_action, '', array(0=>t('None'), 1=>t('Delete'), 2=>t('Mark as seen'), 3=>t('Move to folder'))),
+                       '$mail_movetofolder'    => array('mail_movetofolder',    t('Move to folder:'), $mail_movetofolder, ''),
+                       '$submit' => t('Submit'),
 
                        '$settings_connectors' => $settings_connectors
                ));
@@ -805,6 +821,7 @@ function settings_content(&$a) {
                '$submit'       => t('Submit'),
                '$baseurl' => $a->get_baseurl(),
                '$uid' => local_user(),
+               '$form_security_token' => get_form_security_token("settings"),
                
                '$nickname_block' => $prof_addr,
                
index c9c0f84de16ce4c5290e972465769967e4168535..b484d15bf7f2fb3f81b549ce7a150b2a56966e9f 100755 (executable)
@@ -40,6 +40,7 @@ $desc
 </script>
 
 <form action="profile_photo/$resource" id="crop-image-form" method="post" />
+<input type='hidden' name='form_security_token' value='$form_security_token'>
 
 <input type="hidden" name="cropfinal" value="1" />
 <input type="hidden" name="xstart" id="x1" />
index 8dab72649222d04a003010712c5af405e8910a32..e5c7162d03946f723c8f0620df6bb21e89338692 100755 (executable)
@@ -5,9 +5,9 @@ $default
 <div id="profile-edit-links">
 <ul>
 <li><a href="profile/$profile_id/view?tab=profile" id="profile-edit-view-link" title="$viewprof">$viewprof</a></li>
-<li><a href="profiles/clone/$profile_id" id="profile-edit-clone-link" title="$cr_prof">$cl_prof</a></li>
+<li><a href="$profile_clone_link" id="profile-edit-clone-link" title="$cr_prof">$cl_prof</a></li>
 <li></li>
-<li><a href="profiles/drop/$profile_id" id="profile-edit-drop-link" title="$del_prof" $disabled >$del_prof</a></li>
+<li><a href="$profile_drop_link" id="profile-edit-drop-link" title="$del_prof" $disabled >$del_prof</a></li>
 
 </ul>
 </div>
@@ -17,6 +17,7 @@ $default
 
 <div id="profile-edit-wrapper" >
 <form id="profile-edit-form" name="form1" action="profiles/$profile_id" method="post" >
+<input type='hidden' name='form_security_token' value='$form_security_token'>
 
 <div id="profile-edit-profile-name-wrapper" >
 <label id="profile-edit-profile-name-label" for="profile-edit-profile-name" >$lbl_profname </label>
index 09e4fc9b24cb0b50c66a1217d73c4aa5c4b4215f..61a27379292bb14991271e35ee1c6145f6b24e6f 100755 (executable)
@@ -3,6 +3,6 @@
 <a href="profile_photo" >$chg_photo</a>
 </p>
 <div id="profile-listing-new-link-wrapper" class="button" >
-<a href="profiles/new" id="profile-listing-new-link" title="$cr_new" >$cr_new</a>
+<a href="$cr_new_link" id="profile-listing-new-link" title="$cr_new" >$cr_new</a>
 </div>
 
index f258b5b86dc85fac6be1bef54a73af7d692cec81..0b3a1cac17f82038c637df84ca971100c390fcff 100755 (executable)
@@ -1,6 +1,7 @@
 <h1>$title</h1>
 
 <form enctype="multipart/form-data" action="profile_photo" method="post">
+<input type='hidden' name='form_security_token' value='$form_security_token'>
 
 <div id="profile-photo-upload-wrapper">
 <label id="profile-photo-upload-label" for="profile-photo-upload">$lbl_upfile </label>
index 46c737b23a70986b418eeb1a77db2fabfe6d2f91..25479b5bff8f4ef26c5ff8d1a17063db7d5c848a 100755 (executable)
@@ -5,7 +5,7 @@ $tabs
 $nickname_block
 
 <form action="settings" id="settings-form" method="post" autocomplete="off" >
-
+<input type='hidden' name='form_security_token' value='$form_security_token'>
 
 <h3 class="settings-heading">$h_pass</h3>
 
index 2cbfd17e92af79c4ede727d39961a7dd0214fcfb..28fca53620a0e9e254cdc4778a953c7b38b044a9 100755 (executable)
@@ -4,6 +4,7 @@ $tabs
 
 
 <form action="settings/addon" method="post" autocomplete="off">
+<input type='hidden' name='form_security_token' value='$form_security_token'>
 
 $settings_addons
 
index 9493c8bf7795d24e904ec44925944694023b0f31..43c0346bba7dc5e726c36ceaca4dd327a10594ab 100755 (executable)
@@ -6,6 +6,7 @@ $tabs
 <div class="connector_statusmsg">$ostat_enabled</div>
 
 <form action="settings/connectors" method="post" autocomplete="off">
+<input type='hidden' name='form_security_token' value='$form_security_token'>
 
 $settings_connectors
 
index 0de0dbe98ac19937e870f7858064c8b61721b5db..da1398ab9656eb694b3f352ec8b37b6a58a03560 100755 (executable)
@@ -4,7 +4,8 @@ $tabs
 
 
 <form action="settings/oauth" method="post" autocomplete="off">
-       
+<input type='hidden' name='form_security_token' value='$form_security_token'>
+
        <div id="profile-edit-links">
                <ul>
                        <li>
@@ -24,7 +25,7 @@ $tabs
                {{ endif }}
                {{ if $app.my }}
                <a href="$baseurl/settings/oauth/edit/$app.client_id" class="icon s22 edit" title="$edit">&nbsp;</a>
-               <a href="$baseurl/settings/oauth/delete/$app.client_id" class="icon s22 delete" title="$delete">&nbsp;</a>
+               <a href="$baseurl/settings/oauth/delete/$app.client_id?t=$form_security_token" class="icon s22 delete" title="$delete">&nbsp;</a>
                {{ endif }}             
        </div>
        {{ endfor }}
index 98b7457aa42b2895a1bf9556ad94efec61d9e7d2..d293413867700aff64d9537c5cf9872f387e7ed4 100755 (executable)
@@ -3,6 +3,8 @@ $tabs
 <h1>$title</h1>
 
 <form method="POST">
+<input type='hidden' name='form_security_token' value='$form_security_token'>
+
 {{ inc field_input.tpl with $field=$name }}{{ endinc }}
 {{ inc field_input.tpl with $field=$key }}{{ endinc }}
 {{ inc field_input.tpl with $field=$secret }}{{ endinc }}