.NET ORM 注入 审计-FreeSql中直接使用 SQL 片段的问题
采用项目 ASP.NET Core Web Application 模板创建项目的demo
搞定。环境OK了。看看注入的地方。
ORM 工具通过将 SQL 语句中的变量部分(例如查询条件中的参数)与代码分离,来防止 SQL 注入攻击。
但是在官网上,有提示:动态操作 | FreeSql 官方文档
FreeSql 提供 Where(sql)、GroupBy(sql)、OrderBy(sql)、ToList(sql) 等直接使用 SQL 片段的 API。使用这些 API 时请务必注意SQL注入安全问题。不建议前端直接 POST SQL 到后端使用它们,而应该在后端做一层映射。
-
• POC=
1' and 1=(select @@version)) a --
此时是存在的。
写一个Demo(改动的dotnetcore-examples/ORM/FreeSql),搞定了。
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using OvOv.Core.Domain;
using OvOv.Core.Models.Blogs;
using OvOv.Core.Web;
namespace OvOv.FreeSql.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BlogController : ControllerBase
{
// GET api/Blog
private readonly IFreeSql _fsql;
private readonly IMapper _mapper;
public BlogController(IFreeSql fsql, IMapper mapper)
{
_fsql = fsql;
_mapper = mapper;
}
/// <summary>
/// 博客列表页
/// </summary>
/// <param name="pageDto">分页参数</param>
/// <returns></returns>
[HttpGet]
public ActionResult<PagedResultDto<Blog>> Get([FromQuery] PageDto pageDto)
{
List<Blog> blogs = _fsql.Select<Blog>().OrderByDescending(r => r.CreateTime).Page(pageDto.PageNumber, pageDto.PageSize).ToList();
long count = _fsql.Select<Blog>().Count();
return new PagedResultDto<Blog>(count, blogs);
}
// GET api/blog/5
[HttpGet("{id}")]
public ActionResult<Blog> Get(String id)
{
var blogs = _fsql.Select<Blog>()
.WithSql($"SELECT * FROM Blog WHERE id LIKE '%{id}%'")
.ToList();
return Ok(blogs + "wulala");
}
// POST api/blog
[HttpPost]
public void Post([FromBody] CreateBlogDto createBlogDto)
{
Blog blog = _mapper.Map<Blog>(createBlogDto);
blog.CreateTime = DateTime.Now;
_fsql.Insert<Blog>(blog).ExecuteAffrows();
}
// PUT api/blog
[HttpPut]
public void Put([FromBody] UpdateBlogDto updateBlogDto)
{
//eg.1 更新指定列
//_fsql.Update<Blog>(updateBlogDto.BlogId).Set(a => new Blog()
//{
// Title = updateBlogDto.Title,
// Content = updateBlogDto.Content
//}).ExecuteAffrows();
//eg.2将这个实体更新到数据库中。当更新时,会把其他列的值,如CreateTime也更新掉。
//使用IgnoreColumns可忽略某一些列。
Blog blog = _mapper.Map<Blog>(updateBlogDto);
_fsql.Update<Blog>().SetSource(blog).IgnoreColumns(r => r.CreateTime).ExecuteAffrows();
}
// DELETE api/blog/5
[HttpDelete("{id}")]
public void Delete(int id)
{
_fsql.Delete<Blog>(new { BlogId = id }).ExecuteAffrows();
}
private void Test()
{
CreateBlogDto createBlogDto = new CreateBlogDto()
{
Title = "我是title",
Content = "我是content"
};
Blog newBlog = new Blog()
{
Title = createBlogDto.Title,
Content = createBlogDto.Content
};
}
}
}
这个时候就造成了注入。(打包了,放到了目录下,需要的时候整体替换到github的那个项目里面就行了。)
审计的点:正常FreeSql肯定是不建议这么写的,如果碰到了这种的话,看一下用没用参数化,比如@id占位。
从CODE上修复的话:
参数化查询使用 .WithSql
方法,并通过参数传递值:
csharpCopy codevar blogs = _fsql.Select<Blog>()
.WithSql("SELECT * FROM Blog WHERE id LIKE @id", new { id = $"%{id}%" })
.ToList();
用Lambda表达式进行条件查询,使用ORM提供的Lambda表达式进行条件查询,因为这些查询自然是参数化的,从而减少了SQL注入的风险。例如:
csharpCopy codevar keyword = $"%{id}%";
var blogs = _fsql.Select<Blog>()
.Where(a => a.Id.ToString().Contains(keyword))
.ToList();
这种方式下,你不需要担心SQL注入,因为FreeSql内部会处理参数化。
使用FreeSql的高级功能,FreeSql还提供了更高级的查询功能,比如表达式树转换和动态LINQ,这些都内建了参数化查询的支持,从而确保了查询的安全性。
-
• 当
id
是数值类型时,直接使用ToString()
和Contains()
可能不适用。你需要根据实际情况调整查询逻辑。 -
• 对于复杂的查询逻辑,考虑使用FreeSql提供的高级查询功能,如动态LINQ和表达式树,这些都可以帮助你构建既灵活又安全的查询。
考虑到安全性时,应优先使用ORM的内建方法来构建查询,这样自然包含了参数化查询的实现。只有在确实需要执行原生SQL语句时,才考虑使用参数化的原生SQL查询。
原文始发于微信公众号(wulala520):.NET ORM 注入 审计-FreeSql中直接使用 SQL 片段的问题
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论