Fortify安全漏洞一般处理方法

时间:2024-01-25 10:50:41

Fortify安全漏洞一般处理方法

        前段时间公司又一轮安全审查,要求对各项目进行安全扫描,排查漏洞并修复,手上有几个历史项目,要求在限定的时间内全部修复并提交安全报告,也不清楚之前是如何做的漏洞修复,这次使用工具扫描出来平均每个项目都还有大概100来个漏洞。这些漏洞包括SQL语句注入,C#后端代码,XML文件,以及前端HTML,JS代码几个方面,由于一些项目比较老旧,限定的时间又短,做大的改动如果测试不到位,很难保证不出什么问题,所以做了一些应及处理,不过这些都不失为一种手段,下面就来对这次安全漏洞的处理做个总结。

        公司的漏洞扫描分为两个阶段,第一个阶段是用Fortify这个工具来扫描,检查出漏洞,修复并出报告,第二个阶段是用APPSCAN对线上代码扫描,我们先来说说第一个阶段Fortify工具扫描出来的漏洞如何处理,至于第二阶段,后期做了再来补上。

1.SQL注入

         这一类漏洞主要是针对一些SQL语句做动态拼接时,传入一个特殊的非法字符,如 SELECT id,name FROM User WHERE deparment=1000 and  {ConditionalExpression}  其中  {ConditionalExpression} 作为参数本想在查询页面做一些动态条件的拼接,这样就会带来SQL注入的风险,如果有人通过手段将 {ConditionalExpression} 参数的值 改成这样呢 1=1  OR 2 >1  又或者 1=1  ; drop table deparment 呢这就是一个重大的安全事故了。

载SQL注入一般可以从这几方面预防:

     1.系统中连接数据库的帐号分配合适的权限,一般业务系统中数据库操作帐号,不要分配对数库结构产生改变的权限如 CREATE TABLE ,DROP XXXX 等

     2.对复杂的查询使用存储过程,预先定义好参数,在存储过程中拼接SQL语句

     3.尽量使用例如 SqlParmater 参数化传值使之成为规范

     4.对SQL语句或参数的值做特殊关键词过滤

     5.使用如MyBATIS,Hibernate ,等支持 SQL MAPPER 的 ORM框架 

     6.尽量避免SQL语句动态拼接 或用动态LINQ 替代

     公司的项目大部分都用的MyBatis ORM框架做Mapper 映射,这次漏洞扫描 SQL注入方面还好,基本没有在代码中拼接SQL的,但有一点有个别几处代码 用的是ADO.NET 读写数据库,其中在实例化Connection 对象的地方扫描出connectString 未做加密处理。后面改用项目中现有的数据库操作类库来操作就没再报漏洞了。

2.Path Manipulation 路径篡改

        这次在安全漏洞筛查和处理过程中出现最多的就是 Path Manipulation 路径篡改,手上几个项目中其中就有两个项目用到静态页面生成,涉及到大量的文件操作,如果不做处理会报很多漏洞。首先来说说我对Path Manipulation 漏洞的认识:通过代码对系统上文件的操作如果不设置白名单,黑名单的过滤检查,是一种安全隐患,比如某个公共方法中用到了,System.IO.File.Delete(path)  .NET 提供的文件删除这个方法,path是通过参数传递的,如果不做检查,在一些调用的地方,被人改成了系统某个关键文件,可能直接系统崩溃,这就是一个天大的事情了。

对此类漏洞的修复措施一般做法如下:

      1.设置白名单或黑名单

通常做法是设置白名单,危险不可枚举,我们可以认为哪些是安全的,把它们列入允许操作的清单

     2.设置文件夹安全权限

只对允许操作的文件夹设置读写权限。切不可将整个站点关件夹权限设置可写

处理Path Manipulation 路径篡改 漏洞的.Net 示例代码:

 private static  Dictionary<string,string> CreateFortifyDictionary()
        {
            Dictionary<string, string> fortifyDictionary = new Dictionary<string, string>();

            for(char c1 = 'a'; c1 <= 'z'; c1++)
            {
                fortifyDictionary.Add(c1.ToString(), c1.ToString());
            }

            for (char c2 = 'A'; c2 <= 'Z'; c2++)
            {
                fortifyDictionary.Add(c2.ToString(), c2.ToString());
            }

            for (int c3 = 0; c3 < 10; c3++)
            {
                fortifyDictionary.Add(c3.ToString(), c3.ToString());
            }

            fortifyDictionary.Add(".", ".");
            fortifyDictionary.Add(":", ":");
            fortifyDictionary.Add("/", "/");            
            fortifyDictionary.Add(Separator, Separator);

            return fortifyDictionary;
        }

        public static string SecurityPathFilter(string path)
        {
            path = path.ToLower();
            path = path.Replace("c:"+ Separator + "windows", "");
            path = path.Replace("c:" + Separator + "program files", "");
            path = path.Replace("c:" + Separator + "", "");

            char[] characters = path.ToCharArray();
            StringBuilder resultStringBuilder = new StringBuilder();
            var dictionary = CreateFortifyDictionary();
            
            foreach (Char character in characters)
            {
                string value = string.Empty;
                if (dictionary.TryGetValue(character.ToString(), out value))
                {
                    resultStringBuilder.Append(value);
                }
            }
            return resultStringBuilder.ToString();
        }

3.Cross-site Scripting:Persistent 跨站脚本攻击

        引用 XSS 的定义:传送到 Web 浏览器的恶意内容通常采用 JavaScript 代码片段的形式,但也可能会包含一些 HTML、Flash 或者其他任意一种可以被浏览器执行的代码。基于 XSS 的攻击手段花样百出,几乎是无穷无尽的,但通常它们都会包含传输给攻击者的私人数据(如 Cookie 或者其他会话信息)。在攻击者的控制下,指引受害者进入恶意的网络内容;或者利用易受攻击的站点,对用户的机器进行其他恶意操作。大致意思是说 由于页面在接收参数的过程中,没有进行参数的校验,可能存在 参数中存在可执行代码的漏洞。

处理办法一般是对参数进行转义加码 .NET中 引用System.Web.HttpUtility.DLL 程序集,调用下面方法:

 System.Web.HttpUtility.HtmlEncode(str)

用到的地方需要 反转义解码:

string html= System.Web.HttpUtility.HtmlDecode(str);

4.System Information Leak 系统信息泄露

        顾名思义就是系统的内部信息在泄漏了,给系统带来安全隐患,什么意思呢?下面是摘抄的一段话:

当系统数据或调试信息通过套接字或网络连接使程序流向远程机器时,就会发生外部信息泄露。外部信息泄露会暴露有关操作系统、完整路径名、现有用户名或配置文件位置的特定数据,从而使攻击者有机可乘,它比内部信息(攻击者更难访问)泄露更严重

就是我们系统的调试信息和异常捕获信息不能暴露出来,比如这段代码Fortify 直接会检测出漏洞。

 try
 {
     //
 }
 catch(Exception ex)
 {                
     System.Console.WriteLine(ex.Message);
 }    

我们或可改成这样来解决

try
{
     //
}
catch(Exception ex)
{
     //System.Console.WriteLine(ex.Message);
     logger.Logger("系统异常:"+ex.Message,ex);
}

错误信息不应直接抛给终端,交由日志去记录。

引申一下在系统信息泄露这方面一般采用措施:

    1.IIS上发布站点时关闭Debug 远程调试模式

    2.定义错误页面规范错误提示信息

    3.自定义客户端异常信息类,消化内部异常信息,记录日志,过滤处理后抛给客户端允许可到的异常信息

参考如下示例:

  <compilation debug="false" />   
    <customErrors defaultRedirect="GenericError.htm"
       mode="RemoteOnly" xdt:Transform="Replace">
      <error statusCode="500" redirect="InternalError.htm"/>
      <error statusCode="403" redirect="NoAccess.htm" />
      <error statusCode="404" redirect="FileNotFound.htm" />
    </customErrors>

其中mode 说明如下:

     1. On 表示在本地和远程用户都会看到自定义错误信息。
     2. Off 禁用自定义错误信息,本地和远程用户都会看到详细的错误信息。
     3. RemoteOnly 表示本地用户将看到详细错误信息,而远程用户将会看到自定义错误信息

string message = "";
try
{
     //
}
catch(WTSError ee)
{
     //
     message = ee.OutMessage;
     logger.Logger("系统异常:" + ee.Message, ee);
}
catch(Exception ex)
{
     message = "系统异常,错误类型未知";
     //System.Console.WriteLine(ex.Message);
     logger.Logger("系统异常:"+ex.Message,ex);
}

5. Non-Serializable Object Stored in Session 写入Session 的对象不可被序列化

        这次安全描过程中也报了很多这类漏洞,不清楚写入Session 会话中的对象为什么都要可被序列化,“在session中保存的对象最好是序列化,不然很容易导致类转换的时候发生异常”这是我找到最简短的回答,根据我的理解大概是,Session对象在存储和读取的时候会自动序列化和反序列化,对于非类似于 Int,String等非一般数据类型复杂的数据结构对象存储和读取属性和状态时数据转换容易出错。

对于这类问题的处理可以参考如下:

例如下面这段会报漏洞的代码:

Session["UserToken"] = "sessionObjectValue";

可以自定义一个Session存储对象,标为可序例化,并实现ISerializable 接口 

 [Serializable]
    public class SessionObject : ISerializable
    {
        [OptionalField]
        private string data;

        public string Data
        {
            get { return data; }
            set { data = value; }
        }

        public SessionObject(string data)
        {
            this.data = data;
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Data", data);
            Type basetype = this.GetType().BaseType;
            MemberInfo[] mi = FormatterServices.GetSerializableMembers(basetype, context);
            for (int i = 0; i < mi.Length; i++)
            {
                //由于AddValue不能添加重名值,为了避免子类变量名与基类变量名相同,将基类序列化的变量名加上基类类名
                info.AddValue(basetype.FullName + "+" + mi[i].Name, ((FieldInfo)mi[i]).GetValue(this));
            }

        }

    }

然后在Session写入和读取时写成这样

1 //Session["UserToken"] = new SessionObject("sessionObjectValue");
2  Session["UserToken"] = new SessionObject("sessionObjectValue");
3 
4  //string sessionObjectValue = Session["UserToken"].ToString();
5  string sessionObjectValue = ((SessionObject)Session["UserToken"]).Data;

6.File Separator  文件分割符

       不可避免描述中也出现了这些漏洞,一般在Window环境下,不会出现这类漏洞带来的问题,如果你的系统可以要布署在Linux或Unix 环境下就有可能出现Bug了,这类问题是指在不同操作系统环境下文件路径的分割符号不一样。如果这样一个路径:“C:\tmp\test.txt” Windows 环境是:“C:\tmp\test.txt ” Linux 环境是:"/tmp/test.txt" 为了兼顾不同操作系统环境建议使用 System.IO.Path.DirectorySeparatorChar 对象对于上面的路径这样组合:

"C:" + System.IO.Path.DirectorySeparatorChar + "tmp" + System.IO.Path.DirectorySeparatorChar + "test.txt"

类似的还有换行符:windows 下是 \r\n  Linux 等其它系统下是 \n 在.NET 中使用 System.Environment.NewLine 对象。

7.Insecure Randomness 安全随机数

引用网络上的解释:

不安全的随机数:电脑是一种具有确定性的机器,因此不可能产生真正的随机性。伪随机数生成器 (PRNG) 近似于随机算法,始于一个能计算后续数值的种子。
PRNG 包括两种类型:统计学的 PRNG 和密码学的 PRNG。统计学的 PRNG 可提供有用的统计资料,但其输出结果很容易预测,因此数据流容易复制。若安全性取决于生成数值的不可预测性,则此类型不适用。密码学的 PRNG 通过可产生较难预测的输出结果来应对这一问题。为了使加密数值更为安全,必须使攻击者根本无法、或极不可能将它与真实的随机数加以区分。通常情况下,如果并未声明 PRNG 算法带有加密保护,那么它有可能就是一个统计学的 PRNG,不应在对安全性要求较高的环境中使用,其中随着它的使用可能会导致严重的漏洞(如易于猜测的密码、可预测的加密密钥、会话劫持攻击和 DNS 欺骗)。

       就是我们一般使用的随机函数并不是真正的随机产生,具有一定的可推测性,可能带来一些安全性问题,说实话此类问题不好处理涉及到密码学。如果感兴趣话可找找这方面的资料也可以先看看这篇文章 https://www.cnblogs.com/asxinyu/p/4301554.html,个人认为一般系统可以用GUID 的方案。

       以上就是这次Fortify 安全扫描中遇到的几种漏洞类型,通过上述的方法基本都已解决。记得其中还遇到一个获取电脑域帐号调用C++ 类库方法非安全代码执行漏洞,还好找到另一种不用调用C++类库的方法去替换,如果避免不了要调用C++类库里的方法,如果报此类漏洞不知能有什么好的办法来处理,希望有这方面经验的园友不吝告之。

本文参考和引用的地址如下:

1. https://www.cnblogs.com/eyesmoon/p/7421477.html  Fortify扫描漏洞解决方案 

2. https://blog.csdn.net/abcxy12336/article/details/52335490  针对Fortify工具扫描出几大漏洞的解决办法总结--1

3. https://www.cnblogs.com/asxinyu/p/4301554.html  开源Math.NET基础数学类库使用(14)C#生成安全的随机数