Forum Moderators: coopster

Message Too Old, No Replies

PHP and SQL

         

hayesjl

9:55 am on Aug 9, 2004 (gmt 0)

10+ Year Member



I am working on a order validation script that collects information from a form then POST's to a PHP page that validates then enters the information into a DB.

Here is the page I am using to Validate.
<html>
<head>
</head>
<body>

<?php
//////////////////////////////////////// CONNECT TO MYSQL DB ////////////////////
// OPEN CONNECTION --->
$connection=mysql_connect("localhost","LOGIN", "Password") or die("Unable to connect!");

mysql_select_db("thevegas_vegas1") or die("Unable to select database!");

/* grabs the POST variables and puts them into variables that we can use */
$Fname=$_POST['Fname'];
$Lname=$_POST['Lname'];
$Email=$_POST['Email'];
$Add1=$_POST['Add1'];
$City=$_POST['City'];
$State=$_POST['State'];
$Zip=$_POST['Zip'];
$Package=$_POST['package'];
$Code=$_POST['code'];

//---------VALIDATION-------->
if($Fname){//----> CHECK input
}
else{
$error.="Please, go back and fill out your first name\n";//----> ERROR if no input
}

if($Lname){//----> CHECK input
}
else{
$error.="Please, go back and fill out your last name\n";//----> ERROR if no input
}

if($Email){//----> CHECK input
}
else{
$error.="Please, go back and fill out your e-mail address\n";//----> ERROR if no input
}

if($Add1){//----> CHECK input
}
else{
$error.="Please, go back and fill out your mailing address\n";//----> ERROR if no input
}
if ($Code) {
$sql_query = "SELECT * FROM agents WHERE Code = "$Code"";
if $sql_query == ""
$error.="Referral Code not valid! Please input again or leave blank if you do not have one.\n"; //-----> ERROR if code not found
}
else { //---------> No code so we move on.
}
}
if($Package) {//----> CHECK input
}
else{
$error.="You must select a package to order!, go back and pick a package!\n";//-------> NO PACKAGE NO ORDER!
}

if($City){//----> CHECK input
}
else{
$error.="Please, go back and fill out your city name\n";//----> ERROR if no input
}

if($Zip){//----> CHECK input
}
else{
$error.="Please, go back and fill out your zip code\n";//----> ERROR if no input
}
//-------->ERROR FREE?
if($error==""){
//--------------------------------INSERT IFP PAYPAL CHECK HERE---------------------------------------------//
echo "Thank you for your purchase. An email will be sent to you confirming your purchase immediatly.";
//----------------------------------
$mailContent="--------CONTACT--------\n"
."First Name: ".$Fname."\n"
."Last Name: ".$Lname."\n"
."E-mail: ".$Email."\n"
."Street Address: ".$Add1."\n"
."City: ".$City."\n"
."State: ".$State."\n"
."Zip Code: ".$Zip."\n\n--------INFO--------\n"
."Referral Code ".$Code."\n"
."Package: ".$Package."\n"
//----------------------------------
$toAddress="billing@example.com";
$subject="Order Confirmation";
$recipientSubject="Order Confirmation";
$receiptMessage = "Thank you ".$Fname." for your subscription!\n\n\nHere is the information we have on file:\n\n"
."--------CONTACT--------\n"
."First Name: ".$Fname."\n"
."Last Name: ".$Lname."\n"
."Street Address: ".$Add1."\n"
."City: ".$City."\n"
."State: ".$State."\n"
."Zip Code: ".$Zip."\n\n--------INFO--------\n"
."Referral Code: ".$Code."\n"
."Package: ".$Package."\n"
//----------------------------------
mail($email, $subject, $receiptMessage,"From:$toAddress");
//----------------------------------
mail($toAddress,$recipientSubject,$mailContent,"From:$email");
//--->echo $mailContent;

// EXECUTE QUERY --->
$query="INSERT INTO members (
Fname,
Lname,
Email,
Add1,
City,
State,
Zip,
Code,
Package)
VALUES(
'".$Fname."',
'".$Lname."',
'".$Email."',
'".$Add1."',
'".$City."',
'".$state."',
'".$Zip."',
'".$Code."',
'".$Package."')";
//////----->
$result=mysql_query($query) or die("Error in query:".mysql_error());
//if ($result)
//echo mysql_affected_rows()." row inserted into the database effectively.";

// CLOSE CONNECTION --->
mysql_close($connection);

///////////////////////////////////////////////////////////////////////////////////
}
else{

print "Sorry, but the form cannot be sent until the fields indicated are filled out completely - \n";
print "$error\n";
print "\n";
print "\n";
print "Please use your \"Back\" button to return to the form to correct the omissions. Thank you.\n";
}

?>
</body>
</html>

THe line I am having trouble with is

if ($Code) {
$sql_query = "SELECT * FROM agents WHERE Code = "$Code"";
if $sql_query == ""
$error.="Referral Code not valid! Please input again or leave blank if you do not have one.\n"; //-----> ERROR if code not found
}
else { //---------> No code so we move on.
}
}

I want to make sure the code exitst in the DB before I allow the transaction to take place and be added to the DB.

Any help would be greatly appreciated.

[edited by: coopster at 1:57 pm (utc) on Aug. 11, 2004]
[edit reason] generalized email address [/edit]

hayesjl

10:07 am on Aug 9, 2004 (gmt 0)

10+ Year Member



Quick note if anyone wants to help I am trying to build a custom order/registration page.

New customer comes to page and decides to order/register.
They fill in form (referral code optional)
when they hit submit I want to:
Verify the referral code if they entered one.
Depending on if they had a referral code or not they get one of 2 possible PAYPAL payment options (discount for valid referral) Normal Price without.
I want to Verify the PAYPAL Payment THEN and only THEN
will it add them to the DB and send the email and if it was reallly cool I could add their username and random passowrd to the .htaccess file for the member directory (optional but would be cool)

hayesjl

1:24 pm on Aug 9, 2004 (gmt 0)

10+ Year Member



OK I figured that one out now im down to this,

After I collect the info from the form and searched the DB for the things I needed to validate I need to submit a paypal POST before I add the user to the DB.

Anyone help me with how to POST a subscription payment to paypal without using another form? Id like the first submit to be the only. user registration +validation + payment all in one.

-THanks in advance

mattx17

3:18 pm on Aug 9, 2004 (gmt 0)

10+ Year Member



if ($Code) {
$sql_query = "SELECT * FROM agents WHERE Code = "$Code"";
if $sql_query == ""
$error.="Referral Code not valid! Please input again or leave blank if you do not have one.\n"; //-----> ERROR if code not found
}
else { //---------> No code so we move on.
}
}

Your problem is you are checking if the $sql_query is empty, even though you are defining it right before you check it. You need to execute the statement first, then check if it returned true or false, like so:

$Exec = mysql_query($sql_query);
if ($Exec) {
...

So on and so forth.

Another tip: If you have a long list of form fields, put them in an array. In the name parameter of the input field, do it like so:

name="FormField[FirstName]"
name="FormField[LastName]"

For all of the fields. When it comes time to check them all, it's really quite simple:

foreach($_POST{'FormField'] as $Field => $Value)
{
if (!$Value)
{
die("$Field field is empty!");
}
}

Of course this only checks if the field is empty or not, it doesn't check for other things like valid e-mail addresses and valid types.

Then when it comes time to construct a SQL statement, it's also a breeze:

$sql_query = "insert into table set ";
foreach($_POST['FormField'] as $Field => $Value)
{
$sql_query = $Field . " = '" . addslashes($Value) . "', ";
}
$sql_query = ereg_replace(", $","",$sql_query); // cut off the last comma

Anyway, hope this helps.

hayesjl

12:55 am on Aug 10, 2004 (gmt 0)

10+ Year Member



ya I finally trapped the error and used:
if ($Code){
$result = mysql_query ("SELECT * FROM agents WHERE Code = '$Code'") or die ("Sorry Referral Code Invalid\r\n");
$numrs = mysql_num_rows($result);
if ($numrs ==0){
$error.="Please go back and enter a Valid Referral Code.\r\n";
}
}

worked well. NOw I just need to figure out how to double post. I seen some articles on opening a http session from php but they lack any documentation or explanation that I can figure out how to re-cycle.

psudo code would be something like:

if($package == "season package" && $codeflag == TRUE) {
submit paypal payment to user for amount of 900.00 re-occuring 6 months.
else
submit paypalpayment to user for amount of 1000.00 re-occuring 6 months.
}

or something like that. eventually Ill want to incorporate IPN and account creation automation but baby steps gets me there i need to send a payment. or beable to trigger the POST from within PHP and pass the variables with it to paypal/webscr.

TheBlueEyz

11:22 am on Aug 10, 2004 (gmt 0)

10+ Year Member




hayesjl,

Look up these functions:

fsockopen
fputs

Or the (superior) alternative, if it is available in your PHP installation:

cURL

These two tools allow you to open a connection (HTTP or otherwise) to a server and do whatever you want.

You can send a POST operation to a server like this:

$fp = fsockopen("domain.com/somefile.php", 80, $errno, $errstr, 10);
(pseudocode - look up the proper syntax to use).
@fputs($fp,"POST?var1=1&var2=2&var3=3" HTTP/1.0\r\n\r\n");
fclose($fp);

For future reference, sending a GET request is nothing more than the very simple matter of changing the word "POST" into the word "GET" :)

I also wanted to make another note - you might want to think about beefing up your security in everything you're checking.

At the moment, your mysql query to check the Code, for example, is susceptible to the absolute simplest of all mysql injection attacks:


' or '1

There are other variations on this very simple attack.

hayesjl

8:43 pm on Aug 10, 2004 (gmt 0)

10+ Year Member



actually the bigger difference in get and post is that with get the information being passed is visible and changeable in the address bar where as POST is passed behind the scenes and not so changeable.

As for the SQL injection attacks. Im not going to say it cant be brute forced but its currently set to 4 login attempts per IP then your dead in the water for an hour. The DB is a registered data source so no chance at direct file access AND the user name and password to get into the DB are 14 alpha numeric each with special characters and is changed weekly. SO if your realllllly bored and realllllly lucky on the password I suppose someone could SQL inject my DB but its not holding pentagon files so youd be sadly dissappointed after you gained access.

hayesjl

8:51 pm on Aug 10, 2004 (gmt 0)

10+ Year Member



<%

function killChars(strWords)

dim badChars
dim newChars

badChars = array("select", "drop", ";", "--", "insert",
"delete", "xp_")
newChars = strWords

for i = 0 to uBound(badChars)
newChars = replace(newChars, badChars(i), "")
next

killChars = newChars

end function

%>

Using stripQuotes in combination with killChars greatly removes the chance of any SQL injection attack from succeeding. So if we had the query:

select prodName from products where id=1; xp_cmdshell 'format
c: /q /yes '; drop database myDB; --

and ran it through stripQuotes and then killChars, it would end up looking like this:

prodName from products where id=1 cmdshell ''format c:
/q /yes '' database myDB

...which is basically useless, and will return no records from the query.

TheBlueEyz

3:11 am on Aug 11, 2004 (gmt 0)

10+ Year Member




actually the bigger difference in get and post is that with get the information being passed is visible and changeable in the address bar where as POST is passed behind the scenes and not so changeable.

As for the SQL injection attacks. Im not going to say it cant be brute forced but its currently set to 4 login attempts per IP then your dead in the water for an hour. The DB is a registered data source so no chance at direct file access AND the user name and password to get into the DB are 14 alpha numeric each with special characters and is changed weekly. SO if your realllllly bored and realllllly lucky on the password I suppose someone could SQL inject my DB but its not holding pentagon files so youd be sadly dissappointed after you gained access.

The difference between GET and POST I was talking about was putting data on a socket. The user will never see the socket, so seeing the address bar rather than using a form isn't what I meant.

The "address bar" and a "form" are just convenient visual representations of the HTTP protocol. In reality, GET and POST are integral parts of the HTTP specifications that specify how data is put onto data sockets and how it is interpreted by a server.

THIS is what you are dealing with when you use GET or POST in a socket; they have nothing to do with the address bar or visible data.

I was just giving you a suggestion as to how to initiate a POST operation back to PayPal without making the user fill out a second form.

In regards to your sql query -

Nobody needs to hack your site to use the following injection:


' or '1

They don't need to put in a password, they don't need to submit a form more than once. That single injection will return ALL records in your database. All you're doing is checking to see that more than 0 rows was returned.

Finally -

The 'killchars' subroutine you've posted is written in ASP.NET; I've seen that function posted in alot of articles about sql security.

It's a good start (you'll need to convert it to PHP though), but there are other, more fundamental ways to secure your data.

For example:

Is the code strictly numeric? Or alphanumeric? If so, use one or more of:

is_numeric()
int_val()
ctype_alpha()
ctype_alnum()

Same for usernames and passwords. Basically, doing it this way ensures that


' or '1

Will never work. It immediately strips out the spaces and the apostrophe.

Two other useful functions are

addslashes() // kills the apostrophes straight off!
strip_tags() //strips out any html or javascript injections

hayesjl

4:47 am on Aug 11, 2004 (gmt 0)

10+ Year Member



The referral code is both alpha and numeric

IE:

1SVE707040045

hayesjl

4:51 am on Aug 11, 2004 (gmt 0)

10+ Year Member



with injection attack wouldnt they have to know the location and name of the database and the server where it is being served from?

and doesnt a db query require access to the DB as a user?

I made another php stream in the address bar that would try to query the DB using table names and known records but the return I get is access denied.

If I try it in PHP without a username in the connect string it says no DB selected.

TheBlueEyz

6:45 am on Aug 11, 2004 (gmt 0)

10+ Year Member




with injection attack wouldnt they have to know the location and name of the database and the server where it is being served from?
and doesnt a db query require access to the DB as a user?

I made another php stream in the address bar that would try to query the DB using table names and known records but the return I get is access denied.

If I try it in PHP without a username in the connect string it says no DB selected.

What I meant was this:

Say you have a form that collects someone's username and password.

<form method="post" action="myloginscript.php">
<input type="text" name="username">
<input type="text name="password">
<input type="submit" value="Login!">
</form>

Ok.

Now, someone enters the following into your login box:

Username: ' or '1
Password: ' or '1

Now, your script does this:

$result = mysql_query("select * from users where username = '$username' and password='$password'");

That query evaluates to:

select * from users where username='' or '1' and password='' or '1'

That mysql query will return ALL ROWS in your database.

If you then do this:

if(mysql_num_rows($result) > 0)
{ // login }

You're going to log them in without having received a valid username and password!

That's all I meant.

Since your code is alphanumeric (no spaces, no strange chars), you can use a simple preg_replace:

$cleaned_code = preg_replace('/([^a-zA-Z0-9]+)/', '', $code);

That'll remove everything that isn't a letter or a number.

hayesjl

11:15 am on Aug 11, 2004 (gmt 0)

10+ Year Member



ahh ok I get it now..

Ya I could see where that works. Ill add that to keep them from using it to get past without a valid referral number. But the referral number gets added to the customer record for history and would be caught eventually with the account audit script.

thanks for the info.

As for the member login I just made it so that it modifies the .htaccess on the server when I add someone new and then when they login they actually get a server controlled login box.

hayesjl

1:35 pm on Aug 11, 2004 (gmt 0)

10+ Year Member



Thanks for all your help!

[edited by: coopster at 1:53 pm (utc) on Aug. 11, 2004]
[edit reason] No personal urls please [/edit]