SQL Injection with backslash - sql

This is sample code:
include("db_connect.php");
//
function foo($string){
$s_array = array("'", '"');
$result = str_replace($s_array, "\\", $string);
return $result;
}
//
$first_var = $_POST['first_var'];
$second_var = intval($_POST['second_var']);
//
$first_var_result = foo($first_var);
//
mysql_query("UPDATE some_table SET first_column='".$first_var_result."', second_column='".$second_var."' WHERE id='id'");
When $_POST['first_var'] equals ', foo function replaces ' with \ and mysql returns ERROR.
This is not my code. I'm simpe interested in if this code is vulnerable (SQL Injection)? Thanks.

Not this one but you are very close. If first_var_result ends with ' or ", the replacement \ will escape the apostrophe in the SQL query and second_var would be executed as code. If you would escape it with foo as well, you have SQL injection.

Related

Extra quotes doing SQL insert from Perl to CSV. If I try to remove them, I get no quotes. I need single quotes as in "0123"

When I write to a csv file from Perl using SQL Insert, I get either 0123 or """0123""", but I need "0123". Neither concatenation nor regex seem to resolve the issue.
Here's my code:
my $dbh = DBI->connect(qq{DBI:CSV:csv_eol=\n;csv_sep_char=\\,;});
$dbh->{'csv_tables'}->{'Table'} = {'file' => 'data.csv','col_names' =>
["num","id"]};
#Setup error variables
$dbh->{'RaiseError'} = 1;
$# = '';
## Attempts to change $num
##$num = '"'.$num.'"';## this causes """0123"""
##$num = "\"$num\"";## this causes """0123""" even if I additionally do this:
## $num=~s/"""/"/g;
##$num = " ".$num;## causes " 0123" VERY CLOSE. Try next line:
##$num=~s/ //g;## This causes 0123
##$num = "".$num; ## causes 0123
##$num = "'".$num."'";## causes 123
my $value = "\'$num\',\'$id\'";
my $insert = "INSERT INTO Table VALUES ($value)";
my $sth = $dbh->prepare($insert);
$sth->execute();
$sth->finish();
$dbh->disconnect();
I would like to have the output of $num to end up being "0123" in the CSV, but instead I get 0123 or """0123""" or " 0123"
Thanks to poj at PerlMonks (https://www.perlmonks.org/?node_id=11107315), there is a solution:
Here it is for everyone's convenience:
Use placeholders and always_quote option
#!/usr/bin/perl
use strict;
use DBI;
my $dbh = DBI->connect("dbi:CSV:", "", "",{
'RaiseError' => 1 }
);
$dbh->{'csv_tables'}->{'MyTable'} = {
'file' => 'data1.csv',
'col_names' => ["num","id"],
'always_quote' => 1,
};
my $num = '0123';
my $id = '0124';
my $sql = "INSERT INTO MyTable VALUES (?,?)";
my $sth = $dbh->do($sql,undef,$num,$id);
poj
Interpolating variables into SQL statements is a bad idea. You leave yourself open to SQL injection attacks (see Bobby Tables). So don't do that.
Instead, use bind parameters and pass extra values to the execute() method.
my $insert = 'INSERT INTO Table VALUES (?, ?)';
my $sth = $dbh->prepare($insert);
$sth->execute($num, $id);
The problem is that a CSV entry of 123 and of "123" are identical. A single pair of double quotes is part of the CSV format (at least, the most common variant). So when you try to insert a value containing double quotes, it doubles them up to escape them. DBD::CSV is only surrounding the cell in double quotes when it contains special characters like a comma, space, or escaped double quotes.
foo," foo ",",","""a""b"
# parses to: 'foo' -- ' foo ' -- ',' -- '"a"b'
The parser should not care whether the cell is quoted or not (unless it is needed because the cell contains these special characters). See http://tburette.github.io/blog/2014/05/25/so-you-want-to-write-your-own-CSV-code/

like operator with variable string

i am using LIKE operator in sql but is show some error.here is my code
<?php
header('Access-Control-Allow-Origin: *');
$con=mysqli_connect("url","xxxx","password","xxxx");
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
exit();
}
$callback =$_GET['callback'];
DECLARE #alpha nchar(1)
SET #alpha = 'A'
$result= mysqli_query($con,"SELECT * FROM demo WHERE Name LIKE #alpha + '%'");
$var= array();
while($row = mysqli_fetch_assoc($result))
{
$var[]=$row;
}
echo $callback."(".json_encode($var).")";
mysqli_close($con);
?>
it works properly when i am using it without alpha i.e when i give string directly.but when i am useing with vaiable it shows an error "Unexpected token <"
I am not a PHP expert, so am not sure of the syntaxes...but you can try the following:
SET #alpha = "A"
$result= mysqli_query($con,"SELECT * FROM demo WHERE Name LIKE '" + #alpha + "%'");
i.e. use the PHP strings within double inverted commas (") and the SQL strings within single inverted commas (')
Also, changing the second line as above should result in the SQL query:
SELECT * FROM demo WHERE Name LIKE 'A%' which is what I think you want. I am not sure if using the variable name within the string will result in getting its value or embed it as it is in the query.

Invalid parameter number: number of bound variables does not match number of tokens PDO insert

function mysql_insert($data_array){
$sql = "insert into `". $this->table_name. '`';
$array_keys = array_keys($data_array);
$array_keys_comma = implode(",\n", preg_replace('/^(.*?)$/', "`$1`", $array_keys));
for($a=0,$b=count($data_array); $a<$b; $a++){ $question_marks .="?,"; }
$array_values = array_values($data_array);
$array_values_comma = implode(",", $array_values);
$sql.= " ($array_keys_comma) ";
$sql.= " values(". substr($question_marks, 0,-1) .")";
$prepare = $this->connDB->prepare($sql);
$insert = $prepare->execute(array($array_values_comma));
}
I want to creat like this universal functions, $data_array-comes from $_POST
This function will work for all form. But i dont know what is my wrong :S
I don't know what is my wrong
That's quite easy to know: number of bound variables does not match number of tokens.
I want to creat like this universal functions, $data_array-comes from $_POST
Here you go: Insert/update helper function using PDO
$array_values_comma is a scalar after you implode() the array. So you always pass an array of one element to your execute() function. You should pass $array_values.
Here's how I'd write this function:
function mysql_insert($data_array){
$columns = array_keys($data_array);
$column_list_delimited = implode(",",
array_map(function ($name) { return "`$name`"; }, $columns));
$question_marks = implode(",", array_fill(1, count($data_array), "?"));
$sql = "insert into `{$this->table_name}` ($column_list_delimited)
values ($question_marks)";
// always check for these functions returning FALSE, which indicates an error
// or alternatively set the PDO attribute to use exceptions
$prepare = $this->connDB->prepare($sql);
if ($prepare === false) {
trigger_error(print_r($this->connDB->errorInfo(),true), E_USER_ERROR);
}
$insert = $prepare->execute(array_values($data_array));
if ($insert === false) {
trigger_error(print_r($prepare->errorInfo(),true), E_USER_ERROR);
}
}
A further improvement would be to do some validation of $this->table_name and the keys of $data_array so you know they match an existing table and its columns.
See my answer to escaping column name with PDO for an example of validating column names.

Is this code sufficient to protect me from SQL injection attacks, and PHP injection attacks?

Is this code sufficient to protect me from SQL injection attacks, and PHP injection attacks?
I have this function in an include file of functions:
function strclean ($string) {
$outstr = '';
if (strlen ($string) > 0) {
$ix = 0;
$char = substr ($string, $ix, 1);
// strip leading spaces
while ($char == ' ') {
$ix = $ix + 1;
$char = substr ($string, $ix, 1);
}
// disarm naughty characters
while ($ix < strlen ($string)) {
$char = substr ($string, $ix, 1);
if ($char == '<') $char = '<';
else if ($char == '>') $char = '>';
else if ($char == '"') $char = '"';
else if ($char == '&') $char = '&';
else if ($char < chr(20)) $char = '';
$outstr = $outstr . $char;
$ix = $ix + 1;
}
// strip trailing spaces
while (substr ($outstr, strlen ($outstr) - 1, 1) == ' ' && strlen ($outstr) > 0) {
$outstr = substr ($outstr, 0, strlen ($outstr) - 1);
}
$outstr = mysql_real_escape_string ($outstr);
}
return $outstr;
}
Later on in my page, I have various strings returned from form input such as this example:
$username = strclean ($_POST['username']);
$password = strclean ($_POST['password']);
And even later, I have the following SQL:
$result = mysql_query ('SELECT * FROM users WHERE
username = "' . $username . '"', $dbconn) or die (mysql_error());
I don't search for username and password together in the query. A few lines after this, I check for a valid password like this:
if ($rowsfound == 1) {
$userrow = mysql_fetch_array ($result);
$userword = $userrow ["password"];
if ($userword == $password) {
// logon
}
else {
// incorrect password
}
}
else if ($rowsfound == 0) {
// unknown user
}
else {
// something strange happened! possible sql injection attack?
}
The general rule is to deny everything and allow through only valid characters, rather than removing what you consider to be invalid.
The most important aspect is what you do with these string afterwards. If you have a line later saying:
tsql = "SELECT * FROM Users WHERE Username='" . $username . "' AND "
then this is the primary area of risk, although mysql_real_escape_string should avoid this.
By using a libraries or features that allow passing of parameters to the database there can never be any sql injection, as the database parameters can't be interpreted into TSQL, leaving only PHP/Javascript injection as a possibility.
Basically, look at the bind_param functions as the only true protection.
Whenever displaying data on-screen, consider something like htmlspecialchars() to convert it to HTML. There's no point in storing something escaped if you need it un-escaped later, and raw data in the database poses no risk as long as you always consider it raw.
In summary, the code you list may or may not reduce injection, but there are too many combinations to exclude every possibility, including aspects such as a user using single quotes (you're only replacing double quotes).
All user input data is potentially dangerous. Feel free to store it raw, but whenever USING it make sure your operations are protected using one of the above options.
My PHP is a bit rusty now, but exactly the same rules apply to SQL Server, Oracle, .NET, Java any any other database/language.

Regular Expression to Match All Comments in a T-SQL Script

I need a Regular Expression to capture ALL comments in a block of T-SQL. The Expression will need to work with the .Net Regex Class.
Let's say I have the following T-SQL:
-- This is Comment 1
SELECT Foo FROM Bar
GO
-- This is
-- Comment 2
UPDATE Bar SET Foo == 'Foo'
GO
/* This is Comment 3 */
DELETE FROM Bar WHERE Foo = 'Foo'
/* This is a
multi-line comment */
DROP TABLE Bar
I need to capture all of the comments, including the multi-line ones, so that I can strip them out.
EDIT: It would serve the same purpose to have an expression that takes everything BUT the comments.
This should work:
(--.*)|(((/\*)+?[\w\W]+?(\*/)+))
In PHP, i'm using this code to uncomment SQL (this is the commented version -> x modifier) :
trim( preg_replace( '#
(([\'"]).*?[^\\\]\2) # $1 : Skip single & double quoted expressions
|( # $3 : Match comments
(?:\#|--).*?$ # - Single line comment
| # - Multi line (nested) comments
/\* # . comment open marker
(?: [^/*] # . non comment-marker characters
|/(?!\*) # . not a comment open
|\*(?!/) # . not a comment close
|(?R) # . recursive case
)* # . repeat eventually
\*\/ # . comment close marker
)\s* # Trim after comments
|(?<=;)\s+ # Trim after semi-colon
#msx', '$1', $sql ) );
Short version:
trim( preg_replace( '#(([\'"]).*?[^\\\]\2)|((?:\#|--).*?$|/\*(?:[^/*]|/(?!\*)|\*(?!/)|(?R))*\*\/)\s*|(?<=;)\s+#ms', '$1', $sql ) );
Using this code :
StringCollection resultList = new StringCollection();
try {
Regex regexObj = new Regex(#"/\*(?>(?:(?!\*/|/\*).)*)(?>(?:/\*(?>(?:(?!\*/|/\*).)*)\*/(?>(?:(?!\*/|/\*).)*))*).*?\*/|--.*?\r?[\n]", RegexOptions.Singleline);
Match matchResult = regexObj.Match(subjectString);
while (matchResult.Success) {
resultList.Add(matchResult.Value);
matchResult = matchResult.NextMatch();
}
} catch (ArgumentException ex) {
// Syntax error in the regular expression
}
With the following input :
-- This is Comment 1
SELECT Foo FROM Bar
GO
-- This is
-- Comment 2
UPDATE Bar SET Foo == 'Foo'
GO
/* This is Comment 3 */
DELETE FROM Bar WHERE Foo = 'Foo'
/* This is a
multi-line comment */
DROP TABLE Bar
/* comment /* nesting */ of /* two */ levels supported */
foo...
Produces these matches :
-- This is Comment 1
-- This is
-- Comment 2
/* This is Comment 3 */
/* This is a
multi-line comment */
/* comment /* nesting */ of /* two */ levels supported */
Not that this will only match 2 levels of nested comments, although in my life I have never seen more than one level being used. Ever.
I made this function that removes all SQL comments, using plain regular expressons. It removes both line comments (even when there is not a linebreak after) and block comments (even if there are nested block comments). This function can also replace literals (useful if you are searching for something inside SQL procedures but you want to ignore strings).
My code was based on this answer (which is about C# comments), so I had to change line comments from "//" to "--", but more importantly I had to rewrite the block comments regex (using balancing groups) because SQL allows nested block comments, while C# doesn't.
Also, I have this "preservePositions" argument, which instead of stripping out the comments it just fills comments with whitespace. That's useful if you want to preserve the original position of each SQL command, in case you need to manipulate the original script while preserving original comments.
Regex everythingExceptNewLines = new Regex("[^\r\n]");
public string RemoveComments(string input, bool preservePositions, bool removeLiterals=false)
{
//based on https://stackoverflow.com/questions/3524317/regex-to-strip-line-comments-from-c-sharp/3524689#3524689
var lineComments = #"--(.*?)\r?\n";
var lineCommentsOnLastLine = #"--(.*?)$"; // because it's possible that there's no \r\n after the last line comment
// literals ('literals'), bracketedIdentifiers ([object]) and quotedIdentifiers ("object"), they follow the same structure:
// there's the start character, any consecutive pairs of closing characters are considered part of the literal/identifier, and then comes the closing character
var literals = #"('(('')|[^'])*')"; // 'John', 'O''malley''s', etc
var bracketedIdentifiers = #"\[((\]\])|[^\]])* \]"; // [object], [ % object]] ], etc
var quotedIdentifiers = #"(\""((\""\"")|[^""])*\"")"; // "object", "object[]", etc - when QUOTED_IDENTIFIER is set to ON, they are identifiers, else they are literals
//var blockComments = #"/\*(.*?)\*/"; //the original code was for C#, but Microsoft SQL allows a nested block comments // //https://msdn.microsoft.com/en-us/library/ms178623.aspx
//so we should use balancing groups // http://weblogs.asp.net/whaggard/377025
var nestedBlockComments = #"/\*
(?>
/\* (?<LEVEL>) # On opening push level
|
\*/ (?<-LEVEL>) # On closing pop level
|
(?! /\* | \*/ ) . # Match any char unless the opening and closing strings
)+ # /* or */ in the lookahead string
(?(LEVEL)(?!)) # If level exists then fail
\*/";
string noComments = Regex.Replace(input,
nestedBlockComments + "|" + lineComments + "|" + lineCommentsOnLastLine + "|" + literals + "|" + bracketedIdentifiers + "|" + quotedIdentifiers,
me => {
if (me.Value.StartsWith("/*") && preservePositions)
return everythingExceptNewLines.Replace(me.Value, " "); // preserve positions and keep line-breaks // return new string(' ', me.Value.Length);
else if (me.Value.StartsWith("/*") && !preservePositions)
return "";
else if (me.Value.StartsWith("--") && preservePositions)
return everythingExceptNewLines.Replace(me.Value, " "); // preserve positions and keep line-breaks
else if (me.Value.StartsWith("--") && !preservePositions)
return everythingExceptNewLines.Replace(me.Value, ""); // preserve only line-breaks // Environment.NewLine;
else if (me.Value.StartsWith("[") || me.Value.StartsWith("\""))
return me.Value; // do not remove object identifiers ever
else if (!removeLiterals) // Keep the literal strings
return me.Value;
else if (removeLiterals && preservePositions) // remove literals, but preserving positions and line-breaks
{
var literalWithLineBreaks = everythingExceptNewLines.Replace(me.Value, " ");
return "'" + literalWithLineBreaks.Substring(1, literalWithLineBreaks.Length - 2) + "'";
}
else if (removeLiterals && !preservePositions) // wrap completely all literals
return "''";
else
throw new NotImplementedException();
},
RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
return noComments;
}
Test 1 (first original, then removing comments, last removing comments/literals)
[select /* block comment */ top 1 'a' /* block comment /* nested block comment */*/ from sys.tables --LineComment
union
select top 1 '/* literal with */-- lots of comments symbols' from sys.tables --FinalLineComment]
[select top 1 'a' from sys.tables
union
select top 1 '/* literal with */-- lots of comments symbols' from sys.tables ]
[select top 1 ' ' from sys.tables
union
select top 1 ' ' from sys.tables ]
Test 2 (first original, then removing comments, last removing comments/literals)
Original:
[create table [/*] /*
-- huh? */
(
"--
--" integer identity, -- /*
[*/] varchar(20) /* -- */
default '*/ /* -- */' /* /* /* */ */ */
);
go]
[create table [/*]
(
"--
--" integer identity,
[*/] varchar(20)
default '*/ /* -- */'
);
go]
[create table [/*]
(
"--
--" integer identity,
[*/] varchar(20)
default ' '
);
go]
This works for me:
(/\*(.|[\r\n])*?\*/)|(--(.*|[\r\n]))
It matches all comments starting with -- or enclosed within */ .. */ blocks
I see you're using Microsoft's SQL Server (as opposed to Oracle or MySQL).
If you relax the regex requirement, it's now possible (since 2012) to use Microsoft's own parser:
using Microsoft.SqlServer.Management.TransactSql.ScriptDom;
...
public string StripCommentsFromSQL( string SQL ) {
TSql110Parser parser = new TSql110Parser( true );
IList<ParseError> errors;
var fragments = parser.Parse( new System.IO.StringReader( SQL ), out errors );
// clear comments
string result = string.Join (
string.Empty,
fragments.ScriptTokenStream
.Where( x => x.TokenType != TSqlTokenType.MultilineComment )
.Where( x => x.TokenType != TSqlTokenType.SingleLineComment )
.Select( x => x.Text ) );
return result;
}
See Removing Comments From SQL
The following works fine - pg-minify, and not only for PostgreSQL, but for MS-SQL also.
Presumably, if we remove comments, that means the script is no longer for reading, and minifying it at the same time is a good idea.
That library deletes all comments as part of the script minification.
I am using this java code to remove all sql comments from text. It supports comments like /* ... */ , --..., nested comments, ignores comments inside quoted strings
public static String stripComments(String sqlCommand) {
StringBuilder result = new StringBuilder();
//group 1 must be quoted string
Pattern pattern = Pattern.compile("('(''|[^'])*')|(/\\*(.|[\\r\\n])*?\\*/)|(--(.*|[\\r\\n]))");
Matcher matcher = pattern.matcher(sqlCommand);
int prevIndex = 0;
while(matcher.find()) {
// add previous portion of string that was not found by regexp - meaning this is not a quoted string and not a comment
result.append(sqlCommand, prevIndex, matcher.start());
prevIndex = matcher.end();
// add the quoted string
if (matcher.group(1) != null) {
result.append(sqlCommand, matcher.start(), matcher.end());
}
}
result.append(sqlCommand.substring(prevIndex));
return result.toString();
}
Following up from Jeremy's answer and inspired by Adrien Gibrat's answer.
This is my version that supports comment characters inside single-quoted strings.
.NET C# note you need to enable RegexOptions.IgnorePatternWhitespace
, most other languages this is the x option
(?: (?:'[^']*?') | (?<singleline>--[^\n]*) | (?<multiline>(?:\/\*)+?[\w\W]+?(?:\*\/)+) )
Example
https://regex101.com/r/GMUAnc/3