Arrow

File change notifications for your WordPress blog on Linux

First published on June 9, 2008

Given the popularity of WordPress, it is regularly targeted by hackers. Even if you upgrade your blog immediately when a new version is released, your site might already have been hacked. Therefore, if there are known WordPress vulnerabilities, old or otherwise, your blog is susceptible to being exploited.

There are two main places for hackers to target on your blog: the files and the database. For them to run arbitrary code on your site (instead of just editing your content, which is mostly in the database) they usually have to hack the files.

(As an aside, for database changes, you could look into MySQL binary or master logs, although you usually need full access to your server to look into these. Also, if you have full access you should also look into more intense protection systems as suggested by Donncha.)

A lightweight way to be on the alert of potential hacks is to get notified whenever a file on your site is changed. Every hour, or more or less frequently depending on your needs, you can run a script (via cron or WP-Crontrol) to e-mail yourself a list of all of the files that have been changed since the last check. (If no files have been changed, no e-mail is sent.) If you or someone else that you know was making edits to the files, then you can just continue with your day. However, if unexpected files have been modified, you can investigate further.

The e-mail would read something like this:

Files modified in the last hour:

/path/to/your/folder/config.php
/path/to/your/folder/plugins/antispam.php
/path/to/your/folder/plugins/countdown.php

Here is some quick code that checks whether any files (including all sub-directories) have been changed beneath a certain folder within the last hour and if so, it e-mails the results to a specified address. The location of the script itself on your server is not important — it just needs to be run periodically. Also, it assumes that you are running Linux and can run the exec function in PHP:

<?php

/*
This file e-mails you a list of all modified files in a certain directory
Run this file via cron every hour

"Find" shell command code syntax from:

http://www.mydigitallife.info/2006/01/19/find-files-that-are-modified-today-or-since-certain-time-ago-in-unix/

and from:

http://linux.about.com/od/commands/l/blcmdl1_find.htm

*/

// Shell command that finds all files below a certain directory that modified within the last 62 minutes
// Replace the file path (absolute or relative to this script's location) as necessary
exec('find /path/to/your/folder -cmin -62 -print', $last_changed);

// Only e-mail the results if anything has changed
if ( count ( $last_changed ) > 0 ) {

    // E-mail settings
    $sendto = "E-mail receiver <receiver@yoursite.com>";
    $sendfrom = "File change script <dummyaddress@yoursite.com>";
    $sendsubject = "yoursite.com file change notice";

    // Results of files last modified
    $email_output = 'Files modified in the last hour:';
    $email_output .= "\n";
    $email_output .= "\n";
    $last_changed_files = implode ( "\n", $last_changed);
    $email_output .= $last_changed_files;

    // Mail the file
    // You can also use the PEAR Mail package (http://pear.php.net/package/Mail) or a similar script for more robust mailing

    // Line break, which we will used for the headers
    $send_eol = "\r\n";

    $send_headers = 'From: ' . $sendfrom . $send_eol;
    $send_headers .= 'Reply-To: ' . $sendfrom . $send_eol;
    $send_headers .= 'Return-Path: ' . $sendfrom . $send_eol;

    // Send!
    mail($sendto, $sendsubject, $email_output, $send_headers);
}
?>

If you want to exclude or include certain folders and/or files from the results (for example, exclude a cache folder, since changes to that folder would happen frequently and are usually not the target of hackers), you can use the “prune”, “name”, “regex”, “iregex”, or “path” parameters of the “find” Linux command.

This code can also be applied to any folder, so it’s not just for WordPress installations!

June 17, 2008 update: see a Joomla discussion forum thread about this, which includes some tips on providing more information about the files that have changed.

Arrow

35 Responses to “File change notifications for your WordPress blog on Linux”


  1. Fred says:

    Great, the only thing to make it better would be a bit of logic to suppress the email if nothing had changed.


  2. Peter says:

    It does have that logic. If no files have been changed, no e-mail is sent. Sorry, I have now made that more clear in the post.


  3. Fer says:

    I know this may be a silly question, but since I’m bad at coding I have a question: should I put my e-mail address in here ($sendto = "E-mail receiver ";) or it just sends the email to the webmaster[at]example.com?


  4. Peter says:

    Hi Fer, yes, use this syntax:

    $sendto = ‘Fer Lastname <fer@dabomb.com>’;


  5. Fer says:

    Thank you! And thank you for modifying the code too, there’s too many of us around the net and we get annoying when when can’t figure out those ‘simple’ things :P


  6. Michelle says:

    Thanks for this code – works like a charm for Joomla as well. Just wondering what the language around excluding files and folders is – I’ve been rooting around the about.com page on the find command but I don’t get how to use that exclude files or folders from the primary exec command.


  7. Peter says:

    Hi Michelle, it’s a Linux command, the options for which I’m still learning :D

    See this link for some examples.

    Here is an example that ignores all files named “error_log” (no matter which directory it’s in) and also ignores all files in a specific directory named “peter”):

    exec(‘find /path/to/monitored/folder -name error_log -prune -o -path \’/path/to/montored/folder/peter\' -prune -o -cmin -62 -print’, $last_changed);


  8. Sandy says:

    Thanks so much for the great tip. I’d like to give it a try but I could use a little more info. please. What do I do exactly? Copy this script and paste it into notepad? … change the $sendto = field … okay, then what? Do I save the file as anything (example: notify.php), and then upload it to the root directory of my blog? Thanks so much for your assistance.


  9. Peter says:

    You will have to look some of this stuff up, but the basic steps are:

    1) Copy the code to a text file.
    2) Change the file path in the "exec" line to reflect the absolute path to your blog (and possibly to exclude certain sub-folders or files if needed).
    3) Configure the three lines under the e-mail settings.
    4) Save the file as, say, updatecheck.php and place it anywhere on your server.
    5) Set up a cron job on your server to run that file every hour.


  10. David Potter says:

    Cool idea. You should turn this into a WordPress plugin. Adding an admin page to set the email address, etc. would be icing on the cake so that it wouldn’t have to be set in the php file. It would also be nice to have a version that works on Windows.


  11. Peter V Cook says:

    Now if this could be turned into an RSS feed that would be awesome!


  12. sushimaniac101 says:

    Uh, yes… I’m not sure if my blog is being hacked because the pictures that I have won’t load and I’m not sure if my blog is overloading. Please help!!!


  13. kamal says:

    Yes, agree with David Potter, your code will be more and more useful if u turn it into a wp plugin.
    thanks


  14. Peter says:

    Regarding the issue of making this into a WordPress plugin, I think that defeats the purpose. Ideally, this file would be placed outside of the WordPress directory. If it were within your WordPress directory and your installation was modified without your consent, then this file might not run properly in order to alert of about what happened.


  15. Hauke says:

    Very important when checking .htaccess – make sure that you scroll right to the end of the file. If you open a file in notepad it might look as if there are only a few lines of code, but if you scroll down to the end of the file you might get a surprise.


  16. Avishai says:

    Thanks for the script. We cannot run exec command. Is there a way to get the job done using another command?

    Thanks,
    Avishai


  17. Peter says:

    Hi Avishai, under the current framework, you do need the “exec” or “system” command. To check file modification times, you could use some file-related PHP functions. However, you’d have to use PHP logic to loop through every file in every directory — possible but not very efficient.


  18. Avishai says:

    Thanks, i’ll check them out. Do you think that the enabling the exec command posses a significant security breach? Anyway, i’m now playing with some cronjobs to see if they can also be used to notify, but dealing with them is much less comfortable (for me…) than PHP


  19. Peter says:

    I would say that it is a security risk, but it can certainly be mitigated. If someone exploits a vulnerability in one of your scripts (or someone else’s if you’re on a shared hosting environment) and can run the exec command, they can probably do more. However, that all depends on the privileges that the PHP user is given. I’m no expert on this though.


  20. crystal says:

    Thank, but I can’t get it to work. I changed all the necessary files. I have the Cron Job set up and the job is running fine, but it supplies an email with this sh: /usr/bin/find: Permission denied

    Do you know what’s happening?? Thanks in advance, I can’t WAIT to get it working. :)


  21. crystal says:

    uhh lets try that again.

    Thanks with an s.. and I changed all the necessary lines IN the php file.
    The cron job is running perfectly, but I get this in the log/results email ( the one from the server) sh: /usr/bin/find: Permission denied

    Sorry for the typo’s.. it’s late.


  22. Peter says:

    Hi crystal, are you on a shared server? It seems as though the "find" command is not allowed to be run as an executable for the user account that is running the script. I don’t know the specific steps to take without further research, so you should contact your web host or whoever is managing the server, or paste that error into Google.


  23. crystal says:

    Yes I am on a shared server. Thanks for the reply and the tips. I did talk to them a day or so ago but will be calling them back soon. :)


  24. jidanni says:

    One line is all it takes:
    $ cat .crontab
    11 * * * * find wordpress -type f \( -mmin -62 -o -cmin -62 \)
    $ crontab .crontab
    If there is no matches, no mail will be sent.


  25. Vadim says:

    My hosting doesn’t allow to the exec() command to be executed, what can I do to make it work?
    Thanks,
    Vadim


  26. Peter says:

    Hi Vadim, please see this comment and the couple of comments following that. In short, your options are limited. I invite people to share alternative solutions if they have any!


  27. John says:

    Hi Peter. Excellent article. So just to make sure I got this straight.

    What I do is:

    1. Edit the first part of the line
    <code>exec(‘find /path/to/your/folder -cmin -62 -print’, $last_changed);</code>

    to <strong>find /</strong> if my WP install is in my root

    2. Enter in my email

    3. Save this file as a file name with a php extension and upload it to my server.

    4. Set up a cron job to run this script every hour

    Is that basically what I need to do?
    Thanks

    Reply from Peter: correct, although in the first step, I would put “find . -cmin…” if you are placing the script in the root of your WordPress directory; that or put the absolute path to the WordPress directory. After that, I would test it by running the script directly by printing $email_output to the screen.


  28. Eugene says:

    Hello,

    I get the following error messages when the cron runs:

    "Cannot bind/listen socket – [2] No such file or directory.
    Couldn’t create FastCGI listen socket on port lic_html/test.php"

    Test.php is the name of the file which runs this script. I have contacted HostGator’s technical support and they told me that "You cannot create sockets on our servers through php. You would need to find a script which does not use sockets and then try again."

    Am I doing something wrong or are they correct about the script trying to create sockets. If they are correct, could you please post a workaround for this? When it comes to Unix, I am completely cluess, sorry.

    Thanks for your help.

    Reply from Peter: Sorry, I’m stumped there. If anybody has an explanation or solution, please post it as a comment.


  29. Mykhailo M says:

    Hi, I have noticed today someone had added three extra .php files to my Word press root directory, obviously someone was able to pass through my Plug-in "Secure WordPress 2.0.0" and my complicated Password. Really strange for me to expect that to happen, any ideas where the "Back DOOR" may exist?

    Regarding your plug-in, if someone would add an extra file or folder can your plug-in also send an e-mail notification or would you know which one can do it?

    Many Thanks for helping

    PS. these hackers have nothing else better to do than bother lives of other people who have enough of their own problems to take care of.

    Reply from Peter: Yes, that is what my plugin is designed to do — notify you of file changes such as new files. However, I don’t know what the cause could have been. On an ongoing basis, there are security holes discovered with WordPress, so it’s important to keep your install up to date. The breach could have also been outside of WordPress, by gaining direct server access or access through another site on the server.


  30. Mykhailo M says:

    Thanks Peter, Could you please clarify the installation of your code, after saving it in let say example.txt (WordPad) file how can we set up a cron job on your server?

    Where should we look in order to do it?
    And as I understand the file "example.txt" should be in our Word Press root directory.

    Many Thanks

    Reply from Peter: The example code is in PHP, so you would name it something like “file_modifications_check.php”. You can place it anywhere on your server, as long as the cron job runs it, and you have an absolute path to your WordPress root in the very first “exec” code call (look for /path/to/your/folder). As for setting up the cron job, it really depends on your server. The post includes some links to general cron information. If your host has a control panel, you can probably set up cron jobs through that. If you have access to the command line, you can bring up the crontab for that user with “crontab -e”. Good luck!


  31. Michelle says:

    Well I’m back – my script ran like a charm for years and now with PHP 5.3, I get bumpkiss. Does anybody have ideas for how to make this script compliant with PHP 5.3?

    Reply from Peter: There’s nothing in the script that should conflict with PHP 5.3. However, along with the upgrade to 5.3, your web host might have disabled access to the “exec” PHP command.


  32. Chris says:

    This is exactly what I was looking for! Works perfect, thx a bunch for this!! ^_^


  33. John S says:

    Just to add my own little tweak here – the line below does the following:
    - Excludes a bunch of directories (5 in this example, -o basically means "OR")
    - Looks for modified files in the last 24 hours (parameter -mtime)
    - Outputs full path, date, and details of modified files

    exec(‘find /home/USER/public_html -type d \( -name "cache" -o -name "images" -o -name "testsite" -o -name "blog" -o -name "temp" \) -prune -o -type f -mtime 0 -exec ls -l {} \;’, $last_changed);


  34. Stace Johnson says:

    John S., I tried your modified code, but I’m getting a parse error on that line:

    Parse error: syntax error, unexpected T_STRING in /home/USER/public_html/update_check.php on line 22

    Not sure what the issue could be, unless it has something to do with encoding. I’m on a Windows box, but the server is running some flavor of Linux.


  35. Mohamed says:

    @Stace Jhonson, the error is due to the sing quote ‘. Try to delete and type the single quote. It works.

Speak your mind

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word