• Resolved wp_kc

    (@wp_kc)


    I manage 11 websites; 5 hosted on two different VPSs, and the rest on a shared hosting account. All are running Debian 13, with php8.3. The two VPSs are set up with Fail2Ban with a custom script to look at Ninja Firewall’s log files and ban IPs with high or critical violations.

    Recently, I noticed that I stopped receiving notices from Fail2Ban of bans based on ninja firewall log entries. While debugging the issue, I found notices issued by WordPress’s debug logger saying there was an unhandled exception caused by Ninja Firewall because the plugin was trying to access a non-existent directory, /wp-content/session/.

    I created the directory on one of the servers, and started receiving Fail2Ban notices again soon afterward. When I checked my other web sites, only 2 out of the 11 sites had the session directory.

    It seems like something may have gone wrong in an update and that folder wasn’t created. I’m not sure if it is only affecting logging, or only happens when you are logged in as an admin, or if the exception is leaving sites completely unprotected. I suspect the exception is happening when logged in, otherwise the exception would probably take down the entire site.

Viewing 7 replies - 1 through 7 (of 7 total)
  • Plugin Contributor bruandet

    (@bruandet)

    Did you change the path of the firewall’s cache/log or the session folders (it can be changed using the .htninja, and it is also possible to change the session folder using the NFWSESSION_DIR constant) ?
    If you changed either one, it’s important to disable et reenable NinjaFirewall.

    By default, everything is stored inside wp-content/nfwlog, and has the following directory structure:

    wp-content/nfwlog/cache/ : cached files.
    wp-content/nfwlog/session/ : session files.

    Thread Starter wp_kc

    (@wp_kc)

    Thanks for the reply. I was not aware of that constant and have not changed it. I do see this in your code…

    /**
    * Select whether we want to use PHP or NF (default since v4.8.1) sessions.
    */
    if ( is_file( NFW_LOG_DIR .'/nfwlog/phpsession') ) {
    require_once __DIR__ .'/lib/class-php-session.php';
    } else {
    if (! defined('NFWSESSION_DIR') ) {
    /**
    * NFWSESSION_DIR can be defined in the .htninja.
    */
    define('NFWSESSION_DIR', NFW_LOG_DIR .'/session');
    }
    require_once __DIR__ .'/lib/class-nfw-session.php';
    }

    I did recently upgrade all my sites from php8.2-fpm to php8.3-fpm. Is it possible that the change in PHP versions messed it up somehow? Because I do see a /wp-content/nfwlog/session/ directory on my sites, and they have a bunch of session files with date-time stamps that end the day before I did the switch in PHP versions.

    • This reply was modified 2 months, 3 weeks ago by wp_kc.
    Thread Starter wp_kc

    (@wp_kc)

    I also see this…

        /**
         * Create session dir if it doesn't exist.
         * Note: NFWSESSION_DIR can be defined in the .htninja file.
         */
        if (! self::$session_dir ) {
            if ( defined('NFWSESSION_DIR') ) {
                self::$session_dir = NFWSESSION_DIR;
            } else {
                self::$session_dir = NFW_LOG_DIR .'/sessions';
            }
            if (! is_dir( self::$session_dir ) ) {
                $res = mkdir( self::$session_dir, 0700, true );
                if ( $res === false ) {
                    return false;
                }
            }
            touch( self::$session_dir .'/index.html');
        }

    I don’t think I have anything special in my security settings that would prevent mkdir() from working in the /wp-content/ folder. My directories are set to 755, and my files are set to 644. All files and directories are owned by the apache2 user, www-data.

    Plugins and themes be installed and uninstalled without a problem, so it seems that directories and files can be created and deleted without problems in /wp-content/plugins/ and /wp-content/themes/.

    There are six calls to NinjaFirewall_session::start(), but none of them handle a return value of false. It looks like this affects the login, logout, and captcha procedures.

    Plugin Contributor bruandet

    (@bruandet)

    Upgrading PHP shouldn’t affect the firewall, but could potentially affect file permission and ownership. But your files and folders look fine. What I don’t understand is why it want to write to /wp-content/session/ instead of wp-content/nfwlog/session/.

    -You can run the troubleshooter script: https://nintechnet.com/share/wp-check.txt
    -You can try to disable and reenable the plugin.
    -You can switch to PHP sessions if you want:
    1. Disable NinjaFirewall.
    2. Create a empty file named phpsession in the wp-content/nfwlog/ log folder: wp-content/nfwlog/phpsession
    3. Reenable NinjaFirewall.
    4. Go to NinjaFirewall > Dashboard and check if there’s a notice that says you’re using PHP sessions.

    @bruandet I have the same problem on all my websites. This is my analysis, which may help you reproduce and fix the error:

    Error Message

    PHP Fatal error:  Uncaught UnexpectedValueException: DirectoryIterator::__construct(/var/www/.../wp-content/session): Failed to open directory: No such file or directory in /var/www/.../wp-content/plugins/ninjafirewall/lib/class-helpers.php:29

    The Problem

    The NFWSESSION_DIR constant is defined inconsistently in two different files:

    1. In ninjafirewall.php (line 71) (incorrect):

    define('NFWSESSION_DIR', NFW_LOG_DIR .'/session');

    Since NFW_LOG_DIR = WP_CONTENT_DIR, this resolves to:

    wp-content/session ❌

    2. In lib/firewall.php (line 89) (correct):

    define('NFWSESSION_DIR', "{$nfw_['log_dir']}/session" );

    Since $nfw_['log_dir'] = wp-content/nfwlog, this resolves to:

    wp-content/nfwlog/session

    3. Additional inconsistency in lib/class-nfw-session.php (line 46):

    self::$session_dir = NFW_LOG_DIR .'/sessions';  // Note: 'sessions' with 's'

    This fallback would resolve to:

    wp-content/sessions (yet another different path!)

    How the Bug Manifests

    1. When the firewall is active: The firewall.php script runs first and correctly defines NFWSESSION_DIR as wp-content/nfwlog/session. Session files are created in the correct location.
    2. When the cron job runs: The garbage collector (nfwgccron) is triggered via WP-CLI or WordPress cron. In this context, ninjafirewall.php loads first (without the firewall), and defines NFWSESSION_DIR incorrectly as wp-content/session.
    3. The crash: The garbage collector in lib/utils.php (line 633) calls:
       $list = NinjaFirewall_helpers::nfw_glob( NFWSESSION_DIR, '^sess_', true, true );

    This attempts to iterate over wp-content/session/ which doesn’t exist, causing the DirectoryIterator to throw an UnexpectedValueException.

    Fix

    Primary Fix (ninjafirewall.php, line 71)

    Change:

    define('NFWSESSION_DIR', NFW_LOG_DIR .'/session');

    To:

    define('NFWSESSION_DIR', NFW_LOG_DIR .'/nfwlog/session');

    Secondary Fix (lib/class-nfw-session.php, line 46)

    For consistency, also change:

    self::$session_dir = NFW_LOG_DIR .'/sessions';

    To:

    self::$session_dir = NFW_LOG_DIR .'/nfwlog/session';

    Defensive Fix (lib/class-helpers.php, line 27)

    As an additional safeguard, add a directory existence check in the nfw_glob() function:

    public static function nfw_glob( $directory, $regex, $pathname = false, $sortname = true ) {
    
        $list = [];
    
        // Add this check to prevent crashes if directory doesn't exist
        if ( ! is_dir( $directory ) ) {
            return $list;
        }
    
        foreach ( new DirectoryIterator( $directory ) as $finfo ) {
            // ... rest of the function
        }
    }

    The same defensive check should be added to the nfw_glob_recursive() function in the same file.

    Steps to Reproduce

    1. Install NinjaFirewall 4.8.1
    2. Enable the firewall (this creates sessions in the correct directory)
    3. Wait for the garbage collector cron to run, or trigger it manually via WP-CLI:
       wp cron event run nfwgccron
    1. Observe the fatal error in the PHP error log
    Plugin Contributor bruandet

    (@bruandet)

    @gosuccess: Thanks for providing the log and details. I will push an update today.

    Thread Starter wp_kc

    (@wp_kc)

    Well done @gosuccess ! Thanks. And thanks @bruandet for rolling out the fix so quick.

Viewing 7 replies - 1 through 7 (of 7 total)

You must be logged in to reply to this topic.