Table of Contents
Introduction
If you run an Open Journal Systems (OJS) website, security should never be an afterthought. Journal websites handle submission files, reviewer data, editor communications, and author information: all of which need to stay protected.
OJS installations can become targets for automated exploits. In some cases, attackers modify core files like index.php to inject redirect scripts, plant backdoor plugins, or alter how the site behaves for search engine bots. These exploits can survive server migrations and even OJS version upgrades if the underlying security gaps are not addressed.
The good news is that most OJS security breaches can be prevented with a practical set of hardening steps. This guide covers the essential actions you can take to reduce risk without disrupting your journal’s daily operations.
Problem Overview
A documented case from the PKP Community Forum illustrates how persistent OJS security breaches can be. An OJS 3.3.0-22 installation had its index.php overwritten with a script that detected search engine bots: Googlebot, Bingbot, and others: and quietly redirected them to external spam websites while showing normal journal content to regular visitors and logged-in users.
This type of exploit, sometimes called cloaking, is difficult to notice because editors and administrators browsing the site normally see no visible change. The malicious code also planted an unauthorized plugin folder named “userss” inside the plugins/importexport/ directory, which generated PHP fatal errors when the admin plugins page was accessed.
What makes this case instructive is what did NOT stop the attack. The journal had already been moved from an older server running Ubuntu 18.04 and PHP 7.4 to a completely new server running Ubuntu 24.04, PHP 8.3, and Apache 2.4.58 with SSH access restricted to public/private key authentication only. The files_dir, where uploaded submission files are stored, was already placed outside the public web root. Server integrity scans using rkhunter, chkrootkit, and lynis all came back clean.
Despite all of these measures, the index.php was modified again. This points to the most likely entry vector: compromised OJS administrator or journal manager credentials. As PKP team members noted in the thread, privileged accounts with compromised passwords are a common and often overlooked attack vector. An attacker who obtains valid admin or manager credentials can modify files, install backdoor plugins, and create additional hidden accounts: all while leaving the server environment itself untouched.

Why This Happens
OJS, like any PHP web application, relies on the server environment for much of its security. However, even well-configured servers can be vulnerable if OJS-level access controls are compromised.
The most common entry points for OJS exploits include:

- Compromised administrator or journal manager credentials: an attacker with valid privileged account access can modify application files and install backdoors without triggering server-level alerts
- Outdated OJS versions that contain known vulnerabilities, including stored XSS attacks that can be used to escalate privileges when malicious JavaScript is presented to a logged-in administrator
- Unreviewed or incompatible third-party plugins installed from outside the official Plugin Gallery
- File permissions that are too permissive, allowing the web server user to write to application directories
- A files directory (files_dir) placed inside the web root where uploaded files can be accessed directly via a browser URL
- Server-level vulnerabilities unrelated to OJS itself
Once an attacker gains write access to the OJS directory, whether through compromised credentials, an XSS vulnerability, or another vector, they can modify core files like index.php, upload backdoor scripts, or create hidden plugin folders that execute malicious code. The exploit can then survive OJS version upgrades because the malicious files remain on disk unless they are specifically identified and removed.
Common Causes
- Compromised privileged credentials: Weak, reused, or leaked administrator or journal manager passwords are one of the most common attack vectors. Even with SSH key-only access and a hardened server, an attacker with valid OJS admin credentials can modify files through the application.
- Running outdated OJS releases: PKP regularly releases security patches. Staying on older versions, even within the same major release line, exposes the installation to known vulnerabilities, including stored XSS attacks present in earlier OJS 3.3.0-x releases.
- Unvetted third-party plugins: Plugins not sourced from the official PKP Plugin Gallery or not verified for compatibility with your OJS version can introduce security gaps.
- Overly permissive file permissions: If application directories are writable by the web server process, any vulnerability that allows file writes can lead to code modification.
- Files directory inside the web root: When files_dir is a subdirectory of the public web root, uploaded submission files can be accessed or executed directly through a URL.
- Server-level compromise: In some cases the initial intrusion happens at the web server or hosting account level, not through OJS itself.
Troubleshooting Steps

1. Check for modified or unexpected files
Use a file comparison tool like diff to compare your current OJS installation against a fresh download of the same version from the PKP website. Look for:
- Modified core files such as index.php, config.inc.php, or .htaccess
- New PHP files in unexpected locations
- Unknown plugin folders, especially in plugins/importexport/ or plugins/generic/
- Files with recent modification timestamps that you did not create
2. Review privileged user accounts
After a security incident, check your database for unauthorized administrator or manager accounts. Run these queries after backing up your database:
To check all site administrator user groups (should return only one result):
select * from user_groups where role_id=1;
To list all enrolled site administrators:
select u.* from users u join user_user_groups uug on (uug.user_id = u.user_id) join user_groups ug on (ug.role_id = 1 and uug.user_group_id = ug.user_group_id);
To list all journal managers:
select u.* from users u join user_user_groups uug on (uug.user_id = u.user_id) join user_groups ug on (ug.role_id = 16 and uug.user_group_id = ug.user_group_id);
Remove any accounts you do not recognize. Also enforce strong passwords and consider enabling two-factor authentication for all privileged accounts.
3. Check the journal_settings table for injected JavaScript
If your OJS installation was running an older 3.3.0-x release before being updated, stored XSS attacks may have left malicious JavaScript in the database. PKP lead developer Alec Smecher recommends reviewing the journal_settings table for unexpected script content that could be used to escalate privileges when presented to an administrator.
Run a query like:
select * from journal_settings where setting_value like '%<script%' or setting_value like '%javascript:%';
Review any matching rows carefully and remove entries that you did not add.
4. Move the files directory outside the web root
Your OJS files_dir, where submission files, article galleys, and other uploaded content are stored, should not be accessible through a web browser URL.
Configure this in config.inc.php:
files_dir = /var/www/ojs-files
Make sure this path points to a directory outside your public web root (outside public_html, htdocs, or www). After changing this setting, move the existing files to the new location, clear the OJS cache, and test that submissions can still be downloaded.
5. Set correct file permissions
Application files should be readable by the web server but not writable. Only directories that genuinely need write access, like cache, files_dir, and the public directory for temporary uploads, should be writable.
A safe starting point (adjust paths to your environment):
chown -R www-data:www-data /path/to/ojs
find /path/to/ojs -type d -exec chmod 755 {} \;
find /path/to/ojs -type f -exec chmod 644 {} \;
The config.inc.php file should be especially restricted: readable by the web server process but not writable and not accessible via browser URL.
6. Protect the public directory and sensitive files
If you cannot move files_dir outside the web root because of shared hosting restrictions, use .htaccess (Apache) or location blocks (Nginx) to prevent direct access to uploaded files within the files directory.
For Apache, add an .htaccess file inside the files_dir:
Deny from all
For Nginx, block access to config.inc.php:
location ~* config\.inc\.php { deny all; return 404; }
7. Reset passwords for all privileged accounts

If a security breach involved compromised credentials, change passwords for all administrator, journal manager, and editor accounts immediately. PKP provides a guide for resetting user passwords directly through the database if needed. Enforce strong password policies and consider using OJS’s built-in password reset mechanism to force all privileged users to create new credentials.
8. Keep OJS and plugins updated
Subscribe to PKP security announcements and apply updates promptly. As of this writing, the latest OJS releases include:
- OJS 3.3.0-x LTS line: 3.3.0-22
- OJS 3.4.0-x: 3.4.0-10
- OJS 3.5.0-x: 3.5.0-4

Only install plugins from the official Plugin Gallery and verify they are compatible with your specific OJS version using the PKP Plugin Compatibility tool.
9. Enable HTTPS and security headers
Serve OJS exclusively over HTTPS. Set force_ssl = On in config.inc.php. Add security headers to your web server configuration:
- Content-Security-Policy
- X-Frame-Options: SAMEORIGIN
- X-Content-Type-Options: nosniff
- Referrer-Policy: strict-origin-when-cross-origin
10. Monitor server logs
Regularly review your web server access logs and PHP error logs for unusual patterns. Look for:
- Repeated requests to unusual plugin paths such as /plugins/*/gabut/
- POST requests to unexpected URLs
- Access from unfamiliar IP addresses
- PHP warnings or fatal errors that reference unexpected file paths
Recommended Solution

If your OJS installation has been compromised, follow this recovery sequence:
- Take the site offline temporarily to prevent further damage and protect visitors.
- Back up your database and files_dir: keep a copy for forensic review and as a restore point for legitimate content.
- Identify the breach by checking modified files, unauthorized plugins, and suspicious admin accounts using the steps above. Check the journal_settings table for injected JavaScript if your installation was on an older 3.3.0-x release.
- Clean the installation by replacing the entire OJS codebase with a fresh download of the same version from the PKP website. Do not simply delete the malicious plugin: replace all application files to ensure no backdoors remain.
- Reset all privileged account passwords: administrators, journal managers, and editors: and remove any unrecognized accounts.
- Reconfigure security by moving files_dir outside the web root, setting correct file permissions, and protecting config.inc.php.
- Update OJS to the latest release in your version line.
- Test thoroughly before bringing the site back online.
If you are unsure whether the server itself was compromised, consider a full server reinstallation in a clean environment, migrating only the database and files_dir content, and reinstalling OJS and plugins from trusted sources.
What to Avoid
- Avoid simply deleting a malicious plugin or modified file without checking for additional backdoors: attackers often plant multiple entry points.
- Avoid upgrading OJS on top of a compromised installation without first replacing all application files from a trusted source.
- Avoid focusing only on server-level hardening while neglecting OJS account security: compromised admin credentials can bypass many server-level protections.
- Avoid granting write permissions to application directories that do not need them: restrict writable access to cache, files_dir, and public only.
- Avoid installing plugins from unverified sources or third-party websites: use the official PKP Plugin Gallery.
- Avoid assuming that moving to a new server will automatically fix a security breach if compromised credentials or database-level backdoors are carried over.
When to Ask for Technical Help
If you are not comfortable reviewing server logs, modifying file permissions, running SQL queries, or replacing the OJS codebase, ask for help from someone with server administration experience.
Consider reaching out for technical support if:
- You find modified files but cannot determine how the attacker gained access
- The breach recurs after you have cleaned and hardened the installation
- You suspect backdoors may exist outside the OJS codebase: attackers sometimes place malicious scripts in cron jobs, /tmp, or /dev/shm directories
- You are on shared hosting that restricts files_dir placement outside the web root
- You need help auditing privileged user accounts or reviewing the database for injected content
- The server itself may be compromised and needs forensic review
FAQ
How do I know if my OJS installation has been compromised?
Check for unexpected file changes: new or modified PHP files, especially in locations like index.php, the plugins directory, or the root folder. Look for new admin accounts you did not create, unexpected redirects when accessing journal pages, PHP errors referencing unknown plugin paths, and suspicious entries in server access logs.
Can I prevent exploits just by keeping OJS updated?
Keeping OJS updated to the latest release in your version line is essential, but it is not enough on its own. Breaches can occur through compromised privileged account credentials even when the server and OJS version are up to date. Security also depends on strong password policies, correct file permissions, placing files_dir outside the web root, restricting access to config.inc.php, and maintaining a secure server environment.
My files_dir is outside the web root: is that enough?
Placing files_dir outside the web root is an important layer of protection, but it is not sufficient by itself. An attacker who gains access through compromised administrator or manager credentials can still modify application files like index.php and plant backdoor plugins regardless of where files_dir is located. Account security and file permissions matter just as much.
Should I move to a new server after a security breach?
Not necessarily, but you should treat the installation as potentially compromised and replace the entire OJS codebase from a trusted source. If the breach involved server-level access or you find evidence of backdoors in system locations like cron jobs or /tmp, a clean server environment may be the safest path.
What is the safest way to configure the files directory?
Place files_dir outside the public web root so uploaded files cannot be accessed directly through a browser URL. If your hosting environment does not allow this, use .htaccess rules (Apache) or location blocks (Nginx) to deny direct access to the files directory. Also restrict the types of files that can be uploaded through OJS submission settings.
How often should I audit OJS user accounts?
Review privileged accounts: administrators, journal managers, and editors: at least every few months, and immediately after any suspected security incident. Remove accounts that are no longer needed, enforce strong password policies, and ensure that only trusted individuals have elevated access.
Conclusion
OJS security hardening is not a one-time task: it requires attention to both server-level configuration and application-level access controls. The most impactful steps include keeping OJS updated, enforcing strong password policies for privileged accounts, moving the files directory outside the web root, setting correct file permissions, protecting config.inc.php, and regularly auditing user accounts.
A well-configured server with files_dir outside the web root and SSH key-only access can still be compromised if an attacker obtains valid administrator credentials. Treat account security: passwords, access reviews, and auditing: with the same seriousness as server hardening.
If your installation has already been compromised, methodically clean, reconfigure, and harden before bringing the journal back online. The steps in this guide can help you recover and reduce the likelihood of future incidents.
Soft CTA
If your journal uses OJS and you need help with security hardening, server configuration, upgrades, or ongoing maintenance, the Open Journal Theme team can assist with reviewing your installation and applying best practices to keep your journal protected.
Internal resources:
References
This article draws on the following sources for additional context and verification:
- PKP Community Forum: Another security breach with OJS 3.3.0-22
- PKP Administrator Guide: Securing Your OJS Installation
- OWASP Foundation: Web Security Testing Guide
- PHP Security Guide: PHP Manual: Security
