C# 在linq到sql中保护查询授权

C# 在linq到sql中保护查询授权,c#,linq-to-sql,C#,Linq To Sql,我有3个表:用户表、记录表和用户记录表 这些列是这样的: UserTable UserID | OtherUserDataFields RecordsTable RecordID | OtherRecordDataFields UserRecords UserID | RecordID UserRecords表告诉我哪些用户对哪个记录拥有授权。我有一个函数,通过接收两个参数来更新RecordsTable:一个RecordTheRecord(包含一个RecordID字段)和一个UserID

我有3个表:
用户表、记录表和用户记录表

这些列是这样的:

UserTable
UserID | OtherUserDataFields

RecordsTable
RecordID | OtherRecordDataFields

UserRecords
UserID | RecordID
UserRecords表告诉我哪些用户对哪个记录拥有授权。我有一个函数,通过接收两个参数来更新RecordsTable:一个RecordTheRecord(包含一个RecordID字段)和一个UserID

我正在数据上下文MyDC中编写一个查询,用参数中提供的RecordID获取记录,并测试用户是否有权访问该记录,如下所示:

var RecordToUpdate = (
    from r in MyDC.RecordsTable
    from u in MyDC.UserRecords
    where r.RecordID == TheRecord.RecordID && TheRecord.RecordID == u.RecordID
    where u.UserID == TheUserID
    select r).SingleOrDefault();
这是否可以确保只获取用户授权的记录?我希望避免用户恶意发送未经授权的记录并对这些未经授权的记录进行更改的情况


谢谢你的建议。

我想你的问题可以通过子查询解决

sql:

它可以用Linq表示如下

var filterUserRecord = from u in MyDC.UserRecords
                       where u.UserID  == TheUserID
                       select u

var q1 = from r in  MyDC.RecordsTable
         where filterUserRecord.Any(f => f.RecordID == r.RecordID)
Linq中子查询的详细信息-从读取尝试以下操作:

var RecordToUpdate = (from u in MyDC.UserRecords
                      where u.UserTable.UserID == TheUserID
                      and u.RecordsTable.RecordID == TheRecord.RecordID).SingleOrDefault();
这将为指定了UserID和RecordID的查询返回结果。

也许可以阅读讨论如何在LINQ场景中处理SQL注入(我认为这应该是主要的安全问题)的文章

还有一篇关于微软对EF的安全考虑的文章。对于使用这些工具开发的任何人来说,这都值得一读

[编辑]关于您的最后一条评论,您可以使用类似于本页上已有的查询。稍微精简一点:如果数据库已正常化,RecordId是唯一的主键,则可以绕过连接以生成一个可读性稍好的查询:

var targetRecords = 
    from userRecords in MyDC.UserRecords
    where userRecords.UserTable.UserID == TheUserID
    && userRecords.RecordsTable.RecordID == TheRecord.RecordID
    select userRecords;

var targetRecordsResult = targetRecords.SingleOrDefault();
我在这里将查询与其结果“var”分开,以指出在调用SingleOrDefault以分配给targetRecordsResult之前,不会对“targetRecords”进行计算。当然,如果你愿意,你可以把这句话写成一句话

如前所述,如果您的RecordID是唯一的主键,那么您将返回匹配的记录,或者返回null。请注意,如果情况并非如此,即超过个记录可能具有相同的ID,则SingleOrDefault调用可能会失败。如果您的数据库是这样设计的,则必须使用更像Anand指定的查询。这会稍微详细一些,但会返回任何具有该特定用户的匹配id的记录


在安全性方面,请注意,您的SQL语句将被编译为包含用户ID,这使得篡改非常困难。因此,我的观点是,在本例中,UserID的范围和公开是您主要关心的问题。如您所述,如果用户(以及任何潜在恶意用户)无法访问变量(通过财产暴露等),则这应该更适合您的需要。

我同意Anand的观点,您需要linq查询:

var filterUserRecord = from u in MyDC.UserRecords
                       where u.UserID  == TheUserID
                       select u;

var q1 = from r in  MyDC.RecordsTable
          where r.RecordID = TheRecordID
          where filterUserRecord.Any(f => f.RecordID == r.RecordID)
          select r;
这将转换为SQL查询,如下所示:

SELECT * FROM RecordsTable rt WHERE rt.RecordID = TheRecordID AND EXISTS
   (SELECT recordId FROM UserRecords ur WHERE ur.userId = TheUserID AND ur.recordID = rt.recordID)

请注意,这些是
IQueryable
s,对其进行linq查询将生成另一个
IQueryable
,其中包含要完全转换为SQL(或任何后端)的表达式,而不是在客户端天真地对其进行评估。

和&u.UserID=UserID
缺少
=/code>,就目前而言,它是一个assignment@Tobias:是的,只是一个拼写错误,已修复;谢谢。如果
UserRecords
表中包含到外键,您将能够从LINQ查询中删除连接,这将提高代码的可读性。@frenchie关于您向krizz提出的一些问题:krizz关于他的查询是正确的,只会导致一个SQL server查询。LINQ真正伟大的特性之一是查询是可组合的。这意味着您可以创建一个查询,然后查询该查询。只要您的初始查询不执行导致立即执行的操作(如调用ToList或ToArray),那么这些查询将在运行时合成为单个查询。这非常强大,因为它允许您创建返回查询片段的实用函数。@JMarsch:好的,谢谢您的解释;现在对我来说也是有道理的。不,那需要先加载所有的用户记录;太慢了。我不这么认为,我认为
Any
将转换为SQL的
EXISTS()
。请参阅我的答案。LINQ查询的计算是惰性的。所以我不认为第一个filterUserRecord会首先被计算,然后再被输入到第二个查询中。为了获取所需的数据,您需要遍历这两个表一次。我的查询是如何工作的?我不想先查询所有记录,然后在子查询中选择一个;太慢了。我上面粘贴的查询将首先从RecordsTable中查询RecordID等于TheRecordID的记录,然后仅对找到的记录应用子查询,以确保用户获得授权。并非所有记录都被查询过。你的查询看起来也不错,但我认为我的查询更清晰/更容易理解它的意思。我并不认为你的速度快得多,但可以。我会做一些测试。是的,确实如此,但请注意,它比需要的更详细。它与TheUserId变量一样安全。如果这是受控的,而不是公开的,那么您的查询就会满足您的安全要求。正如其他人指出的,您不需要手动连接,可以依赖于使用定义的关系。这使得查询更容易理解。另外,请注意krizz的注释:查询不会撤回所有用户,在这种情况下,LINQ查询在创建时默认不会执行。这意味着生成的SQL是高效和安全的。好的,那么您的查询版本是什么?用密码。UserID字段存储在会话中,从不发送到客户端页面。
SELECT * FROM RecordsTable rt WHERE rt.RecordID = TheRecordID AND EXISTS
   (SELECT recordId FROM UserRecords ur WHERE ur.userId = TheUserID AND ur.recordID = rt.recordID)