Why sanitizing input is important

About

"Sanitizing" means cleaning, desinfecting or getting rid of unwanted stuff.
In this aspect it means "keeping users from inserting garbage into our application".
Most bugs from amateur programmers come from this specific problem, they expect every user to have good intentions and only type/send what is expected of them.
Not every user is good. Not every user will write a number in the age field of a form.

Example #1: NodeJS

app.get("/customer/:id", function(req,res){
  sqlPool.query("SELECT * FROM customer WHERE id="+req.params.id,
  function(err,response){
    if(err){
      res.status(500).send("ERROR!");
      return;
    }
    res.status(200).json(response[0]);
  });
});
This piece of code is from an NodeJS Express application. It returns the customer object from the database if a GET request was sent to /customer/1.

An example to exploit this bad code:
Instead of sending 3 simply send ' or 1=1; drop table customer; -- as the ID instead.

Explanation: ' or 1=1; will make the first statement return true, then drop table customer; deletes all data, and then -- will make the rest of the SQL statement a comment.

The most important fix for this: Do not let unfiltered user input get in a database query!
Either use so-called "Prepared Statements" or "escape" functions. An escape function with NodeJS' mysql module would be mysql.escape(string_variable).
These escape functions remove special characters from your string, making it safe to use in database queries.

Another, more simple thought to consider: What if the user doesn't send an ID, or sends a string as the ID?
This could be solved by first checking if something was sent, and then verifying that it was a number.
To do this you could use this code example:
app.get("/customer/:id", function(req,res){
  const id = req.params.id;
  if(!id){
    res.status(400).send("No ID provided.");
    return;
  }
  if(isNaN(parseInt(id))){
    res.status(422).send("ID is not a number.");
    return;
  }
  [...]
});
Now a user with bad intentions can't send SQL Injections or other weird stuff anymore. This also makes the application more stable, because not checking for wrong/missing input could throw errors later down in the code.

Example #2: gLUA

The bad code example in question is the following:

net.Receive("policeArmory",function(len,ply)
  local wepClass = net.ReadString()
  local player = net.ReadEntity()
  player:Give(wepName)
end)
I have found this code on a GMod server once. This is a piece of serverside code that enables players who work for the police to get weapons from the armory.
BUT: There are no security checks. Every user can send anything they want and get it. Users could even give other people weapons if they want, because the code takes a player via net messages (player) and not via the function (ply).

Example of an exploit for this code (clientside):
net.Start("policeArmory")
  net.WriteString("m9k_davycrocket")
  net.WriteEntity(LocalPlayer())
net.SendToServer()

To make this serverside code more robust we will add a few checks and use the ply object that the net.Receive function provides to us.
The extra security checks are: If the player is alive, valid, a police member and if the weapon he requested is allowed (and if he isn't spamming the system).
All together the code would now look like this:
local allowedWeapons = {
  ["weapon_m16"] = true,
  ["weapon_glock"] = true,
  ["weapon_mp5"] = true
}
net.Receive("policeArmory",function(len,ply)
  local wepClass = net.ReadString()
  if not IsValid(ply) then return end
  if not ply:IsPlayer() then return end
  if not ply:Alive() then return end
  if not (ply:Team() == "Police") then return end
  if not allowedWeapons[wepClass] then return end
  ply:Give(wepClass)
  if not ply.armory_timeout then
    ply:Give(wepClass)
    ply.armory_timeout = CurTime()+120
  else
    if CurTime() < ply.armory_timeout then return end
    ply:Give(wepClass)
    ply.armory_timeout = CurTime()+120
  end
end)
Ofcourse you don't have to do all of these checks. They have been split up to one per line to make it more readable.
The error message is not included here yet. Either create it clientside or send an error message in the serverside code above.
Now you can neither abuse the system nor cheat it anymore.

Conclusion

This can be a bit difficult to fully understand at first, but it gets easier the more you use it.
The most important thing to look out for is the connection to human input. You don't have to check if an internal function actually returns what it should be, but you have to check if a user sends the data you expect him to send.

A small checklist you could use for securing your application: