diff --git a/admin/polls.php b/admin/polls.php
index ac5780ada51826f79787617bd397d9efdc8b8aa2..bf91aac3a9c74f6499051f0e64fed89c469ba249 100644
--- a/admin/polls.php
+++ b/admin/polls.php
@@ -20,6 +20,7 @@
 use Framadate\Services\AdminPollService;
 use Framadate\Services\LogService;
 use Framadate\Services\PollService;
+use Framadate\Services\SecurityService;
 use Framadate\Services\SuperAdminService;
 use Framadate\Utils;
 
@@ -39,18 +40,19 @@ $logService = new LogService();
 $pollService = new PollService($connect, $logService);
 $adminPollService = new AdminPollService($connect, $pollService, $logService);
 $superAdminService = new SuperAdminService($connect);
+$securityService = new SecurityService();
 
 /* PAGE */
 /* ---- */
 
-if (!empty($_POST['delete_poll'])) {
-    $delete_id = filter_input(INPUT_POST, 'delete_poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-z0-9]+$/']]);
+if (!empty($_POST['delete_poll']) && $securityService->checkCsrf('admin', $_POST['csrf'])) {
+    $delete_id = filter_input(INPUT_POST, 'delete_poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
     $poll_to_delete = $pollService->findById($delete_id);
 }
 
 // Traitement de la confirmation de suppression
-if (!empty($_POST['delete_confirm'])) {
-    $poll_id = filter_input(INPUT_POST, 'delete_confirm', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-z0-9]+$/']]);
+if (!empty($_POST['delete_confirm']) && $securityService->checkCsrf('admin', $_POST['csrf'])) {
+    $poll_id = filter_input(INPUT_POST, 'delete_confirm', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
     $adminPollService->deleteEntirePoll($poll_id);
 }
 
@@ -60,5 +62,6 @@ $polls = $superAdminService->findAllPolls();
 $smarty->assign('polls', $polls);
 $smarty->assign('poll_to_delete', $poll_to_delete);
 $smarty->assign('log_file', is_readable('../' . LOG_FILE) ? LOG_FILE : null);
+$smarty->assign('crsf', $securityService->getToken('admin'));
 
 $smarty->display('admin/polls.tpl');
diff --git a/app/classes/Framadate/Security/Token.php b/app/classes/Framadate/Security/Token.php
index 2f06afcaad8e7fa6296f103c01b21a9f057aa610..7222dbfa46d3f4318c172f7ff2ce457515009025 100644
--- a/app/classes/Framadate/Security/Token.php
+++ b/app/classes/Framadate/Security/Token.php
@@ -3,18 +3,32 @@ namespace Framadate\Security;
 
 class Token {
 
-    private $tokan_name;
     private $time;
     private $value;
 
-    function __construct($tokan_name, $time) {
-       $this->tokan_name = $tokan_name;
-       $this->time = $time;
-       $this->value = $this->generate();
+    function __construct() {
+        $this->time = time() + TOKEN_TIME;
+        $this->value = $this->generate();
     }
 
     private function generate() {
-        // TODO
+        return sha1(uniqid(mt_rand(), true));
+    }
+
+    public function getTime() {
+        return $this->time;
+    }
+
+    public function getValue() {
+        return $this->value;
+    }
+
+    public function isGone() {
+        return $this->time < time();
+    }
+
+    public function check($value) {
+        return $value === $this->value;
     }
 
 }
diff --git a/app/classes/Framadate/Services/SecurityService.php b/app/classes/Framadate/Services/SecurityService.php
index ea570e01a56bcd1dfde98de0955a1e0c3a5f3ba4..fc67a72b0d4b47d4492d0cb0fdbb8c8ccecd191e 100644
--- a/app/classes/Framadate/Services/SecurityService.php
+++ b/app/classes/Framadate/Services/SecurityService.php
@@ -8,12 +8,44 @@ class SecurityService {
     function __construct() {
     }
 
+    /**
+     * Get a CSRF token by name, or (re)create it.
+     *
+     * It creates a new token if :
+     * <ul>
+     *  <li>There no token with the given name in session</li>
+     *  <li>The token time is in past</li>
+     * </ul>
+     *
+     * @param $tokan_name string The name of the CSRF token
+     * @return Token The token
+     */
     function getToken($tokan_name) {
-        if (!isset($_SESSION['token']) || !isset($_SESSION['token'][$tokan_name])) {
-            $_SESSION['token'][$tokan_name] = new Token($tokan_name, 60*5);
+        if (!isset($_SESSION['tokens'])) {
+            $_SESSION['tokens'] = [];
+        }
+        if (!isset($_SESSION['tokens'][$tokan_name]) || $_SESSION['tokens'][$tokan_name]->isGone()) {
+            $_SESSION['tokens'][$tokan_name] = new Token();
+        }
+
+        return $_SESSION['tokens'][$tokan_name]->getValue();
+    }
+
+    /**
+     * Check if a given value is corresponding to the token in session.
+     *
+     * @param $tokan_name string Name of the token
+     * @param $csrf string Value to check
+     * @return bool true if the token is well checked
+     */
+    public function checkCsrf($tokan_name, $csrf) {
+        $checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf;
+
+        if($checked) {
+            unset($_SESSION['tokens'][$tokan_name]);
         }
 
-        return $_SESSION['token'][$tokan_name]->getValue();
+        return $checked;
     }
 
 }
diff --git a/tpl/admin/polls.tpl b/tpl/admin/polls.tpl
index c594caaa15810819e18eb77d28eea7d162c64ff4..1758f13ebb20edbc9c706f34f6feca97403da227 100644
--- a/tpl/admin/polls.tpl
+++ b/tpl/admin/polls.tpl
@@ -2,6 +2,7 @@
 
 {block 'admin_main'}
     <form action="" method="POST">
+        <input type="hidden" name="csrf" value="{$crsf}"/>
         {if $poll_to_delete}
             <div class="alert alert-warning text-center">
                 <h3>{_("Confirm removal of the poll ")}"{$poll_to_delete->id}"</h3>