iic has asked for the wisdom of the Perl Monks concerning the following question:

Hi,Perlmonks
I use Win32::ODBC access to SQLServer,there is a section
of my codes below.
#use CGI.pm to get user's input...
#connect to DSN...
$SQL=<<SQL;
select * from st
where st_name='$name'
SQL
if($dsn->Sql($SQL)){print $dsn->Error();}
...
$name is provided from user.If some guy input(just a sample)
x' delete st where st_name not like '%''%
, he will delete all in my table!
My question is
Is there a tactful way to deal with the codes for SQL in
advance?
Many Thanks!

Replies are listed 'Best First'.
RE: Bad codes for SQL
by Corion (Patriarch) on Aug 18, 2000 at 15:16 UTC

    First of all, the Perl Monks FAQ has information on how to format and post Perl code. Use <CODE> tags wrapped around your code like this :
    <CODE>
    # Some Perl code
    </CODE>

    There are two ways prevent users from stuffing bad data into the parameters and execute arbitrary SQL code. One way is to quote the data, that is, placing all (string) data in '', and replacing all "'" within strings with "\'". This would be done like the following, but mind that there are already invented wheels for this around somewhere (although I don't know where exactly, maybe in the DBI module series) :

    Update: Jouke knows DBI better than I do, and the function you want is $dbh->quote() - that is, if you switch from Win32::ODBC to the DBI/DBD combo.

    sub SQLQuote { my ( $data ) = @_; $data =~ s/'/\\'/g; return $data; };
    The other method would be to supply your data as parameters to the SQL query, but I have never done this myself, so maybe another monk could step in here (hint, hint). The SQL queries look then like this :
    SELECT * FROM table WHERE name=? AND id=?
    and the statement to call it would be :
    $dsn->Sql($SQL, $name, $id );

    Update: kudra told me that using parameters would automagically provide correct quoting for the database in question. It goes more like this :

    # Code courtesy of kudra++, errors courtesy of me $SQL = < SQL; SELECT * FROM table WHERE name=? and id=? SQL $statement = $dbh->prepare( $SQL ); $statement->execute($name, $id);

    Update : Thinking further about this, you might want to make sure before using all that code that the strings somewhat resemble what you expect, that is, data that should be a number should match /^\d+$/ and usernames should maybe match /^[a-z0-9_]+$/ and strings in general should maybe not contain characters below 32 - this will protect you a bit more against bad data supplied by the users.

    Update: And I've even learned some more about SQL - what I called "parameter syntax" is instead called "placeholders". Another day at Perlmonks, another thing learned :)

      Corion is right in using the questionmarks. In DBI this would look like this:
      my $sth = $dbh->prepare('SELECT * FROM table WHERE name=? AND id=?'); $sth->execute($name, $id);
      but I'm not sure wether or not this provides the quoting...(sure, some other Monk will know ;-) ) Jouke Visser, Perl 'Adept'
      Unfortunately prepared queries really requires DBI.

      What you do is "prepare" a query, get back a statement handle, then pass it the parameters when you "execute" it. If latency is low for inserting multiple rows this can can come close to the maximum theoretical throughput. But there is a lot of overhead to preparing a query with parameters.

        "unfortunately" you say...I'd say: why use anything else?? Why should anyone use Win32::ODBC when DBI is available? Jouke Visser, Perl 'Adept'
RE: Bad codes for SQL
by t0mas (Priest) on Aug 18, 2000 at 15:23 UTC
    Escape all quotes. Parse the strings for quotes before putting them into your sql.

    And _never_ trust any user to provide the input you exepect!

    Always prepare statements and bind escaped values to columns. Never let the user provide parts of the statements him/her self. Or better: Never let the user do ANYTHING to ANY table.

    Provide procedures for every action a user can do on a table, ie. for update/insert/delete/select.

    My ideal view of a database design is user interface procedures which work on logical views of the physical tables. That way you can change the physical design of the database (perhaps for performance reasons) without changing any user interfaces at all. It separates the physical and logical table structure.



    /brother t0mas
RE: Bad codes for SQL
by Jouke (Curate) on Aug 18, 2000 at 15:05 UTC
    I don't know a lot about the Win32::ODBC module, but I normally use DBI along with DBD::ODBC to connect to ODBC-databases (works fine and is more portable). Then I should use $dbh->quote. With DBI, the code would look something like:
    $SQL = "SELECT * FROM st WHERE st_name=".$dbh->quote($name);
    etc... HTH, Jouke Visser, Perl 'Adept'
RE: Bad codes for SQL
by ncw (Friar) on Aug 18, 2000 at 17:31 UTC
    I haven't used Win32::ODBC before but here is the best way to solve your problem using DBI.

    This is one of my coding rules for secure programs - always use parameters to pass in user input when doing SQL queries. This is the ? in the prepare statement below. You then fill in the blanks when you execute the statement.

    my $find_name = $dbh->prepare("select * from table where name = ?"); $find_name->execute($unsafe_name_from _user_via_cgi); my $row = $find_name->fetchrow_hashref; # or whatever $find_name->finish;
    The advantage of doing this is that you can have any user input you like in $unsafe_name_from _user_via_cgi and it will never be interpreted as SQL.

    You can also re-use the prepared SQL which can make it quicker if you do the same type of query over and over.

Re: Bad codes for SQL
by wardk (Deacon) on Aug 18, 2000 at 20:25 UTC

    I wrote a similar CGI program at a recent client's site using Win32::ODBC. But the driver you use is not really relevent in my mind.

    First off, if you are concerned about a user inputing complete SQL statements that will be destructive, either don't let them ( however this might involve creating an interface that allows column selection then crtieria, which can be a very complex solution) or REGEX the SQL for damaging code ( check for delete, and quanantine that user if they send such SQL ) and don't allow it to execute.

    You can alway revoke permissions in the database to disallow a web user from writing to the database, by giving them select rights only. If the user isn't supposed to be able to write to the database this SHOULD be in place already. Check with your DBA.

    If your system uses a login, then you can disallow this AND identify someone who attmepts to submit destructive code.

RE: Bad codes for SQL
by iic (Sexton) on Aug 18, 2000 at 14:51 UTC
    #use CGI.pm to get user's input...
    #connect to DSN...
    $SQL=<<SQL;
    select * from st
    where st_name='$name'
    SQL
    if($dsn->Sql($SQL)){print $dsn->Error();}
    ...