HEX
Server: nginx/1.28.0
System: Linux yisu-68a5f20334161 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User: www (1000)
PHP: 8.2.28
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/q.autos58.cn/wp-content/plugins/link-manager/includes/class-lm-security.php
<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class LM_Security {

    /**
     * Verify HMAC-SHA256 signature from the X-LM-Signature header.
     *
     * @param WP_REST_Request $request
     * @return bool
     */
    public static function verify_hmac( $request ) {
        $signature = $request->get_header( 'X-LM-Signature' );
        if ( empty( $signature ) ) {
            return false;
        }

        $secret = get_option( 'lm_hmac_secret', '' );
        if ( empty( $secret ) ) {
            return false;
        }

        $body    = $request->get_body();
        $expected = hash_hmac( 'sha256', $body, $secret );

        return hash_equals( $expected, $signature );
    }

    /**
     * Check if the given IP is in the allowed list.
     *
     * @param string $ip
     * @return bool
     */
    public static function check_ip_whitelist( $ip ) {
        $allowed = get_option( 'lm_allowed_ips', '' );
        if ( empty( $allowed ) ) {
            return true;
        }

        $list = array_map( 'trim', explode( ',', $allowed ) );
        $list = array_filter( $list );

        if ( empty( $list ) ) {
            return true;
        }

        return in_array( $ip, $list, true );
    }

    /**
     * Simple transient-based rate limiting.
     *
     * @param string $key
     * @param int    $max_requests
     * @param int    $window       Time window in seconds.
     * @return bool  True if within limit, false if exceeded.
     */
    public static function rate_limit( $key, $max_requests = 60, $window = 60 ) {
        $transient_key = 'lm_rate_' . md5( $key );
        $data          = get_transient( $transient_key );

        if ( false === $data ) {
            set_transient( $transient_key, array( 'count' => 1, 'start' => time() ), $window );
            return true;
        }

        if ( $data['count'] >= $max_requests ) {
            return false;
        }

        $data['count']++;
        $remaining = $window - ( time() - $data['start'] );
        if ( $remaining > 0 ) {
            set_transient( $transient_key, $data, $remaining );
        }

        return true;
    }

    /**
     * Combined request verification: rate limit + HMAC + IP whitelist.
     *
     * @param WP_REST_Request $request
     * @return true|WP_Error
     */
    public static function verify_request( $request ) {
        $ip = $request->get_header( 'X-Forwarded-For' );
        if ( empty( $ip ) ) {
            $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
        }

        if ( ! self::rate_limit( $ip ) ) {
            return new WP_Error(
                'rate_limit_exceeded',
                'Rate limit exceeded. Try again later.',
                array( 'status' => 429 )
            );
        }

        if ( ! self::verify_hmac( $request ) ) {
            return new WP_Error(
                'invalid_signature',
                'Invalid HMAC signature.',
                array( 'status' => 403 )
            );
        }

        if ( ! self::check_ip_whitelist( $ip ) ) {
            return new WP_Error(
                'ip_not_allowed',
                'IP address not in whitelist.',
                array( 'status' => 403 )
            );
        }

        return true;
    }
}