如何处理层之间的输入和参数验证?

时间:2010-03-05 15:22:47

标签: c# asp.net

如果我有一个3层Web表单应用程序,它接受用户输入,我知道我可以使用表示层中的验证控件来验证该输入。我是否还应该在业务和数据层中进行验证,以防止SQL注入和问题?每层应该进行哪些验证?

另一个例子是传递ID以返回记录。数据层应该确保id有效还是应该在BLL / UI中发生?

6 个答案:

答案 0 :(得分:7)

您应该在应用的所有图层中进行验证。

每层的验证都是特定于图层本身的。每个层都应该安全地向“坏”请求发送并获得有意义的响应,但在每个层执行哪些检查将取决于您的具体要求。

概括地说:

  • 用户界面 - 应验证用户输入,提供有用的错误消息和纠正它们的视觉线索;它应该保护你的下层免受无效的用户输入。
  • 业务/域层 - 应该检查方法的参数是否有效(抛出ArgumentException及其相似的参数)并且应该检查在业务规则的约束内是否可以进行操作;它应该保护您的域免受编程错误的影响。
  • 数据层 - 应检查您尝试插入或更新的数据在数据库的上下文中是否有效,它是否满足所有关系约束和检查约束;它应该保护您的数据库免受数据访问中的错误。

每层的验证将确保只允许该层认为正确的数据和操作进入。这为您提供了大量的可预测性,知道信息必须符合某些标准才能通过您的数据库,操作必须符合逻辑才能通过您的域层,并且用户输入已经过清理并且更容易工作用。

它还为您提供安全保护,知道如果您的任何图层被破坏,则会有另一个图层在其后面执行检查,这样可以防止任何进入您不想要的图像。

答案 1 :(得分:5)

Should I also validate in the business and data layers as well to protect against SQL injection and also issues?

是和是。

在您的业务层代码中,您需要再次验证输入(因为客户端可以被欺骗),还需要验证您的业务逻辑,确保条目对您的应用程序有意义。

对于数据层 - 您还需要确保数据对数据库有效。使用参数化查询,因为这几乎可以确保不会发生SQL注入。

关于ID的具体问题 - 数据库会知道ID是否存在。这是否有效取决于它是否对您的业务层有意义。如果它纯粹是一个DB artefact(不是你的对象模型的一部分),而不是DB需要处理它,如果 是你的对象模型的一部分并且对它有重要意义,那么业务层应该处理它

答案 2 :(得分:1)

您绝对需要在业务和数据层中进行验证。用户界面是不受信任的图层,有人可能会绕过您的客户端验证,在某些情况下可能会绕过您的服务器端UI验证。

防止SQL注入只是参数化查询的问题。短语“SQL注入”甚至不应该存在,它已经解决了多年和几年的问题,但每天我都看到人们使用字符串连接编写查询。不要这样做。参数化命令,你会没事的。

将应用分成多个层的主要原因之一是每个层都可以重复使用。如果各个层不进行自己的验证,那么它们不是自治的,并且您没有适当的关注点分离。如果没有单独的组件进行内置验证,您也无法进行任何全面测试。

我倾向于放宽对internalprivate的类或方法的这些限制,因为它们未经过直接测试或使用。只要公共API经过完全验证,私有API通常就会认为该类处于有效状态。

所以,基本上,是的,每个层,实际上每个公共类和方法都需要验证自己的数据/参数。


语义验证(如检查特定客户ID是否有效)将取决于您的设计要求。显然,在所述ID实际到达数据层之前,业务层无法知道ID是否存在,因此它无法提前执行此检查。是否为丢失的ID抛出异常或仅返回null /忽略错误取决于类/方法的设计目的。

但是,如果此ID需要采用特殊格式 - 例如,您可能使用的是特殊编码的帐号(“R-12345-A-678”) - 那么它确实成为域名的责任/ business layer用于验证输入并确保其符合正确的格式,尤其是在业务类的使用者尝试创建新帐户时。

答案 3 :(得分:1)

任何图层都不应信任来自其他图层的数据。我用这个比喻的是一个领地。假设您想向国王发送消息。如果消息的格式不正确,它将在被听到之前被拒绝。您可以继续发送消息,直到最终获得正确的格式,或者您可以使用使者。使者的工作是帮助您验证您的信息将采用可接受的格式,以便国王能够听到。

系统中的每一层都是一个封地。每个层通过验证它将被接受来充当它将发送数据的层的使者。没有任何层信任来自该层外部的数据(没有人信任来自该领域之外的消息)。数据库不信任中间层。中间层不信任数据库或表示层。演示文稿不信任用户或中间层。

所以答案是绝对应该检查并重新检查每一层中的数据。

答案 4 :(得分:0)

简答:是的。

验证输入在每个新层中被接收并且在它被采取行动之前,通常我会在它被使用或传递到下一层之前验证这样的输入(javascript检查它是否是有效的电子邮件并且没有恶意输入,同样,业务层在使用它构建查询之前也会这样做。)

到你的上一个问题:如果ID返回一条记录,那么它是有效的,你必须找到记录的id以确认它是否有效,所以你要进行大量的unnessecary查找如果你试试那个。

我希望有所帮助。

答案 5 :(得分:-1)

我在Model-View-Presenter的Presenter层进行了所有验证。验证有点棘手,因为它实际上是一个横切关注的问题。

我更喜欢在演示者层进行,因为我可以将调用短路到模型。

另一种方法是在模型层中进行验证,然后进行错误通信问题,因为除了异常之外,您无法轻易地通知其他层错误。您始终可以使用数据打包异常或创建自己的自定义异常,您可以附加错误消息或类似构造的列表,但这对我来说总是很脏。

稍后当我通过Web服务公开我的模型时,我将在Presenter和模型中实现双重验证检查,因为如果调用Web服务,可以跳转presenter层。另一个很大的优点是,它将我对模板的验证与模型分离,因为模型可能只需要对类型的原始验证来匹配数据库,而我的UI的用户我想要更精细的规则来输入他们不仅仅是他们身体上可以。

其他问题:sql注入部分是一个模型问题,不应该在任何中间层。但是,当文本字段不允许使用特殊字符时,大多数sql注入攻击都会完全无效。另一部分是你应该几乎总是使用参数化的sql,这使得sql注入不可用。

关于ID的问题是模型关注它是否可以获得具有该ID的记录,或者它应该返回null或者根据您希望建立的约定为未找到的记录抛出异常。