开发安全的前端帖子编辑表单

时间:2015-12-13 作者:Iurie

我想在我的Wordpress网站中添加前端后期编辑功能。我找到了一些这样做的插件,但它们不能满足我的所有需要,所以我决定进行调整an existing solution 开发一个我自己的插件,该插件将返回一个前端编辑表单的短代码。我的插件可以工作,我可以编辑和保存帖子,但是I can\'t solve a warningI don\'t have (yet) sufficient knowledge to make this plugin more secure. 有什么建议吗?

警告:

警告:无法修改标题信息-标题已由(输出开始于…/wp-content/plugins/front-post-edit.php:139)在/wp包括/可插拔。php在线1228

P、 139行是我插件中的最后一行(一行没有代码)

我的插件代码:

<?php
/*
 * Plugin Name: Front Post Editor
 * 
 */

add_shortcode( \'front_post_edit\', \'post_shortcode\' );

function post_shortcode() {
    return getForm();
}

function getForm() {

    if ( !is_user_logged_in()) {
        echo \'<p class="alert-box notice">You must be <a href="\' . esc_url( wp_login_url( get_permalink() ) ) . \'" title="Login">logged in</a>!\';
    } else {

        if( \'POST\' == $_SERVER[\'REQUEST_METHOD\'] && !empty( $_POST[\'action\'] ) &&  $_POST[\'action\'] == "edit_post" && isset($_POST[\'postid\'])) {
            $post_to_edit = array();
            $post_to_edit = get_post($_POST[\'postid\']);

            /* these are the fields that we are editing in the form below */
            $title = $_POST[\'item_title\'];
            $description = $_POST[\'item_description\'];
            $category = $_POST[\'item_category\'];
            $location = $_POST[\'item_location\'];
            $location2 = $_POST[\'item_location2\'];

            /* this code will save the title and description into the post_to_edit array */
            $post_to_edit->post_title = $title;
            $post_to_edit->post_content = $description;

            /* this code is a must */
            $pid = wp_update_post($post_to_edit);

            /* save taxonomies: post ID, form field name, taxonomy name, if it appends(true) or rewrite(false) */
            wp_set_post_terms($pid, array($_POST[\'item_category\']),\'category\',false);
            wp_set_post_terms($pid, array($_POST[\'item_location\']),\'location\',false);

            /* update custom fields with the new info */
            update_post_meta($pid, \'item_location2\', $location2);

            /* redirect user after done editing */
            wp_redirect( home_url( \'/myposts\' ) );

        }

        /* get post to edit */
        $post_to_edit = get_post($_POST[\'postid\']);

        /* get this post\'s category taxonomy term id */
        $term_name = strip_tags( get_the_term_list( $post_to_edit->ID, \'category\', \'\', \', \', \'\' ) );
        $term_obj = get_term_by(\'name\', $term_name, \'category\');
        $term_id = $term_obj->term_id;

        /* array for wp_dropdown_category to display with the current post category selected by default */
        $args_cat = array(
            \'selected\'           => $term_id,
            \'name\'               => \'item_category\',
            \'class\'              => \'postform\',
            \'tab_index\'          => 10,
            \'depth\'              => 2,
            \'hierarchical\'       => 1,
            \'taxonomy\'           => \'category\',
            \'hide_empty\'      => false );

        /* get this post\'s location taxonomy term id */
        $term_name2 = strip_tags( get_the_term_list( $post_to_edit->ID, \'location\', \'\', \', \', \'\' ) );
        $term_obj2 = get_term_by(\'name\', $term_name2, \'location\');
        $term_id2 = $term_obj2->term_id;

        $args_loc = array(
            \'selected\'           => $term_id2,
            \'name\'               => \'item_location\',
            \'class\'              => \'postform\',
            \'tab_index\'          => 10,
            \'depth\'              => 2,
            \'hierarchical\'       => 1,
            \'taxonomy\'           => \'location\',
            \'hide_empty\'      => false ); 

    ?>

<!-- EDIT FORM -->

    <form id="edit_post" name="edit_post" method="post" action="" enctype="multipart/form-data">

        <!-- post name -->
        <fieldset name="item_title">
            <label for="item_title">Item title:</label><br />
            <input type="text" id="item_title" value="<?php echo $post_to_edit->post_title; ?>" tabindex="5" name="item_title" />
        </fieldset>

        <!-- post Content -->
        <fieldset class="item_description">
            <label for="item_description">Item description:</label><br />
            <textarea id="item_description"  tabindex="15" name="item_description"><?php echo $post_to_edit->post_content; ?></textarea>
        </fieldset>

        <!-- post Category -->
        <fieldset id="item_category">
            <label for="item_category">Item category:</label>
            <?php wp_dropdown_categories( $args_cat ); ?>
        </fieldset>

        <!-- post Location -->
        <fieldset id="item_location">
            <label for="item_location">Item location:</label>
            <?php wp_dropdown_categories( $args_loc ); ?>
        </fieldset>

        <!-- custom fields -->

        <fieldset class="item_location2">
            <label for="item_location2">Location 2:</label><br />
            <input type="text" value="<?php echo get_post_meta($post_to_edit->ID,\'item_location2\', true); ?>" id="item_location2" tabindex="20" name="item_location2" />
        </fieldset>

        <!-- submit button -->
        <fieldset class="submit">
            <input type="submit" value="Save Post" tabindex="40" id="submit" name="submit" />
        </fieldset>

        <input type="hidden" name="postid" value="<?php echo $post_to_edit->ID; ?>" /> <!-- DONT REMOVE OR CHANGE -->
        <input type="hidden" name="action" value="edit_post" />                           <!-- DONT REMOVE OR CHANGE -->
        <input type="hidden" name="change_cat" value="" />                                <!-- DONT REMOVE OR CHANGE -->
    <?php // wp_nonce_field( \'new-post\' ); ?>
    </form>
    <!-- END OF FORM -->

<?php } ?><!-- user is logged in -->
<?php } ?><!-- getForm -->
使用以下表单循环生成的每个条目的页脚都添加了一个编辑按钮:

<form class="edit-post" action="<?php echo home_url( \'/edit\'); ?>" method="post">
    <input type="hidden" name="postid" value="<?php the_ID(); ?>" />
    <input type="submit"  value="Edit" />
</form>

2 个回复
SO网友:Mark Kaplun

从代码来看,您的警告似乎是因为执行重定向太晚了。根据经验,重定向应该在初始化操作之后进行。在重定向之后,你应该死()wp_redirect 为您而做)

至于安全性,仅仅检查用户是否登录是不够的,您需要检查他是否有能力编辑帖子,比如if current_user_can(\'edit_post\',$post_id). 您需要在UI端和服务器端都进行检查。仅仅因为你没有向黑客展示这种能力,并不意味着如果你在服务器端没有保护,他就不会构造一个特殊的HTTP请求来更改帖子。

SO网友:Iurie

经过一些学习和研究,我放弃了问题中描述的方法,事实上,我找到了如何使用@TheDeadMedic添加前端编辑功能的方法solution. 现在I have only two little questions:

1) 有没有更好的方法来使用/设置帖子ID和发布日期?我使用了两个隐藏输入:foo_idfoo_date.

2) 表单之间的通信有多安全?

这是将帖子ID获取到“postid”变量并将其传递到“post form”页面的表单:

<form action="<?php echo home_url( \'/post-form\'); ?>" method="post">
    <input type="hidden" name="postid" value="<?php the_ID(); ?>" /> 
    <input type="submit"  value="Edit" />
</form>

<?php
这是用于发布编辑帖子的前端表单(我添加的代码标记为// MY CODE):

class WPSE_Submit_From_Front {
    const NONCE_VALUE = \'front_end_new_post\';
    const NONCE_FIELD = \'fenp_nonce\';

    protected $pluginPath;
    protected $pluginUrl;
    protected $errors = array();
    protected $data = array();

    function __construct() {
        $this->pluginPath = plugin_dir_path( __file__ );
        $this->pluginUrl  = plugins_url( \'\', __file__ );

        add_action( \'wp_enqueue_scripts\', array( $this, \'addStyles\' ) );
        add_shortcode( \'post_from_front\', array( $this, \'shortcode\' ) );

        // Listen for the form submit & process before headers output
        add_action( \'template_redirect\',  array( $this, \'handleForm\' ) );
    }

    function addStyles() {
        wp_enqueue_style( \'submitform-style\', "$this->pluginUrl/submitfromfront.css" );
    }

    /**
     * Shortcodes should return data, NOT echo it.
     *
     * @return string
     */
    function shortcode() {
        if ( ! current_user_can( \'publish_posts\' ) )
            return sprintf( \'<p>Please <a href="%s">login</a> to post links.</p>\', esc_url( wp_login_url(  get_permalink() ) ) );
        elseif ( $this->isFormSuccess() )
            return \'<p class="success">Nice one, post created.</p>\';
        else
            return $this->getForm();
    }

    /**
     * Process the form and redirect if sucessful.
     */
    function handleForm() {
        if ( ! $this->isFormSubmitted() )
            return false;

        // http://php.net/manual/en/function.filter-input-array.php
        $data = filter_input_array( INPUT_POST, array(
            // MY CODE
            \'foo_id\'      => FILTER_DEFAULT,
            \'foo_date\'    => FILTER_DEFAULT,
            // END MY CODE
            \'postTitle\'   => FILTER_DEFAULT,
            \'postContent\' => FILTER_DEFAULT,
            \'location2\'   => FILTER_DEFAULT,
        ));

        $data = wp_unslash( $data );
        $data = array_map( \'trim\', $data );

        // You might also want to more aggressively sanitize these fields
        // By default WordPress will handle it pretty well, based on the current user\'s "unfiltered_html" capability

        $data[\'postTitle\']   = sanitize_text_field( $data[\'postTitle\'] );
        $data[\'postContent\'] = wp_check_invalid_utf8( $data[\'postContent\'] );
        $data[\'location2\']   = sanitize_text_field( $data[\'location2\'] );

        $this->data = $data;

        if ( ! $this->isNonceValid() )
            $this->errors[] = \'Security check failed, please try again.\';

        if ( ! $data[\'postTitle\'] )
            $this->errors[] = \'Please enter a title.\';

        if ( ! $data[\'postContent\'] )
            $this->errors[] = \'Please enter the content.\';

        if ( ! $this->errors ) {
            $post_id = wp_insert_post( array(
                // MY CODE
                \'ID\'           => $data[\'foo_id\'],
                \'post_date\'    => $data[\'foo_date\'],
                // END MY CODE
                \'post_title\'   => $data[\'postTitle\'],
                \'post_content\' => $data[\'postContent\'],
                \'post_status\'  => \'publish\',
            ));

            if ( $post_id ) {
                add_post_meta( $post_id, \'location2\', $data[\'location2\'] );

                // Redirect to avoid duplicate form submissions
                wp_redirect( add_query_arg( \'success\', \'true\' ) );
                exit;

            } else {
                $this->errors[] = \'Whoops, please try again.\';
            }
        }
    }

    /**
     * Use output buffering to *return* the form HTML, not echo it.
     *
     * @return string
     */
    function getForm() {

    // MY CODE
    if( \'POST\' == $_SERVER[\'REQUEST_METHOD\'] && isset( $_POST[\'postid\'] ) ) {
        $post_to_edit                   = array();
        $post_to_edit                   = get_post( $_POST[\'postid\'] );
        $this->data[\'foo_id\']           = $post_to_edit->ID;
        $this->data[\'foo_date\']         = $post_to_edit->post_date;
        $this->data[\'item_name\']        = $post_to_edit->post_title;
        $this->data[\'item_description\'] = $post_to_edit->post_content;
    }
    // END MY CODE

        ob_start();
        ?>

<div id ="frontpostform">
    <?php foreach ( $this->errors as $error ) : ?>

        <p class="error"><?php echo $error ?></p>

    <?php endforeach ?>

    <form id="formpost" method="post">
        <fieldset>
            <label for="postTitle">Post Title</label>
            <input type="text" name="postTitle" id="postTitle" value="<?php

                // "Sticky" field, will keep value from last POST if there were errors
                if ( isset( $this->data[\'postTitle\'] ) )
                    echo esc_attr( $this->data[\'postTitle\'] );

            ?>" />
        </fieldset>

        <fieldset>
            <label for="postContent">Content</label>
            <textarea name="postContent" id="postContent" rows="10" cols="35" ><?php

                if ( isset( $this->data[\'postContent\'] ) )
                    echo esc_textarea( $this->data[\'postContent\'] );

            ?></textarea>
        </fieldset>

        <fieldset>
        <label for="location2">Location 2</label>
        <input type="text" name="location2" id="location2" title="Location 2 (opțional)" value="<?php

            // "Sticky" field, will keep value from last POST if there were errors
            if ( isset( $this->data[\'location2\'] ) )
                echo esc_attr( $this->data[\'location2\'] );

        ?>" />
        </fieldset>

        <fieldset>
            // MY CODE
            <input type="hidden" name="foo_id" id="foo_id" value="<?php

            // "Sticky" field, will keep value from last POST if there were errors
            if ( isset( $this->data[\'foo_id\'] ) )
                echo esc_attr( $this->data[\'foo_id\'] );

        ?>" />

            <input type="hidden" name="foo_date" id="foo_date" value="<?php

            // "Sticky" field, will keep value from last POST if there were errors
            if ( isset( $this->data[\'foo_date\'] ) )
                echo esc_attr( $this->data[\'foo_date\'] );

        ?>" />
            // END MY CODE
            <button type="submit" name="submitForm" >Create Post</button>
        </fieldset>

        <?php wp_nonce_field( self::NONCE_VALUE , self::NONCE_FIELD ) ?>
    </form>
</div>

        <?php
        return ob_get_clean();
    }

    /**
     * Has the form been submitted?
     *
     * @return bool
     */
    function isFormSubmitted() {
        return isset( $_POST[\'submitForm\'] );
    }

    /**
     * Has the form been successfully processed?
     *
     * @return bool
     */
    function isFormSuccess() {
        return filter_input( INPUT_GET, \'success\' ) === \'true\';
    }

    /**
     * Is the nonce field valid?
     *
     * @return bool
     */
    function isNonceValid() {
        return isset( $_POST[ self::NONCE_FIELD ] ) && wp_verify_nonce( $_POST[ self::NONCE_FIELD ], self::NONCE_VALUE );
    }
}

new WPSE_Submit_From_Front;

相关推荐

是否可以取消对特定帖子类型的POSTS_PER_PAGE限制?

我想知道我是否可以取消特定帖子类型的posts\\u per\\u页面限制。在存档中。php页面我显示不同的帖子类型,对于特定的“出版物”帖子类型,我想显示所有帖子。我如何在不影响传统“post”类型的情况下实现这一点?