Don’t keep backups on your web server, even if you think they’re secret

August 10, 2018 Internet No comments

It’s good to keep backups of website’s HTML and other assets. A common way to do backups, if you’re not using some sort of version control system like Git, is to make a zip of the entire document tree. Usually it’ll just get called “” or maybe “” or whatever the current date is.

It’s a fine way to take a snapshot, but don’t leave it on your web server in your website’s document tree. The document tree is that folder where you upload the files, like /sites/mysite. If you make a zip or tarball or similar and leave it as /sites/mysite/, you’re asking for it to be stolen by bad guys. Maybe you’ve got PHP files in there that have secrets in them, like connection passwords to your database. Maybe you’ve got original work files like the .psd files that you created your .jpg files from. If you don’t want it seen, don’t put it in your document tree.

“No way, nobody knows it’s there”, you may think. You don’t link to the backup file anywhere, and there’s no directory listing on the server. This idea is called “security through obscurity”, and it’s not security at all. It turns out that the bad guys don’t have to know a file is there. They just have to make a lucky guess.

Here’s what brought this to mind: Today I was looking through the error log for a website I work on and noticed a series of 404s, where someone at the same IP address in China was asking for files that didn’t exist. Here are some of the files that this bot was looking for:


Pretty logical way to start, looking for likely filenames, in all three of .zip, .tar.gz and .rar formats. Then they wen’t looking for duplicates that had to have a sequence number added.

/web (2).zip
/web (2).tar.gz
/web (2).rar

Then they looked for variations on the site’s name ( for this example) and today’s date. Again, they’re trying all the archive formats, and now adding the .7z zip format.


Now they’re trying all sorts of variations on the filename, and they’re making guesses as to what a subdirectory might be called.


On Apache the document folder is often called public_html so that’s a good thing to try.


Maybe the target site uses underscores? It’s worth a shot.


And on and on it went, with all sorts of guesses at naming schemes. And why not? It’s free to make a guess. They tried all sorts of variations, like these examples:

/_example_com (2).zip
/example (2).zip

In under three minutes, the bot tried 307 different variations trying to find a backup file in my root directory. This is why security through obscurity doesn’t work. The bot doesn’t have to know, but just has to have a lucky guess.

Stay safe. Keep your backups out of the document tree, or better yet, off the web server entirely.

How to use templates in vim

July 13, 2018 vim No comments ,

For many kinds of files, when you create a new one from scratch, it would be handy to have part of the file created from a boilerplate template every time. For example, whenever I want to create a Perl file, it should start like this:


use warnings;
use strict;
use 5.010;

# vi:et:sw=4 ts=4 ft=perl

You can do this easily by putting this command in your .vimrc file:

au BufNewFile * silent! 0r ~/.vim/templates/%:e.tpl

What that’s saying is “Whenever vim opens a new, empty file, read in the contents of the file at ~/.vim/templates/%:e.tpl“, where %:e is the extension of the file you’re asking to open. So for an HTML standard file, I have this in ~/.vim/templates/html.tpl:

<!DOCTYPE html>
        <title> </title>

vi: expandtab sw=4 ts=4 ft=html:

The filename that it reads from is based on the extension of the file, not the filetype, so I have to have the same Perl template file in pm.tpl, pl.tpl and t.tpl, since all three are Perl extensions.

To find out more about how vim handles templates, see :help template.
I don’t know where I first discovered this technique, since I’ve had it in my .vimrc forever. Here’s another helpful summary:

ack 2.24 is released, speeds up common cases

June 21, 2018 ack No comments

I’ve just uploaded a new version of ack, version 2.24, to the CPAN and posted it to

This version of ack introduces an optimization to not search most files line-by-line until after ack has read the whole file and determined that the pattern matches in the file at all. This speeds things up quite a bit. Here are some timings in seconds for acking against the Drupal codebase. It compares ack 2.22, the just-released 2.24 and the latest beta of ack version 3:

                                    |   2.22 |   2.24 | 3 beta
          ack zqj /home/andy/drupal |   1.93 |   1.53 |   1.46
ack zqj-not-there /home/andy/drupal |   1.85 |   1.50 |   1.45
          ack foo /home/andy/drupal |   1.99 |   1.66 |   1.62
       ack foo -w /home/andy/drupal |   1.94 |   1.63 |   1.57
  ack foo\w+ -C10 /home/andy/drupal |   4.54 |   2.13 |   1.97
ack (set|get)_\w+ /home/andy/drupal |   2.01 |   1.79 |   1.79

Of course ack doesn’t have the raw speed of a tool like ripgrep, but it’s got a different feature set. See this feature comparison chart of greplike tools for details.

The best open source project for someone might not be yours, and that’s OK

January 2, 2018 ack, Open source 1 comment , , , ,

If you work on an open source project, consider helping your users by pointing them to other “competing” projects that might be better choices for them.

You may be familiar with ack, a grep-like code searching tool that I created in 2005. Over the years, a number of similar tools have come along, with the two most prominent being ag and ripgrep.  I just published a chart that compares the features of ack, ag, ripgrep, git grep and plain ol’ GNU grep.

Some might say that ag and ripgrep and any of the other tools I list on are competing projects, but I think that way of thinking is wrong.  It’s only a competition if you see it as a competition.  I’m not competing against anyone for anything: Clicks, dollars, popularity, etc. If someone uses ripgrep instead of ack, it doesn’t hurt me.  It’s the difference between an abundance vs. scarcity view of the world.  I choose abundance.  I think most of us who work in open source do, too.

When I created ack, I created a tool that was useful for me.  After a while, I figured that others might find it useful as well.  I released it publicly and people loved it.  People wrote things like “ack is certainly one of those simple pleasures of life on the command line”, and they told me it made their lives easier.  Great!  That’s why I released it.

Other people have told me “I don’t need that, I can just use standard Unix tools”.  That’s great, too, if that’s what you need.  Sometimes when they tell me that, it seems like they’re expecting me to argue or be upset.  But why would I?  It doesn’t matter to me what tool you use so long as you’re happy with it.

Now, other tools have sprung up that do grep-like things that fit different needs, and drop features their authors didn’t find necessary.  I love that there are so many tools out there.  Maybe some day someone will create a tool that fits my needs better than ack does.  If that day comes, I’ll pass ack off to someone else, and I’ll use the new tool, and I’ll love it.

If you have a project, you probably know about the other projects that exist better than anyone else.  You’re keeping an eye on similar projects to see what features they’re adding and what people are saying about them.  Now, take that knowledge and help others by sharing it with them.

It might mean that a potential user of your project uses someone else’s project, and that’s just fine if they do.  Happy users are why we should be doing this.

Do you know of other projects that do this sort of comparison chart, or otherwise help potential users learn about “competing” projects with honest comparisons?  Please let me know in the comments below, or email me at

Skip the exit interview when you leave your job

March 31, 2017 Job hunting, Work life No comments

When it’s time to leave your job, someone from Human Resources may want to sit you down and have an “exit interview”. They’ll ask you questions like “Why are you leaving your position?” and “What was it like to work with your manager.” It’s done with this premise that they’re looking to make the company better.

Don’t take the bait. Say nothing negative about anyone or anything. Tell them that you’ve found another opportunity that you need to take, and that it’s been a privilege to work with them all. And that’s it.

Here’s why: There is absolutely no benefit for you to gain by talking in an exit interview, and plenty of negative consequences to come out of it. At best you’ll be remembered as a complainer, and you may make enemies.

As I wrote in my book “Land The Tech Job You Love”

Once you’ve given your resignation, your goal until you leave the company is to be well remembered. You want people to say “Ol’ Steve, he was a good guy.” Leave graciously, and burn no bridges.

No matter how much you may enjoy the thought of telling your boss just how stupid he is, how poorly he runs the department, and how you can’t wait to hear how they’ll manage without you, keep it as a thought. There’s no good outcome of your actions, and they can only hurt you. Sending a long, rambling email telling your soon-to-be-ex co-workers about how you’re glad to be leaving is pointless and destructive.

People have long memories. We work in an extremely well-connected industry. Send a letter complaining about how terrible the company is, and that’s how you’ll be remembered. Everything good you’ve done will be eclipsed by your noisy departure. Chances are it will get around.

HR may assure you that everything that you say in your interview will be strictly confidential. This is a fiction or a fantasy, depending on how much the person in HR believes it. Plus, when you manager gets sent to sensitivity training a month after you leave, how hard is it for him to put two and two together?

Nobody wants to make an enemy. You may think you’ll never have to deal with the people you speak badly of again, but it’s an awfully small world out there.

Sometimes people say that “Well, how will the company get any better?” I suggest that that’s no longer your concern, and that the time for that has passed anyway. If you’re unhappy enough with the company to leave, then they’ve been doing things wrong for the duration of your employment.

What do you imagine will happen as a result of your exit interview? That the company that you need to get away from will magically pull its head of its ass? “Damn shame that Jenkins left, but I’m glad he told us about what a bad manager his boss was. Once we got rid of that guy, the world became a better place around here.” It won’t happen.

If you have suggestions on how things should work differently at the company, the time to tell your boss is while you work there. If they can’t bring themselves to act on it during your tenure, telling them at the exit interview isn’t going to do any good.

Again, the key here is that there is no potential upside for you. The best you can hope for is to not piss someone off. The potential negatives are great.

When the time comes for the exit interview, tell them you’ve found another opportunity that you need to take, and that you’re grateful for having had the chance to work at the company.  Be gracious and well-remembered as you leave.

How to talk in an interview about problems at your past job without coming off as a complainer

November 4, 2013 Interviews No comments

Someone on reddit asked “Why is talking bad about a previous job a taboo? What’s wrong with complaining? It’s showing dissatisfaction with the rules or environment. If I see a rule that’s unfair or inefficient, isn’t it in the company’s best interest to let them know in order to fix the issue?”

The problem with “showing dissatisfaction” is that it comes across as whining and complaining. You’re telling about how things stink, but not about what you’ve done to make things better. Managers hate complainers because when the complainer gets hired, the manager will have to spend time dealing with the complainer’s complaints. The complainer is likely to cause drama, and nobody has time for that.

However, it’s possible to take a negative at your past employer and turn it into a positive for you in the interview. If you can tell about how you have actually fixed an issue, that’s great. Tell that story.

For example, a common interview question is “Tell me about a project that didn’t go so well.” Here are two ways to answer that.

Bad: “Well, there was this one time that we had a big update to the app for a big trade show. Then, two weeks before the show, Director of Marketing comes and says ‘We have to change the color scheme’, and it was two solid weeks of redoing all the screens and re-testing and more than a few hours of overtime.”

This is complaining. Now, tell that story again in another way.

Good: “One time we had a big app update for a trade show, and two weeks before the show, Marketing decided to change the color scheme. It put is in a bit of a crunch, but we pulled it off and the show was a success. After the show, we looked at what happened and discovered that we weren’t communicating enough with the project stakeholders, and as a result we made sure that we had weekly updates to show project progress to the people that mattered.”

That is not complaining. That is describing a problem that came up and how you improved it as a result. That’s why you get hired: To do work and to solve problems.

A good interviewer will follow up with the “Tell me about a project that didn’t go well” question with “And what did you learn from the experience?” Don’t wait for that follow-up. Make your answer include what you learned. Then you’re not complaining.

Make the Linux chkconfig service list easier to read

October 4, 2013 Programming, Unix 1 comment , ,

If you run a Linux box, and you want to see what services start up at which level, you use runlevel:

$ chkconfig
acpid           0:off   1:off   2:on    3:on    4:on    5:on    6:off
atd             0:off   1:off   2:off   3:on    4:on    5:on    6:off
auditd          0:off   1:off   2:on    3:off   4:off   5:off   6:off
blk-availability        0:off   1:on    2:on    3:on    4:on    5:on    6:off
cpuspeed        0:off   1:on    2:on    3:on    4:on    5:on    6:off
crond           0:off   1:off   2:on    3:on    4:on    5:on    6:off
cups            0:off   1:off   2:on    3:off   4:off   5:off   6:off

Boy is that hard to read at a glance. All the “on” and “off” look very similar to each other, and that blk-availability service’s length screws up the tabbed columns. I decided I needed a better way, which I called cclist.

$ cclist
  2345  acpid
   345  atd
  2     auditd
 12345  blk-availability
 12345  cpuspeed
  2345  crond
  2     cups

Here’s the code to ~/bin/cclist:


open( my $fh, '/sbin/chkconfig --list |' ) or die "Can't open chkconfig: $!";

while (<$fh>) {
    if ( /^(\S+)(\s+\d:o(n|ff)){7}/ ) {
        my @cols = split;
        my $service = shift @cols;
        for ( @cols ) {
            my ( $level, $status ) = split /:/;
            print $status eq "on" ? $level : " ";
        print "\t$service\n";
    else {

Those “illegal job interview questions” aren’t actually illegal

September 22, 2013 Interviews No comments , ,

It’s common knowledge that it’s illegal for US employers to ask about your age, sex, religion, marital status, national origin, or other protected statuses. Thing is, it’s not illegal for them to ask.  It’s illegal for them to discriminate, but it’s not illegal to ask. Still, the idea of the “illegal interview questions” is a common one. Search for “illegal interview questions” on Google and you’ll get 50,000 hits. Lots of blog posts and news articles, but nothing from anyone I see as a legal authority.

I’m certainly not a lawyer, but I feel confident in quoting the US Equal Employment Opportunity Commission’s website for this (emphasis mine):

As a general rule, the information obtained and requested through the pre-employment process should be limited to those essential for determining if a person is qualified for the job; whereas, information regarding race, sex, national origin, age, and religion are irrelevant in such determinations.

Employers are explicitly prohibited from making pre-employment inquiries about disability.

Although state and federal equal opportunity laws do not clearly forbid employers from making pre-employment inquiries that relate to, or disproportionately screen out members based on race, color, sex, national origin, religion, or age, such inquiries may be used as evidence of an employer’s intent to discriminate unless the questions asked can be justified by some business purpose.

Short version: It’s not illegal to ask those questions, but it’s stupid to do so because it gives the candidate ammo to use in a lawsuit against you.

I think it’s an important distinction. I read plenty of comments on reddit and the like where people seem to think that being asked about their marital status is somehow going to get them a job because that’s an illegal question. It’s not. Other than feeding one’s sense of righteous indignation, there’s not much that will probably come out of being asked an “illegal question.” There has to be a lawsuit for anything to come of it, which means you need to find a lawyer who thinks that you can win a discrimination suit, because the lawyer will be able to prove discrimination.

The key is that you have to prove that you were discriminated against. Simply saying “They asked me an illegal question” isn’t enough. Here’s what a random employment law firm’s website says:

An employee in an employment discrimination and wrongful termination case must prove that the reason he or she was fired, or not hired or not promoted, is because of his or her “protected classification.” In other words, you have to prove that you were denied employment or a promotion because of your race, gender, ethnic background, age or other discriminatory factor.

It’s also important to know that in addition to the Federal laws, you may have rights in other states. For example, some states forbid discrimination based on sexual orientation, while many others do not (yet). Search Google for “discrimination laws [your state]” and you should get hits for your state’s government agency that covers this topic.

None of this is to endorse employers asking such questions. A good employer shouldn’t ask you any questions that aren’t related to the job.

What to do if you’re asked a question that gets at something discriminatory? Check out this article on how to handle bad interview questions.

The simple math of why your resume probably isn’t getting read.

September 13, 2013 Job hunting, Resumes No comments ,

You spend hours slaving over your resume, crafting every word of every bullet point, and yet you’re getting no interest from the companies you send the resume to.  Maybe your problem is that you’re ignoring the most important part of your resume: The first half-page, or the first screenful.

Let’s do some simple math here.  Last time I posted job ads for programmers I was getting 300-400 responses per ad, so let’s say conservatively that a job posting nets a hiring manager 250 resumes. If he spends 10 minutes on each resume, examining each in detail, that comes out to:

250 resumes x 10 mins/resume  = 2500 minutes = 41.2 hours

That’s one entire work week doing absolutely nothing but reading those resumes 8 hours a day.  That’s not going to happen.

Much more realistic is for the reader to spend maybe a minute on each resume determining which ones are obviously crap, and which ones have potential and get put aside into a pile for closer consideration.

250 resumes x 1 min/resume  = 250 minutes = 4 hours

That’s much more manageable.  Now the hiring manager is able to set aside the 5-10% of the resumes that are not clearly garbage, or shotgunned to everyone, or from offshore consulting firms offering their services.

So you have at most a minute of actual reading time, max.  I’ve seen the claim of 10-20 seconds per resume commonly cited, too.

What does this mean to you, the resume writer?

Nobody is going to read past the first half-page of your resume unless you give them a reason to read the rest.

Think of the top half of your resume as a movie trailer, a teaser for what’s in the rest of the movie.  You want that top half-page to put all the best about you out front.   You’re going to start with a summary of what’s to follow, such as:

  • Six years experience system administration for 20-30 Linux and Windows servers.
  • Fully certified as both Red Hat Something Something and Windows Certified Blah Blah.
  • Extensive experience with backup strategies to physical media and offsite solutions.

In three lines, you’ve summarized who you are and given the reader reason to read the rest.  Yes, it is redundant to what’s in the rest of the resume, but that’s OK, because (and I know I’m repeating myself) nobody is going to read your entire resume unless they have a reason to.

The top half-page of your resume is so crucial it’s why an objective is absolutely the worst way to start a resume.  Consider a typical resume objective:

JOB TARGET: My goal is to become associated with a company where I can utilize my skills and gain further experience while enhancing the company’s productivity and reputation.

There is absolutely nothing in that to make the reader want to read further. Everything is about what the writer wants, not what she can bring to the company. That resume is bound for the reject folder.

You have less than half a minute to convince the reader to read your entire resume.  Make the first part of your resume tell all the important stuff, and only the important stuff.

My bash prompt with git/svn branch+status display

April 24, 2013 Unix 2 comments , , , ,

After spending a few hours last night switching between three different branches in the ack2 project, and typing “git br” over and over, I decided I needed to put branch status in my bash prompt. The only question was: Which one would I steal? Fortunately, Rob Hoelz was online and I mentioned it to him and he handed me his, so I stole it and also added Subversion support as well.


# Adapted from from

function __prompt
    # List of color variables that bash can use
    local BLACK="\[\033[0;30m\]"   # Black
    local DGREY="\[\033[1;30m\]"   # Dark Gray
    local RED="\[\033[0;31m\]"     # Red
    local LRED="\[\033[1;31m\]"    # Light Red
    local GREEN="\[\033[0;32m\]"   # Green
    local LGREEN="\[\033[1;32m\]"  # Light Green
    local BROWN="\[\033[0;33m\]"   # Brown
    local YELLOW="\[\033[1;33m\]"  # Yellow
    local BLUE="\[\033[0;34m\]"    # Blue
    local LBLUE="\[\033[1;34m\]"   # Light Blue
    local PURPLE="\[\033[0;35m\]"  # Purple
    local LPURPLE="\[\033[1;35m\]" # Light Purple
    local CYAN="\[\033[0;36m\]"    # Cyan
    local LCYAN="\[\033[1;36m\]"   # Light Cyan
    local LGREY="\[\033[0;37m\]"   # Light Gray
    local WHITE="\[\033[1;37m\]"   # White

    local RESET="\[\033[0m\]"      # Color reset
    local BOLD="\[\033[;1m\]"      # Bold

    # Base prompt
    PS1="$LCYAN\h:$YELLOW\w$LCYAN \\\$$RESET "

    local dirty
    local branch

    # Look for Git status
    if git status &>/dev/null; then
        if git status -uno -s | grep -q . ; then
        branch=$(git branch --color=never | sed -ne 's/* //p')

    # Look for Subversion status
        svn_info=$( (svn info | grep ^URL) 2>/dev/null )
        if [[ ! -z "$svn_info" ]] ; then
            branch_pattern="^URL: .*/(branch(es)?|tags)/([^/]+)"
            trunk_pattern="^URL: .*/trunk(/.*)?$"
            if [[ $svn_info =~ $branch_pattern ]]; then
            elif [[ $svn_info =~ $trunk_pattern ]]; then
            dirty=$(svn status -q)

    if [[ ! -z "$branch" ]]; then
        local status_color
        if [[ -z "$dirty" ]] ; then
        PS1="$LCYAN($BOLD$status_color$branch$LCYAN)$RESET $PS1"

if [[ -z "$PROMPT_COMMAND" ]]; then

Just drop that into your ~/.bash directory as, and then add

source ~/.bash/

to your .bashrc. Now you have color-coded branch names: red for dirty, green for clean.