如何安全地在Controller中调用删除操作?

时间:2019-03-10 10:28:36

标签: symfony symfony-forms symfony4 symfony-4.2

我创建了一个带有评论系统的博客,我希望作者或管理员删除他的评论。

因此,我在互联网上进行了搜索,但是我发现只有关于Symfony 2/3的帖子,我很难理解。

所以我创建了自己的函数

/**
 * @Route("/blog/commentDelete/{id}-{articleId}-{articleSlug}", name="comment_delete")
 */
public function commentDelete($id, $articleId, $articleSlug, CommentRepository $commentRepository, AuthorizationCheckerInterface $authChecker){

   $em = $this->getDoctrine()->getManager();
   $comment = $commentRepository->find($id);

    $user = $this->getUser();
    if ($user->getId() != $comment->getAuthor()->getId() && $authChecker->isGranted('ROLE_MODERATOR') == false ){
        throw exception_for("Cette page n'existe pas");
    }

   $em->remove($comment);
   $em->flush();
   $this->addFlash('comment_success', 'Commentaire supprimé avec succès');
   return $this->redirectToRoute('blog_show', array('id' => $articleId, 'slug' => $articleSlug));
}

在树枝上,我有这个链接:

<a href="{{ path('comment_delete', {'id': comment.id, 'articleId': article.id, 'articleSlug': article.slug}) }}">Supprimer</a>

我需要操作的评论ID,并且评论删除后,文章ID和文章条目才能重定向用户。

我检查删除评论的人是作者还是主持人。

但是,我听说这绝对不安全,因为我必须使用表单,但在这种情况下我真的不知道如何使用表单……或者也许是用JS隐藏最终用户的链接?

所以我想知道我的功能是否足够安全或是否存在更好的解决方案以及如何实现?

3 个答案:

答案 0 :(得分:1)

一种保护删除操作的方法是:


    <?php

    namespace App\Security\Voter;

    use App\Entity\User;
    use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    use Symfony\Component\Security\Core\Authorization\Voter\Voter;
    use App\Entity\Comment;

    class CommentVoter extends Voter
    {
        const CAN_DELETE = 'CAN_DELETE';

        protected function supports($attribute, $subject)
        {

            return in_array($attribute, [self::CAN_DELETE]) && $subject instanceof Comment;
        }

        protected function voteOnAttribute($attribute, $subject, TokenInterface $token)
        {
            $user = $token->getUser();
            // if the user is anonymous, do not grant access
            if (!$user instanceof User) {
                return false;
            }

            /** @var Comment $comment */
            $comment = $subject;

            switch ($attribute) {
                case self::CAN_DELETE:
                    return $this->canDelete($comment, $user);
            }

            throw new \LogicException('This code should not be reached!');
        }

        private function canDelete(Comment $comment, User $user)
        {
            if($user->getId() !== $comment->getAuthor()->getId() && $user->hasRole('ROLE_MODERATOR') === false) {
                return false;  
            }

            return true;
        }

    }

在您的用户实体中,hasRole方法可以类似于:

   /**
     * @param string $role
     */
    public function hasRole(string $role)
    {
        return in_array(strtoupper($role), $this->getRoles(), true);
    }
  • 在模板中,您可以执行以下操作:
{% if is_granted('CAN_DELETE', comment) %}
    <form action="{{ path('comment_delete', {'id': comment.id, 'articleId': article.id, 'articleSlug': article.slug}) }}" method="post">
       <input type="hidden" name="_csrf_token" value="{{csrf_token('delete_comment')}}" />
       <button>supprimer</button>
    </form>
{% endif %}

  • 最后在您的控制器中,您可以执行以下操作:

    /**
     * @Route("/blog/commentDelete/{id}-{articleId}-{articleSlug}", methods={"POST"}, name="comment_delete")
     */
    public function commentDelete($id, $articleId, $articleSlug, CommentRepository $commentRepository, EntityManagerInterface $em){

       $comment = $commentRepository->find($id);
       $csrfToken = $request->request->get('_csrf_token');

       if(!$this->isCsrfTokenValid('delete_comment', $csrfToken) || !$this->isGranted('CAN_DELETE', $comment){
           throw exception_for("Cette page n'existe pas");
       }

       $em->remove($comment);
       $em->flush();
       $this->addFlash('comment_success', 'Commentaire supprimé avec succès');
       return $this->redirectToRoute('blog_show', array('id' => $articleId, 'slug' => $articleSlug));
    }

在这里,您的delete方法受csrf令牌和表决器保护。 我认为这是解决方案的尝试。

答案 1 :(得分:0)

为了解决这类问题,我建议使用Symfony选民https://symfony.com/doc/current/security/voters.html

答案 2 :(得分:0)

您正在朝正确的方向前进。始终在后端进行验证和权限检查。

隐藏链接或使用表单并将其设置为禁用不会阻止使用开发工具的人将请求发送到您的控制器。我宁愿将前端检查视为对用户的一种便利-直接向他们显示某些数据无效/在进行请求之前不允许他们做某事。

我正在使用SensioFrameworkExtraBundle进行ROLE检查(我仍然不喜欢此类检查的注释.. hmm)-如果用户没有合适的控制器动作,则会抛出PermissionDeniedException。 像执行$user->getId() != $comment->getAuthor()->getId()

一样,可能需要执行以下进一步检查