如何在不开放SQL注入的情况下调用存储过程

问题描述 投票:0回答:2

我有一个 .net Core API,我需要从中调用存储过程。我有以下代码,但我被告知它存在 SQL 注入的风险。我的经验不够,无法确定威胁在哪里。我怎样才能以更安全的方式调用这个过程?

[HttpPut]
[Route("move")]
public IActionResult Move(string id, string dir)
{
    if (id == null || dir == null)
    {
        return BadRequest();
    }

    try
    {
        // todo: sql injection threat
        _db.Database.ExecuteSqlCommand($"Exec ProcMove @ParaId = {id}, @Direction = {dir}");
        _db.SaveChanges();

        return Ok();
    }
    catch (Exception)
    {
        return BadRequest();
    }
}

我使用 Angular 5 作为前端,并使用

调用端点
http.put("http://localhost:5000/api/move?id={id}&dir={dir}");
.net angular asp.net-core entity-framework-core
2个回答
1
投票

这里有几个问题。第一个唾手可得的成果是输入在发送到数据库之前没有经过验证。作为字符串,

id
dir
都可以是用户想要发送的任何内容。例如,如果攻击者发送了如下恶意表述:

.../move?id={id}&dir=SomeValidDir;DROP%20TABLE%20someTable;

你可能没空了。在这种情况下,Q&D(快速而肮脏)的解决方案是在将

id
dir
传递给数据库之前确保对它们进行一些约束。
id
真的是整数吗?您的上下文中有效的
dir
是否符合“始终是单个单词,仅包含字母数字字符”等标准?例如,快速修复
id
可能是更改函数签名:

public IActionResult Move(int id, string dir)

攻击者能做的最糟糕的事情就是选择一个奇怪的数字;您的框架(实际上是语言)将负责确保它是整数的其余部分。如果有进一步的限制,例如“

id
必须介于 10 到 42,000 之间”,则在发送到数据库之前包括这些进一步的检查。

第二种更优雅且“更普遍正确”的解决方案是使用准备好的语句。预准备语句的概念是,您首先告诉数据库查询的“格式”以及参数的内容/位置,然后在单独的调用中传递参数。我将在这里省略该描述(但指向有关准备好的语句的 MSDN 文档),因为存储过程本质上是准备好的语句。 因此,您的(建设性)批评者可能会因为您“没有”使用“准备好的陈述”而犹豫不决,而他们这样做是正确的。为了解决他们的批评,您需要直接调用存储过程,并单独传递参数:

using ( SqlCommand cmd = new SqlCommand("ProcMove", con) ) { // ... cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add("@ParaId", SqlDbType.VarChar).Value = id; cmd.Parameters.Add("@Direction", SqlDbType.VarChar).Value = dir; cmd.ExecuteNonQuery(); // ... } 我不确定这段代码是否真的有效,但这似乎是您在尝试字符串替换或字符串连接,这就是风险所在。您想要参数化 SQL。

类似这样的:

1
投票

© www.soinside.com 2019 - 2024. All rights reserved.