EF4 ExecuteStoreQuery SQL Injection 驚魂記

栏目: 数据库 · 发布时间: 6年前

内容简介:臨下班接到通報,有段 Entity Framework 4 的老程式被偵測出有 SQL Injection 漏洞:我第一時間的反應是「靠! 是哪個王八蛋? 都宣導多少次了還在寫 SQL Injection,被我抓到是誰就準備被阿魯巴到死吧...」,調出版控修改歷程很快揪出犯人 - 就是我本人。天哪,我當時到底在想什麼呀? 不敢相信自己會犯這麼低級的錯,但鐵證如山,事到如今我別無選擇了,從活動櫃拿出珍藏多年的窖藏 12 年單一純麥巴拉刈,扭開瓶蓋之際忽然想到,等等,這又不是 string.Format("SE

臨下班接到通報,有段 Entity Framework 4 的老程式被偵測出有 SQL Injection 漏洞:

var res = ctx.ExecuteStoreQuery<Player>(
    "SELECT * FROM Player WHERE UserName = {0}", userName);

我第一時間的反應是「靠! 是哪個王八蛋? 都宣導多少次了還在寫 SQL Injection,被我抓到是誰就準備被阿魯巴到死吧...」,調出版控修改歷程很快揪出犯人 - 就是我本人。

天哪,我當時到底在想什麼呀? 不敢相信自己會犯這麼低級的錯,但鐵證如山,事到如今我別無選擇了,從活動櫃拿出珍藏多年的窖藏 12 年單一純麥巴拉刈,扭開瓶蓋之際忽然想到,等等,這又不是 string.Format("SELECT ... {0}", varName),它是 ExcecuteStoreQuery<T>() ,在還沒有Dapper 可用的 EF4 時代,這是在 EF 直接執行 SQL 語法的捷徑。而依我一直以來的認知,{0} {1} 會被轉成資料參數物件,微軟也不至於設計誘人誤陷 SQL Injection 的 API,所以我當年才敢大膽使用。

偏偏當年我沒有留下 KB 可以印證這點,更要命的是我還查到 ExecuteStoreQuery("...{0}...", varName) 可能導致 SQL Injection 的說法。頓時一陣涼意從背脊一路涼上後腦杓,這下大條了,莫非我多年來認知有錯,無意間已埋下無數 SQL Injection 地雷?

此事非同小可,我先是查了 ObjectContext.ExecuteStoreQuery<T>() 原始碼,其背後是呼叫 CreateStoreCommand(string commandText, params object[] parameters) 建立 DbCommand。若 object[] parameters 不含任何 DbParamter 物件(!parameters.Any(p => p is DbParameter))時,會自動將 parameters 以 command.CreateParameter() 轉成 DbParameter 陣列,再用 string.Format() 將指令中的 {0} {1}... 轉成 @p0、@p1...,故 parameters 背後仍是以參數方式執行,並無 SQL Injection 疑慮。

private DbCommand CreateStoreCommand(string commandText, params object[] parameters)
        {
            DbCommand command = this._connection.StoreConnection.CreateCommand();
            command.CommandText = commandText;

            // get relevant state from the object context
            if (this.CommandTimeout.HasValue)
            {
                command.CommandTimeout = this.CommandTimeout.Value;
            }
            EntityTransaction entityTransaction = this._connection.CurrentTransaction;
            if (null != entityTransaction)
            {
                command.Transaction = entityTransaction.StoreTransaction;
            }

            if (null != parameters && parameters.Length > 0)
            {
                DbParameter[] dbParameters = new DbParameter[parameters.Length];

                // three cases: all explicit DbParameters, no explicit DbParameters
                // or a mix of the two (throw in the last case)
                if (parameters.All(p => p is DbParameter))
                {
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        dbParameters[i] = (DbParameter)parameters[i];
                    }
                }
                else if (!parameters.Any(p => p is DbParameter))
                {
                    string[] parameterNames = new string[parameters.Length];
                    string[] parameterSql = new string[parameters.Length];
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        parameterNames[i] = string.Format(CultureInfo.InvariantCulture, "p{0}", i);
                        dbParameters[i] = command.CreateParameter();
                        dbParameters[i].ParameterName = parameterNames[i];
                        dbParameters[i].Value = parameters[i] ?? DBNull.Value;

                        // By default, we attempt to swap in a SQL Server friendly representation of the parameter.
                        // For other providers, users may write:
                        //
                        //      ExecuteStoreQuery("select * from foo f where f.X = ?", 1);
                        //
                        // rather than:
                        //
                        //      ExecuteStoreQuery("select * from foo f where f.X = {0}", 1);
                        parameterSql[i] = "@" + parameterNames[i];
                    }
                    command.CommandText = string.Format(CultureInfo.InvariantCulture, command.CommandText, parameterSql);
                }
                else
                {
                    throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ObjectContext_ExecuteCommandWithMixOfDbParameterAndValues);
                }

                command.Parameters.AddRange(dbParameters);
            }

            return command;
        }

分析完程式碼還是不太放心,想親手實測求證。但 ExecuteStoreQuery 是 EF4 時代的方法,VS2017 專案的 ADO.NET Data Entity Model 從 EF5.0 起跳,除非回頭安裝 VS2010,很難新增 EF4 專案。所幸在 CodeProject 我找到一篇 Entity Framework Tutorial for Beginners 有 VS2010 建立的可下載專案範例,稍作修改後終於能重現 ExecuteStoreQuery<T>,並透過 SQL Profiler 驗證參數陣列有被傳成 @p0。

EF4 ExecuteStoreQuery SQL Injection 驚魂記

做完實驗印證自己的認知無誤,一堆 ExecuteStoreQuery 寫法安全無虞,這才鬆了一口氣。

EF4 已是快 20 年的活化石,EF 新版的做法已改,已無 ExecuteStoreQuery 可用。而現在再有類似需求,我會義無反顧抄起Dapper 上場,故瞎忙一整晚的心得價值並不高,但卻能讓我安心睡覺,哈!

Research of how EF4 ExecuteStoreQuery convert {0} {1} and paramters to clarify if it will cause SQL inejction.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创新者

创新者

[美] 沃尔特 · 艾萨克森 / 关嘉伟、牛小婧 / 中信出版社 / 2016-6 / 88.00

讲述了计算机和互联网从无到有的发展历程,并为我们生动地刻画出数字时代的创新者群像。 在近200年的数字化进程中群星闪耀,艾萨克森从一个计算机程序的创造者、诗人拜伦之女埃达说起,细数了这一群站在科学与人文交叉路口的创新者,他们包括通用型电子计算机的创造者奠奇利、科学家冯·诺依曼、仙童半导体公司的“八叛逆”、天才图灵、英特尔的格鲁夫、微软的比尔·盖茨、苹果公司的乔布斯、谷歌的拉里·佩奇等。《创新......一起来看看 《创新者》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具