Reading an access log for an attack The log already contains the whole story. Six one-line commands turn a million rows into who, from where, and whether they got in. 5 min read Backups are a security control A backup on the same server, with the same credentials, is not a backup. It is a second copy waiting for the same event. 4 min read CSRF: a form submitted from somewhere else Your visitor is logged in, and another site makes their browser send a request to yours. One token per form stops it. 4 min read How to tell whether a site is compromised Most compromised sites look perfectly normal to their owner. The signals that are worth checking, and the four commands that find them. 5 min read fail2ban: what it stops and what it does not It reads logs and bans addresses that repeat a pattern. Useful, narrow, and easy to configure into banning your own office. 5 min read Permissions that are correct, not just working 777 makes the error go away and opens the site to anyone who can write a file. What the numbers mean and what a web site actually needs. 5 min read Cleaning a compromised site The order that gets a hacked site clean and keeps it clean - and the step almost everyone skips. 8 min read Hardening SSH Six lines of configuration turn the most-attacked service on your server into one that cannot be guessed at all. 4 min read Malware in the database, not the files You reinstalled the core, replaced every plugin, and the redirect is still there. It is in a row, not a file. 4 min read Ownership, umask and the group trap chmod 777 fixes the symptom and opens the door. Ownership and the group bit fix the cause, and keep new files correct. 5 min read Brute force on the control panel Thousands of login attempts a day is normal background noise. It only becomes a breach when one password is weak and nothing is counting. 4 min read Keeping PHP patched without breaking the site Staying on an unsupported PHP version is a slow security failure. Patch releases are safe; major versions need twenty minutes of preparation. 4 min read Why one backup is not a backup Ransomware looks for backups first, and finds the ones the server can reach. The 3-2-1 rule exists because of exactly that. 4 min read Rate-limiting a login page Password guessing is thousands of attempts an hour. A limit makes it useless, and it costs one block in the server config. 4 min read SQL injection, in plain terms The data was read as part of the question. One habit removes the whole class, and escaping is not it. 5 min read SSH keys instead of passwords A key ends brute-force attempts entirely. Generate one, install it, test it in a second window, and only then turn passwords off. 5 min read Two-factor, and where to keep the recovery codes It stops a stolen password being enough. The setup takes two minutes; the part people get wrong is what happens when the phone is lost. 4 min read The uploads directory must never execute Checking the file extension is not enough. If the web server will run PHP from your uploads folder, one bad upload is a shell. 4 min read The four things that get WordPress sites hacked Not obscure exploits. Four ordinary omissions account for almost every compromised site, and each takes minutes to close. 6 min read Cross-site scripting, in plain terms Someone else's code running on your page, in your visitor's session. One habit prevents almost all of it. 5 min read