我是第二年的ICT学生。我从来没有在今年之前做过PHP,我们的讲师给了我们基础知识,在学期结束时,给了我们一个项目,将我们在课程中学到的东西和数据库课程结合起来。我们在Windows上使用经典的AMP设置。
现在,我们的导师告诉我们根据我们在课堂上制作小型网站的方式制作个人资料网站。
我没有看到将用户输入数据库的有点奇怪方法的背后。
首先,我们进行一些PHP表格检查,以确保输入的数据安全且有点逼真(例如,这里的邮政编码是4个数字,从不更多,没有字母或其他符号)。
如果一切正常,我们会执行以下操作:
$sql = new SqlObject(); //from SqlObject.class.php
$newUser = new User(login,passw,mail,...,...,...); //from User.class.php
$sql->addUser($newUser);
SqlObject类是一个包含我们需要更新,插入并通常更改数据库中数据的所有SQL命令的类。我们从不在普通页面中编写SQL。但那并不是我困惑的事情。这是User.class.php文件。
此文件仅包含构造函数和与需要输入数据库的字段数量完全相同的字段。例如:
<?php
class User {
// members
var $id;
var $name;
var $password;
// constructor
function User($id=-1,$name='',$password='') {
$this->id = $id;
$this->name = $name;
$this->password = $password;
}
}
?>
就是这样。 SqlObject.class.php文件需要第一行的User.class.php文件。
SqlObject.class.php文件中的函数addUser($user)
如下所示:
function addUser($user) {
$insQuery = 'INSERT INTO users(name,password)';
$insQuery.= " VALUES ('".$user->name."', '".$user->password."')";
@mysql_query($insQuery) or showError("user insert failed");
}
为什么要通过User.class.php文件绕道而行呢?某种安全理由?
我会再说一遍:这是我使用PHP的第一年,我还是学生。
编辑:人们抱怨在插入数据之前没有检查SQL注入。
在这篇文章的开头,我提到了“formchecking”。
register.php
文件执行所有转义和检查输入。这包括几个Regex测试,mysql_real_escape_string()和一些更简单的测试。
一旦所有测试都通过并且所有输入都被转义,只有这样才会发生:
$sql = new SqlObject(); //from SqlObject.class.php
$newUser = new User(login,passw,mail,...,...,...); //from User.class.php
$sql->addUser($newUser);
如果输入没有收到我看到有些人希望在SqlObject.class.php文件中提供它的处理,那么该代码永远不会被执行。
EDIT2:正如所承诺的,博客posted
答案 0 :(得分:5)
所以,你问为什么不直接传递用户和密码
function addUser($name='',$password='') { etc
我的猜测是,您展示的代码是面向未来的示例,其中的想法是User类可能会在将来做更详细的事情,或者从其他来源获取凭据,而不是拥有名称和密码传递给它的构造函数。
这是一个关注点分离的想法,如果代码负责组装超级数据,addUer函数只是使用它需要的东西。在大型程序中,拥有这些类型的组织确实很有帮助 - 从表面上看,它似乎增加了复杂性,但是当你开始这样思考时,它可以限制你需要保留的部分数量。您也可以想象分解整个编程任务,以便一个人查看User类,另一个人查看addUser。他们可以独立工作。愚蠢在这个小小的情况下,但在现实世界中非常有益。
答案 1 :(得分:2)
首先,addUser()方法很糟糕,允许sql注入,因为它不使用预处理语句也不转义内容。它应该读起来像
function addUser($user) {
$insQuery = 'INSERT INTO users(name,password)';
$insQuery.= " VALUES ('".mysql_real_escape_string($user->name)."',";
$insQuery.= " '".mysql_real_escape_string($user->password)."')";
@mysql_query($insQuery) or showError("user insert failed");
}
这很糟糕,因为它告诉你不好的做法,你的老师应该从第一天开始教你关于SQL注入的事情,而不仅仅是为了学术而简化它们。
除此之外,我们的想法是使用抽象来证明代码。
抽象允许您在更高层次上思考。在当前级别,User类可能看起来有点过分,因为它只作为存储工具,但它可以帮助您根据域实例而不是SQL语句进行思考,这样可以帮助您在需要时进行修改。
这些抽象也创建了一个单独的位置来复杂化代码,而不会让更改遍布全局,例如,如果您需要关于用户的其他内容,则只需要更改User类,例如添加a,比如说,printUser()方法或其他。
function printUser() {
return "Mr. ".$this->name;
}
但这是一个容易和人为的学术范例,它可能没有多大意义。你需要一个更大的场景来真正看到抽象的好处。
关于您的修改:
我很高兴你被告知SQL注射,即使不在正确的地方:)
执行SQL注入转义的正确位置不是之前的“formchecking”步骤,而是在将数据发送到SQL Server时执行此操作。那个'formchecking'是正确的验证位置,如你所说,zipcodes的长度,或空字段等,但不是为了逃避字符串是SQL安全的。
一个简单的例子应该说明原因:
$name = formcheck("John O'Donnell"); //Where you do all your checking step,
//including mysql_real_escape_string
$user = new User($name,"gandalf");
但是,
mysql_real_escape_string("John O'Donnell") = "John O\'Donnell";
现在,您使用User实例在屏幕上显示名称:
echo "Welcome ".$user->printUser();
你得到:
Welcome Mr. John O\'Donell
答案 2 :(得分:2)
由于项目规模较小,因此可能没有多大意义。但是你现在有了一个用户类,你可以在其中添加更多功能以便在其他地方使用。例如检查用户名是否存在的功能,更新,删除等。
它还使得您可以在下一个项目中重用此代码,因为它更强大。比如在下一个项目中你需要写一些更大的东西,比如一个完整的用户管理系统。然后你会意识到将事情分开是多么好。
答案 3 :(得分:1)
起初,我认为这是为了防止SQL注入,但由于没有输入验证,情况也不是这样。所以只有在未来添加功能才有可能。
您是否学会在数据库类中使用mysql_real_escape_string
?因为这确实需要保护您的数据库免受攻击!
更新以反映OP的编辑:我仍然对输入的sanatizing感到不舒服。 UI或GUI仅用于一个目的:显示数据。在将数据发送到datalayer(您的sqlobject类)之前,businesslayer(这将是您的User类)关心sanatizing。请考虑一下,也许与您的导师讨论。
答案 4 :(得分:0)
如果这是PHP中的OO示例,那么它就是一个坏的。为什么?因为SqlObject包含一个方法AddUser(),这表明该类直接链接到User类。您可以从SqlObject派生一个SqlUser类,并使用该类来添加用户。这个新类将处理与数据库的通信,并且还负责防止任何SQL注入。即使您可以在其他位置逃避任何代码注入尝试,它仍应包含在此类中,以防您忘记在其他位置添加调用。 (不要以为这不会发生,因为它会!)
然而,即使在最终代码中花费可靠性,也有理由让事情变得更简单。这是一个学校项目,因此是如何编写代码的一个例子。对于一个简单的项目来说,它是合理的,即使有很大的改进空间。另外,请记住,您正在受控环境中学习。一旦你开始为一家商业公司工作,事情应该看起来好多了。 (不幸的是,在很多公司,情况要糟糕得多。)
答案 5 :(得分:0)
如果AddUser类在SqlClass中,这意味着只有SqlClass可以添加用户。这意味着sql类必须依赖于用户类才能在添加用户之前知道用户是什么。 (你不能添加苹果和橘子,特别是如果你不知道橙子是什么)