Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

A SQL command to authenticate a user might take the form:

 

Code Block
SELECT * FROM Users WHERE userid='<USERID>'
AND                             
 AND 
                            password='<PASSWORD>'

If it returns any records, the user ID and password are valid.

However, if an attacker can substitute arbitrary strings for <USERID> and <PASSWORD>, they can perform a SQL injection by using the following string for <USERID>:

 

Code Block
validuser' OR '1'='1

When injected into the command, the command becomes:

 

Code Block
SELECT * FROM Users WHERE userid='validuser' OR '1'='1' AND password=<PASSWORD>

If validuser is a valid user name, this SELECT statement selects the validuser record in the table. The password is never checked because userid='validuser' is true; consequently the items after the OR are not tested. As long as the components after the OR generate a syntactically correct SQL expression, the attacker is granted the access of validuser.

Likewise, an attacker could supply a string for <PASSWORD> such as:

 

Code Block
' OR '1'='1

This would yield the following command: 

Code Block
SELECT * FROM Users WHERE userid='' AND password='' OR '1'='1'

This time, the '1'='1' tautology disables both user ID and password validation, and the attacker is falsely logged in without a correct login ID or password.

...

Unfortunately, this code example permits a SQL injection attack because the string passed to prepare accepts unsanitized input arguments. The attack scenario outlined previously would work as described.

 

Code Block
bgColor#ffcccc
langperl
use DBI;


use warnings;


use strict;


my $dbfile = "users.db";


my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","")


  or die "Couldn't connect to database: " . DBI->errstr;


sub hash() {

# ... hash the password
}print "Enter your id: ";
my $userid = <STDIN>;
chomp $userid;
print "Enter your password: ";
my $password = <STDIN>;
chomp $password;
my $hashed_password = hash( $password);
my $sth =

  # hash the password
}

print "Enter your id: ";
my $userid = <STDIN>;
chomp $userid;
print "Enter your password: ";
my $password = <STDIN>;
chomp $password;
my $hashed_password = hash( password);
my $sth = $dbh->prepare("SELECT * FROM Users WHERE userid = '$userid' AND password = '$hashed_password'")


  or die "Couldn't prepare statement: " . $dbh->errstr;


$sth->execute(
)
or die
$userid, $hashed_password)
  or die "Couldn't execute statement: " . $sth->errstr;


if (my @data = $sth->fetchrow_array()) {


  my $username = $data[1];


  my $id = $data[2];


  print "Access granted to user: $username ($userid)\n";


}


if ($sth->rows == 0) {


  print "Invalid username / password. Access denied\n";


}


$sth->finish;


$dbh->disconnect;

 

Compliant Solution (prepared statement)

Fortunately, Perl's DBI library provides an API for building SQL commands that sanitize untrusted data. The prepare() method properly escapes input strings, preventing SQL injection when used properly. This is an example of component-based sanitization.

 

Code Block
bgColor#ccccff
langperl
use DBI;


use warnings;


use strict;


my $dbfile = "users.db";


my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile","","")


  or die "Couldn't connect to database: " . DBI->errstr;


sub hash() {

# ... hash the password
}

print "Enter your id: ";
my $userid = <STDIN>;
chomp $userid;
print "Enter your password: ";
my $password = <STDIN>;
chomp $password;
my $hashed_password = hash( $password);

my $sth =

  # hash the password
}

print "Enter your id: ";
my $userid = <STDIN>;
chomp $userid;
print "Enter your password: ";
my $password = <STDIN>;
chomp $password;
my $hashed_password = hash( password);
my $sth = $dbh->prepare("SELECT * FROM Users WHERE userid = ? AND password = ?")


  or die "Couldn't prepare statement: " . $dbh->errstr;


$sth->execute($userid, $hashed_password)


  or die "Couldn't execute statement: " . $sth->errstr;


if (my @data = $sth->fetchrow_array()) {


  my $username = $data[1];


  my $id = $data[2];


  print "Access granted to user: $username ($userid)\n";


}


if ($sth->rows == 0) {


  print "Invalid username / password. Access denied\n";


}


$sth->finish;


$dbh->disconnect;

 

...


 

Risk Assessment

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

IDS33-PL

high

likely

high

P9

L2

...