Changeset 756


Ignore:
Timestamp:
Nov 16, 2021 8:30:58 AM (2 years ago)
Author:
anonymous
Message:

Backport utility functions from v2.x

Files:
4 edited

Legend:

Unmodified
Added
Removed
  • branches/1.1dev/lib/App.inc.php

    r710 r756  
    413413 *                                   -false  <-- To not carry any queries. If URL already has queries those will be retained.
    414414 */
    415 function printHiddenSession($carry_args=null)
     415function printHiddenSession($carry_args=null, $include_csrf_token=false)
    416416{
    417417    static $_using_trans_sid;
     
    474474    if (!isset($_COOKIE[session_name()]) && !$_using_trans_sid) {
    475475        echo '<input type="hidden" name="' . session_name() . '" value="' . session_id() . '" />';
     476    }
     477
     478    // Include the csrf_token in the form.
     479    // This token can be validated upon form submission with $app->verifyCSRFToken() or $app->requireValidCSRFToken()
     480    if ($include_csrf_token) {
     481        printf('<input type="hidden" name="csrf_token" value="%s" />', getCSRFToken());
    476482    }
    477483}
     
    620626}
    621627
    622 /**
    623  * Force the user to connect via https (port 443) by redirecting them to
    624  * the same page but with https.
     628/*
     629* Generate a csrf_token if it doesn't exist or is expired, save it to the session and return its value.
     630* Otherwise just return the current token.
     631* Details on the synchronizer token pattern:
     632* https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern
     633*
     634* @access   public
     635* @param    bool    $force_new_token    Generate a new token, replacing any existing token in the session (used by $app->resetCSRFToken())
     636* @return   string The new or current csrf_token
     637* @author   Quinn Comendant <quinn@strangecode.com>
     638* @version  1.0
     639* @since    15 Nov 2014 17:57:17
     640*/
     641function getCSRFToken($force_new_token=false)
     642{
     643    if ($force_new_token || !isset($_SESSION['csrf_token']) || (removeSignature($_SESSION['csrf_token']) + 86400 < time())) {
     644        // No token, or token is expired; generate one and return it.
     645        return $_SESSION['csrf_token'] = addSignature(time(), null, 64);
     646    }
     647    // Current token is not expired; return it.
     648    return $_SESSION['csrf_token'];
     649}
     650
     651/*
     652* Generate a new token, replacing any existing token in the session. Call this function after $app->requireValidCSRFToken() for a new token to be required for each request.
     653*
     654* @access   public
     655* @return   void
     656* @author   Quinn Comendant <quinn@strangecode.com>
     657* @since    14 Oct 2021 17:35:19
     658*/
     659function resetCSRFToken()
     660{
     661    getCSRFToken(true);
     662}
     663
     664/*
     665* Compares the given csrf_token with the current or previous one saved in the session.
     666*
     667* @access   public
     668* @param    string  $user_submitted_csrf_token The user-submitted token to compare with the session token.
     669* @return   bool    True if the tokens match, false otherwise.
     670* @author   Quinn Comendant <quinn@strangecode.com>
     671* @version  1.0
     672* @since    15 Nov 2014 18:06:55
     673*/
     674function verifyCSRFToken($user_submitted_csrf_token)
     675{
     676
     677    if ('' == trim($user_submitted_csrf_token)) {
     678        logMsg(sprintf('Empty string failed CSRF verification.', null), LOG_NOTICE, __FILE__, __LINE__);
     679        return false;
     680    }
     681    if (!verifySignature($user_submitted_csrf_token, null, 64)) {
     682        logMsg(sprintf('Input failed CSRF verification (invalid signature in %s).', $user_submitted_csrf_token), LOG_WARNING, __FILE__, __LINE__);
     683        return false;
     684    }
     685    $csrf_token = getCSRFToken();
     686    if ($user_submitted_csrf_token != $csrf_token) {
     687        logMsg(sprintf('Input failed CSRF verification (%s not in %s).', $user_submitted_csrf_token, $csrf_token), LOG_WARNING, __FILE__, __LINE__);
     688        return false;
     689    }
     690    logMsg(sprintf('Verified CSRF token %s', $user_submitted_csrf_token), LOG_DEBUG, __FILE__, __LINE__);
     691    return true;
     692}
     693
     694/*
     695* Bounce user if they submit a token that doesn't match the one saved in the session.
     696* Because this function calls dieURL() it must be called before any other HTTP header output.
     697*
     698* @access   public
     699* @param    string  $message    Optional message to display to the user (otherwise default message will display). Set to an empty string to display no message.
     700* @param    int    $type    The type of message: MSG_NOTICE,
     701*                           MSG_SUCCESS, MSG_WARNING, or MSG_ERR.
     702* @param    string $file    __FILE__.
     703* @param    string $line    __LINE__.
     704* @return   void
     705* @author   Quinn Comendant <quinn@strangecode.com>
     706* @version  1.0
     707* @since    15 Nov 2014 18:10:17
     708*/
     709function requireValidCSRFToken($message=null, $type=MSG_NOTICE, $file=null, $line=null)
     710{
     711    if (!verifyCSRFToken(getFormData('csrf_token'))) {
     712        $message = isset($message) ? $message : _("Sorry, the form token expired. Please try again.");
     713        raiseMsg($message, $type, $file, $line);
     714        dieBoomerangURL();
     715    }
     716}
     717
     718/**
     719 * This function has changed to do nothing. SSL redirection should happen at the server layer, doing so here may result in a redirect loop.
    625720 */
    626721function sslOn()
    627722{
    628     global $CFG;
    629 
    630     if (function_exists('apache_get_modules')) {
    631         $modules = apache_get_modules();
    632     } else {
    633         // It's safe to assume we have mod_ssl if we can't determine otherwise.
    634         $modules = array('mod_ssl');
    635     }
    636 
    637     if ('on' != getenv('HTTPS') && $CFG->ssl_enabled && in_array('mod_ssl', $modules)) {
    638         raiseMsg(sprintf(_("Secure SSL connection made to %s"), $CFG->ssl_domain), MSG_NOTICE, __FILE__, __LINE__);
    639         // Always append session because some browsers do not send cookie when crossing to SSL URL.
    640         dieURL('https://' . $CFG->ssl_domain . getenv('REQUEST_URI'), null, true);
    641     }
    642 }
    643 
    644 
    645 /**
    646  * to enforce the user to connect via http (port 80) by redirecting them to
    647  * a http version of the current url.
     723    logMsg(sprintf('sslOn was called and ignored.', null), LOG_DEBUG, __FILE__, __LINE__);
     724}
     725
     726/**
     727 * This function has changed to do nothing. There is no reason to prefer a non-SSL connection, and doing so may result in a redirect loop.
    648728 */
    649729function sslOff()
    650730{
    651     if ('on' == getenv('HTTPS')) {
    652         dieURL('http://' . getenv('HTTP_HOST') . getenv('REQUEST_URI'), null, true);
    653     }
     731    logMsg(sprintf('sslOff was called and ignored.', null), LOG_DEBUG, __FILE__, __LINE__);
    654732}
    655733
  • branches/1.1dev/lib/Utilities.inc.php

    r754 r756  
    739739function hash64($string, $length=18)
    740740{
    741     $app =& App::getInstance();
    742 
    743     return mb_substr(preg_replace('/[^\w]/' . $app->getParam('preg_u'), '', base64_encode(hash('sha512', $string, true))), 0, $length);
     741    return mb_substr(preg_replace('/[^\w]/', '', base64_encode(hash('sha512', $string, true))), 0, $length);
    744742}
    745743
    746744/**
    747745 * Signs a value using md5 and a simple text key. In order for this
    748  * function to be useful (i.e. secure) the key must be kept secret, which
     746 * function to be useful (i.e. secure) the salt must be kept secret, which
    749747 * means keeping it as safe as database credentials. Putting it into an
    750748 * environment variable set in httpd.conf is a good place.
    751749 *
    752750 * @access  public
    753  *
    754751 * @param   string  $val    The string to sign.
    755  * @param   string  $key    (Optional) A text key to use for computing the signature.
    756  *
     752 * @param   string  $salt   (Optional) A text key to use for computing the signature.
     753 * @param   string  $length (Optional) The length of the added signature. Longer signatures are safer. Must match the length passed to verifySignature() for the signatures to match.
    757754 * @return  string  The original value with a signature appended.
    758755 */
    759 function addSignature($val, $key=null)
    760 {
    761     global $CFG;
    762 
    763     if ('' == $val) {
    764         logMsg(sprintf('Adding signature to empty string.', null), LOG_NOTICE, __FILE__, __LINE__);
    765     }
    766 
    767     if (!isset($key)) {
    768         $key = $CFG->signing_key;
    769     }
    770 
    771     return $val . '-' . substr(md5($val . $key), 0, 18);
     756function addSignature($val, $salt=null, $length=18)
     757{
     758    if ('' == trim($val)) {
     759        logMsg(sprintf('Cannot add signature to an empty string.', null), LOG_INFO, __FILE__, __LINE__);
     760        return '';
     761    }
     762
     763    if (!isset($salt)) {
     764        global $CFG;
     765        $salt = $CFG->signing_key;
     766    }
     767
     768    return $val . '-' . mb_substr(preg_replace('/[^\w]/', '', base64_encode(hash('sha512', $val . $salt, true))), 0, $length);
    772769}
    773770
     
    776773 *
    777774 * @access  public
    778  *
    779775 * @param   string  $signed_val     The string to sign.
    780  *
    781776 * @return  string  The original value with a signature removed.
    782777 */
    783778function removeSignature($signed_val)
    784779{
    785     return substr($signed_val, 0, strrpos($signed_val, '-'));
    786 }
    787 
    788 /**
    789  * Verifies a signature appened to a value by addSignature().
     780    if (empty($signed_val) || mb_strpos($signed_val, '-') === false) {
     781        return '';
     782    }
     783    return mb_substr($signed_val, 0, mb_strrpos($signed_val, '-'));
     784}
     785
     786/**
     787 * Verifies a signature appended to a value by addSignature().
    790788 *
    791789 * @access  public
    792  *
    793790 * @param   string  $signed_val A value with appended signature.
    794  * @param   string  $key        (Optional) A text key to use for computing the signature.
    795  *
     791 * @param   string  $salt       (Optional) A text key to use for computing the signature.
     792 * @param   string  $length (Optional) The length of the added signature.
    796793 * @return  bool    True if the signature matches the var.
    797794 */
    798 function verifySignature($signed_val, $key=null)
     795function verifySignature($signed_val, $salt=null, $length=18)
    799796{
    800797    // Strip the value from the signed value.
    801     $val = substr($signed_val, 0, strrpos($signed_val, '-'));
     798    $val = removeSignature($signed_val);
    802799    // If the signed value matches the original signed value we consider the value safe.
    803     if ($signed_val == addSignature($val, $key)) {
     800    if ('' != $signed_val && $signed_val == addSignature($val, $salt, $length)) {
    804801        // Signature verified.
    805         return true;
     802        return true;
    806803    } else {
     804        logMsg(sprintf('Failed signature (%s should be %s)', $signed_val, addSignature($val, $salt, $length)), LOG_DEBUG, __FILE__, __LINE__);
    807805        return false;
    808806    }
  • branches/1.1dev/polyfill/mysql.inc.php

    r699 r756  
    176176            try {
    177177                // Add instance
    178                 $this->_instances[$usePosition] = new Pdo($dsn, $username, $password, $flags);
     178                $this->_instances[$usePosition] = new PDO($dsn, $username, $password, $flags);
    179179
    180180                return $usePosition;
     
    211211            try {
    212212                $this->_params[$link]['databaseName'] = $databaseName;
    213                 return $this->mysql_query("USE {$databaseName}", $link);
     213                return $this->mysql_query("USE `{$databaseName}`", $link);
    214214            } catch (PDOException $e) {
    215215                return false;
     
    265265            static $last = null;
    266266
    267             if ($result === false) {
     267            if ($result === false || $result === null) {
    268268                trigger_error('mysql_fetch_*(): supplied argument is not a valid MySQL result resource', E_USER_WARNING);
    269269                return false;
  • trunk/polyfill/mysql.inc.php

    r719 r756  
    265265            static $last = null;
    266266
    267             if ($result === false) {
     267            if ($result === false || $result === null) {
    268268                trigger_error('mysql_fetch_*(): supplied argument is not a valid MySQL result resource', E_USER_WARNING);
    269269                return false;
Note: See TracChangeset for help on using the changeset viewer.