Skip to content
Snippets Groups Projects
Select Git revision
  • 80a8a75ea6070650b86d4f6aa008bfb65b22f513
  • main default protected
  • step-3263
  • feature/plugins-cli
  • feature/vite
  • step-2484-peerreview
  • biest/issue-5051
  • tests/simplify-jsonapi-tests
  • fix/typo-in-1a70031
  • feature/broadcasting
  • database-seeders-and-factories
  • feature/peer-review-2
  • feature-feedback-jsonapi
  • feature/peerreview
  • feature/balloon-plus
  • feature/stock-images-unsplash
  • tic-2588
  • 5.0
  • 5.2
  • biest/unlock-blocks
  • biest-1514
21 results

DataFieldEntry.class.php

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    CSRFProtection.php 4.76 KiB
    <?php
    # Lifter010: DONE
    
    /**
     * CSRFProtection.php - protect from request forgery
     *
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License as
     * published by the Free Software Foundation; either version 2 of
     * the License, or (at your option) any later version.
     *
     * @author      mlunzena@uos.de
     * @license     http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
     * @category    Stud.IP
     */
    
    /**
     * To protect Stud.IP from forged request from other sites a security token is
     * generated and stored in the session and all forms (or rather POST request)
     * have to contain that token which is then compared on the server side to
     * verify the authenticity of the request. GET request are not checked as these
     * are assumed to be idempotent anyway.
     *
     * If a forgery is detected, an InvalidSecurityTokenException is thrown and a
     * log entry is recorded in the error log.
     *
     * The (form or request) parameter is named "security token". If you are
     * authoring an HTML form, you have to include this as an
     * input[@type=hidden] element. This is easily done by calling:
     *
     * \code
     * echo CSRFProtection::tokenTag();
     * \endcode
     *
     * Checking the token is implicitly done when calling #page_open in file
     * lib/phplib/page4.inc
     */
    class CSRFProtection
    {
        /**
         * The name of the parameter.
         */
        const TOKEN = 'security_token';
    
        const AJAX_TOKEN = 'HTTP_X_CSRF_TOKEN';
    
    
        /**
         * This checks the request and throws an InvalidSecurityTokenException if
         * fails to verify its authenticity.
         *
         * @throws MethodNotAllowedException      The request has to be unsafe
         *                                        in terms of RFC 2616.
         * @throws InvalidSecurityTokenException  The request is invalid as the
         *                                        security token does not match.
         */
        public static function verifyUnsafeRequest()
        {
            if (self::isSafeRequestMethod()) {
                throw new MethodNotAllowedException();
            }
    
            if (!self::checkSecurityToken()) {
                throw new InvalidSecurityTokenException();
            }
        }
    
        /**
         * @return boolean true if the request method is either GET or HEAD
         */
        private static function isSafeRequestMethod()
        {
            return in_array(Request::method(), ['GET', 'HEAD']);
        }
    
        /**
         * This checks the request and throws an InvalidSecurityTokenException if
         * fails to verify its authenticity.
         *
         * @throws InvalidSecurityTokenException  request is invalid
         */
        public static function verifySecurityToken()
        {
            if (!self::verifyRequest()) {
                throw new InvalidSecurityTokenException();
            }
        }
    
        /**
         * This checks the request and returns either true or false. It is
         * implicitly called by CSRFProtection::verifySecurityToken() and
         * it should never be needed to call this.
         *
         * @returns boolean  returns true if the request is valid
         */
        public static function verifyRequest()
        {
            return Request::isGet() || self::checkSecurityToken();
        }
    
        /**
         * Verifies the equality of the request parameter "security_token" and
         * the token stored in the session.
         *
         * @return boolean  true if equal
         */
        private static function checkSecurityToken()
        {
            return self::token() === ($_POST[self::TOKEN] ?? $_SERVER[self::AJAX_TOKEN] ?? null);
        }
    
        /**
         * Returns the token stored in the session generating it first
         * if required.
         *
         * @return string  a base64 encoded string of 32 random bytes
         * @throws SessionRequiredException  there is no session to store the token in
         */
        public static function token()
        {
            // w/o a session, throw an exception
            if (session_id() === '') {
                throw new SessionRequiredException();
            }
    
            // create a token, if there is none
            if (!isset($_SESSION[self::TOKEN])) {
                $_SESSION[self::TOKEN] = base64_encode(random_bytes(32));
            }
    
            return $_SESSION[self::TOKEN];
        }
    
        /**
         * Returns a snippet of HTML containing an input[@type=hidden] element
         * like this:
         *
         * \code
         * <input type="hidden" name="security_token" value="012345678901234567890123456789==">
         * \endcode
         *
         * @param array $attributes Additional attributes to be added to the input
         * @return string  the HTML snippet containing the input element
         */
        public static function tokenTag(array $attributes = [])
        {
            $attributes = array_merge($attributes, [
                'name'  => self::TOKEN,
                'value' => self::token(),
            ]);
    
            return sprintf(
                '<input type="hidden" %s>',
                arrayToHtmlAttributes($attributes)
            );
        }
    }