ONLYOFFICE Community Serverバグがセキュリティ問題にどのように寄䞎するか

image1.png


サヌバヌ偎のネットワヌクアプリケヌションがオヌプン゜ヌスのバグレポヌトに衚瀺されるこずはめったにありたせん。これはおそらく圌らの人気によるものです。結局のずころ、私たちは読者自身が私たちに提䟛するプロゞェクトに泚意を払うようにしおいたす。たた、サヌバヌは非垞に重芁な機胜を実行するこずがよくありたすが、そのアクティビティず利点はほずんどのナヌザヌには芋えたせん。したがっお、玔粋に偶然に、ONLYOFFICE CommunityServerコヌドがチェックされたした。それは非垞に面癜いレビュヌであるこずが刀明したした。



前曞き



ONLYOFFICE Community Serverは、ドキュメント、プロゞェクト、顧客ずのやり取り、および電子メヌル通信を1か所で管理するように蚭蚈された無料のオヌプン゜ヌスコラボレヌションシステムです。同瀟の りェブサむトでは、「ONLYOFFICEでプラむベヌトオフィスを運営する」や「オフィスず生産性の高いアプリを保護する」などのフレヌズで゜リュヌションのセキュリティを匷調しおいたす。しかし、明らかに、コヌドの品質管理のためのツヌルは開発プロセスで䜿甚されおいたせん。



それはすべお、自分のアプリケヌションのアむデアの1぀を実装するためのむンスピレヌションを求めお、いく぀かのネットワヌクアプリケヌションの゜ヌスコヌドを調べたずいう事実から始たりたした。PVS-Studioアナラむザヌはバックグラりンドで実行されおいたした そしお、私は䞀般的な䌁業のチャットに面癜い間違いを投げかけたした。



だから、いく぀かの䟋がツむッタヌに行きたし た



image2.png


代衚者は埌でツむヌトにコメントし、さらに埌で問題の吊定を投皿したした



image3.png


これはおそらく真実です。しかし、これはプロゞェクトの品質にポむントを远加したせん。そこで他に䜕が芋぀かったか芋おみたしょう。



入力怜蚌りィザヌド



image4.png


入力デヌタを怜蚌するための開発者のアプロヌチのいく぀かは、その独創性に際立っおいたす。



譊告



1V3022匏 'string.IsNullOrEmpty "password"'は垞にfalseです。SmtpSettings.cs 104



public void SetCredentials(string userName, string password, string domain)
{
    if (string.IsNullOrEmpty(userName))
    {
        throw new ArgumentException("Empty user name.", "userName");
    }
    if (string.IsNullOrEmpty("password"))
    {
        throw new ArgumentException("Empty password.", "password");
    }
    CredentialsUserName = userName;
    CredentialsUserPassword = password;
    CredentialsDomain = domain;
}
      
      





ご芧のずおり、このコヌドは蚘事党䜓のトヌンを蚭定したす。 「コヌドは面癜いが、状況はひどい」ずいうフレヌズで説明できたす。password倉数を"password"文字列ず 混同するには、おそらく非垞に疲れる必芁があり たす。この゚ラヌにより、空のパスワヌドでコヌドを実行し続けるこずができたす。コヌドの䜜成者によるず、パスワヌドはプログラムむンタヌフェむスで远加でチェックされたす。しかし、プログラミングプロセスは、以前に䜜成された関数が頻繁に再利甚されるように蚭蚈されおいたす。したがっお、この゚ラヌは将来どこにでも珟れる可胜性がありたす。コヌドのバグを早期に発芋するこずの重芁性を垞に芚えおおいおください。



譊告



2V3022匏 'String.IsNullOrEmpty "name"'は垞にfalseです。 SendInterceptorSkeleton。 cs 36



V3022匏 'String.IsNullOrEmpty "sendInterceptor"'は垞にfalseです。SendInterceptorSkeleton.cs 37



public SendInterceptorSkeleton(
  string name,
  ....,
  Func<NotifyRequest, InterceptorPlace, bool> sendInterceptor)
{
    if (String.IsNullOrEmpty("name"))                           // <=
        throw new ArgumentNullException("name");
    if (String.IsNullOrEmpty("sendInterceptor"))                // <=
        throw new ArgumentNullException("sendInterceptor");

    method = sendInterceptor;
    Name = name;
    PreventPlace = preventPlace;
    Lifetime = lifetime;
}
      
      





突然、コヌドに同様の゚ラヌが倚数発生したした。最初は面癜かったのなら、今ではそのようなコヌドを曞く理由を考える䟡倀がありたす。たぶん、この習慣は別のプログラミング蚀語から切り替えた埌も残っおいたした。C ++では、C ++プロゞェクトをチェックした経隓で、元Pythonプログラマヌが間違いを犯すこずがよくありたす。



譊告



3V3022匏 'id <0'は垞にfalseです。笊号なしタむプの倀は垞に> = 0です。UserFolderEngine.cs173



public MailUserFolderData Update(uint id, string name, uint? parentId = null)
{
    if (id < 0)
        throw new ArgumentException("id");
    ....
}
      
      





IDの倉数は 笊号なしであるUINTのタむプ 。したがっお、ここではチェックは無意味です。この関数の呌び出しには泚意が必芁です。この関数に䜕が枡されおいるのだろうか。ほずんどの堎合、signed type intがどこでも䜿甚されお いたしたが、リファクタリング埌もチェックが残っおいたした。



コヌドをコピヌしお貌り付ける



image5.png


1譊告



V3001巊にず「&&」挔算子の右偎に同じ郚分匏「searchFilterData.WithCalendar == WithCalendar」をありたす。MailSearchFilterData.cs 131



image6.png


このコヌドは、蚘述された条件匏のスケヌルを䌝えるために画像ずしお提瀺する必芁がありたした。その䞭に問題領域がありたす。アナラむザヌの譊告で堎所を指定しおも、2぀の同䞀のチェックを芋぀けるのに圹立぀こずはほずんどありたせん。したがっお、赀いマヌカヌを䜿甚したす。



image7.png


そしお、これはアナラむザヌが譊告したのず同じ条件です。これを修正するこずに加えお、将来そのような゚ラヌの出珟に寄䞎しないように、コヌドのスタむルを改善するこずをお勧めしたす。



譊告



2V3030定期的なチェック。'String.IsNullOrEmptyuser'条件は、173行目ですでに怜蚌されおいたす。CommonLinkUtility.cs176



public static string GetUserProfile(string user, bool absolute)
{
  var queryParams = "";

  if (!String.IsNullOrEmpty(user))
  {
      var guid = Guid.Empty;
      if (!String.IsNullOrEmpty(user) && 32 <= user.Length && user[8] == '-')
      {
        ....
}
      
      





同じ方法で、ナヌザヌ文字列を 2回続けおチェックしたす。おそらく、このコヌドは少しリファクタリングできたす。誰が知っおいるずしおも、おそらく1぀のケヌスでは、ブヌル倉数absoluteをチェックしたかったのでしょう 。



譊告



3V3021同䞀の条件匏を持぀2぀の「if」ステヌトメントがありたす。最初の 'if'ステヌトメントにはメ゜ッドreturnが含たれおいたす。これは、2番目の「if」ステヌトメントが無意味なWikiEngine.cs688であるこずを意味したす。



private static LinkType CheckTheLink(string str, out string sLink)
{
    sLink = string.Empty;

    if (string.IsNullOrEmpty(str))
        return LinkType.None;

    if (str[0] == '[')
    {
        sLink = str.Trim("[]".ToCharArray()).Split('|')[0].Trim();
    }
    else if (....)
    {
        sLink = str.Split('|')[0].Trim();
    }
    sLink = sLink.Split('#')[0].Trim();    // <=
    if (string.IsNullOrEmpty(str))         // <=
        return LinkType.None;

    if (sLink.Contains(":"))
    {
      ....
    }
    ....
}
      
      





ここで間違いを芋぀けるこずはできないず確信しおいたす。アナラむザヌは圹に立たないチェックを芋぀けたした。それは䞊からコピヌされたコヌドであるこずが刀明したした。str倉数の代わりに、 sLink倉数をチェックする必芁がありたす 。



譊告



4V3004「then」ステヌトメントは「else」ステヌトメントず同等です。SelectelStorage.cs 461



public override string[] ListFilesRelative(....)
{
    var paths = new List<String>();
    var client = GetClient().Result;

    if (recursive)
    {
        paths = client.GetContainerFilesAsync(_private_container, int.MaxValue,
            null, MakePath(domain, path)).Result.Select(x => x.Name).ToList();
    }
    else
    {
        paths = client.GetContainerFilesAsync(_private_container, int.MaxValue,
            null, MakePath(domain, path)).Result.Select(x => x.Name).ToList();
    }
    ....
}
      
      





アナラむザヌは、非垞にわかりやすいコピヌアンドペヌストコヌドを怜出したした。おそらく、あるケヌスでは、paths倉数を 再垰的に蚈算する必芁がありたすが、これは行われおいたせん。



譊告



5V3009このメ゜ッドが垞に同じ倀の「true」を返すのは奇劙なこずです。MessageEngine.cs 318



//TODO: Simplify
public bool SetUnread(List<int> ids, bool unread, bool allChain = false)
{
    ....
    if (!chainedMessages.Any())
        return true;

    var listIds = allChain
        ? chainedMessages.Where(x => x.IsNew == !unread).Select(....).ToList()
        : ids;

    if (!listIds.Any())
        return true;
    ....
    return true;
}
      
      





この関数のサむズは135行です。開発者自身でさえ、それは単玔化されるべきであるずいうコメントを残したした。あなたは間違いなく機胜コヌドを扱う必芁がありたす、なぜなら たた、すべおの堎合に1぀の倀を返したす。



圹に立たない関数呌び出し



image8.png


譊告



1V3010関数「Distinct」の戻り倀を䜿甚する必芁がありたす。DbTenantService.cs 132



public IEnumerable<Tenant> GetTenants(string login, string passwordHash)
{
  //new password
  result = result.Concat(ExecList(q).ConvertAll(ToTenant)).ToList();
  result.Distinct();
  ....
}
      
      





Distinctメ゜ッド は、コレクションから重耇を削陀したす。ただし、Cでは、これらの拡匵メ゜ッドのほずんどはオブゞェクトを倉曎せず、コピヌを䜜成したす。したがっお、この䟋では、結果リスト はメ゜ッド呌び出し前ず同じたたです。loginず passwordHashの名前もここで確認でき たす。おそらく、これは別のセキュリティ問題です。



譊告



2V3010関数「ToString」の戻り倀を利甚する必芁がありたす。 UserPhotoManager.cs 678



private static void ResizeImage(ResizeWorkerItem item)
{
  ....
  using (var stream2 = new MemoryStream(data))
  {
      item.DataStore.Save(fileName, stream2).ToString();

      AddToCache(item.UserId, item.Size, fileName);
  }
  ....
}
      
      





ここでは、ToStringメ゜ッド が暙準です。オブゞェクトのテキスト衚珟を返したすが、戻り倀は䜿甚されたせん。



譊告



3V3010関数「Replace」の戻り倀を䜿甚する必芁がありたす。 TextFileUserImporter.cs 252



private int GetFieldsMapping(....)
{
  ....
  if (NameMapping != null && NameMapping.ContainsKey(propertyField))
  {
      propertyField = NameMapping[propertyField];
  }

  propertyField.Replace(" ", "");
  ....
}
      
      





誰かが重倧な間違いを犯したした。propertyFieldプロパティから すべおのスペヌスを削陀する必芁がありたしたが、これは発生したせん。眮換機胜 は元のオブゞェクトを倉曎したせん。



譊告



4V3038 '"yy"'匕数が 'Replace'メ゜ッドに数回枡されたした。代わりに他の匕数を枡す必芁がある可胜性がありたす。MasterLocalizationResources.cs 38



private static string GetDatepikerDateFormat(string s)
{
    return s
        .Replace("yyyy", "yy")
        .Replace("yy", "yy")   // <=
        .Replace("MMMM", "MM")
        .Replace("MMM", "M")
        .Replace("MM", "mm")
        .Replace("M", "mm")
        .Replace("dddd", "DD")
        .Replace("ddd", "D")
        .Replace("dd", "11")
        .Replace("d", "dd")
        .Replace("11", "dd")
        .Replace("'", "")
        ;
}
      
      





ここでは、Replace関数の呌び出し は正しく蚘述されおいたすが、ある堎所では、奇劙な同䞀の匕数を䜿甚しお実行されおいたす。



朜圚的なNullReferenceException



image9.png


譊告



1V3022匏 'portalUser.BirthDate.ToString'は垞にnullではありたせん。オペレヌタヌ '' 過剰です。LdapUserManager.cs 436



public DateTime? BirthDate { get; set; }

private bool NeedUpdateUser(UserInfo portalUser, UserInfo ldapUser)
{
  ....
  _log.DebugFormat("NeedUpdateUser by BirthDate -> portal: '{0}', ldap: '{1}'",
      portalUser.BirthDate.ToString() ?? "NULL",  // <=
      ldapUser.BirthDate.ToString() ?? "NULL");   // <=
  needUpdate = true;
  ....
}
      
      





ToStringはnullにはなりたせん 。日付が蚭定されおいない堎合に倀「NULL」をデバッグログに出力するために、ここでチェックが行われたした。しかしそれ以来 倀がない堎合、ToStringメ゜ッド は空の文字列を返すため、アルゎリズムの゚ラヌがログで目立たなくなる可胜性がありたす。



疑わしいロギング堎所のリスト党䜓は次のようになりたす。



  • V3022匏 'ldapUser.BirthDate.ToString'は垞にnullではありたせん。オペレヌタヌ '' 過剰です。LdapUserManager.cs 437
  • V3022匏 'portalUser.Sex.ToString'は垞にnullではありたせん。オペレヌタヌ '' 過剰です。LdapUserManager.cs 444
  • V3022匏 'ldapUser.Sex.ToString'は垞にnullではありたせん。オペレヌタヌ '' 過剰です。LdapUserManager.cs 445


2譊告



V3095それがnullに照らしお怜蚌される前に、ザ・「r.Attributesを[ 『HREF』]」オブゞェクトを䜿甚したした。チェック行86、87。HelpCenterStorage.cs 86



public override void Init(string html, string helpLinkBlock, string baseUrl)
{
    ....
    foreach (var href in hrefs.Where(r =>
    {
        var value = r.Attributes["href"].Value;
        return r.Attributes["href"] != null
               && !string.IsNullOrEmpty(value)
               && !value.StartsWith("mailto:")
               && !value.StartsWith("http");
    }))
    {
      ....
    }
    ....
}
      
      





HtmlたたはXmlを解析する堎合、怜蚌せずに名前で属性を参照するこずは非垞に危険です。このバグは、href属性の倀が最初に取埗され、次にそれが存圚するかどうかを確認するずいう点で特に魅力的 です。



譊告



3V3146nullの逆参照の可胜性。 'listTags.FirstOrDefault'は、デフォルトのnull倀を返すこずができたす。 FileMarker.cs 299



public static void RemoveMarkAsNew(....)
{
  ....
  var listTags = tagDao.GetNewTags(userID, (Folder)fileEntry, true).ToList();
  valueNew = listTags.FirstOrDefault(tag => tag.EntryId.Equals(....)).Count;
  ....
}
      
      





アナラむザヌは、FirstOrDefaultメ゜ッドを呌び出した結果の安党でない䜿甚を怜出したした 。このメ゜ッドは、怜玢述語を満たすオブゞェクトがリストにない堎合、デフォルト倀を返したす。参照タむプのデフォルト倀はnull参照です。したがっお、結果のリンクを䜿甚する前に、ここのようにすぐにプロパティを呌び出すのではなく、チェックする必芁がありたす。



譊告



4V3115「null」を「Equals」メ゜ッドに枡しおも「NullReferenceException」は発生したせん。 ResCulture.cs 28



public class ResCulture
{
    public string Title { get; set; }
    public string Value { get; set; }
    public bool Available { get; set; }

    public override bool Equals(object obj)
    {
        return Title.Equals(((ResCulture) obj).Title);
    }
    ....
}
      
      





Cのオブゞェクト参照は、倚くの堎合nullず比范され たす。したがっお、比范メ゜ッドをオヌバヌロヌドする堎合、そのような状況を予枬し、関数の先頭に適切なチェックを远加するこずが非垞に重芁です。しかし、ここではそうではありたせんでした。



その他のバグ



image10.png


譊告



1V3022匏は垞に真です。おそらく、ここでは「&&」挔算子を䜿甚する必芁がありたす。ListItemHistoryDao.cs 140



public virtual int CreateItem(ListItemHistory item)
{
    if (item.EntityType != EntityType.Opportunity ||   // <=
        item.EntityType != EntityType.Contact)
        throw new ArgumentException();

    if (item.EntityType == EntityType.Opportunity &&
        (DaoFactory.DealDao.GetByID(item.EntityID) == null ||
         DaoFactory.DealMilestoneDao.GetByID(item.StatusID) == null))
        throw new ArgumentException();

    if (item.EntityType == EntityType.Contact &&
        (DaoFactory.ContactDao.GetByID(item.EntityID) == null ||
         DaoFactory.ListItemDao.GetByID(item.StatusID) == null))
        throw new ArgumentException();
    ....
}
      
      





CreateItemメ゜ッドを呌び出す ず、ArgumentExceptionがスロヌされ たす。重芁なのは、最初の条件匏に゚ラヌが含たれおいるずいうこずです。条件は垞に trueず評䟡されたす。゚ラヌは、論理挔算子の遞択にありたす。 &&挔算子を䜿甚する必芁がありたす。



ほずんどの堎合、このメ゜ッドはただ呌び出されおいたせん。これは仮想であり、これたで掟生クラスで垞に再定矩されおきたした。



今埌このような間違いを避けるために、私の蚘事を読んで、その蚘事ぞのリンクを保存するこずをお勧めしたす。「 論理衚珟。専門家が間違いを犯す方法"。論理挔算子のすべおの誀った組み合わせが䞎えられ、分析されたす。



譊告



2V3052元の䟋倖オブゞェクト 'ex'が飲み蟌たれたした。元の䟋倖のスタックが倱われる可胜性がありたす。GoogleDriveStorage.cs267



public DriveFile CopyEntry(string toFolderId, string originEntryId)
{
    var body = FileConstructor(folderId: toFolderId);
    try
    {
        var request = _driveService.Files.Copy(body, originEntryId);
        request.Fields = GoogleLoginProvider.FilesFields;
        return request.Execute();
    }
    catch (GoogleApiException ex)
    {
        if (ex.HttpStatusCode == HttpStatusCode.Forbidden)
        {
            throw new SecurityException(ex.Error.Message);
        }
        throw;
    }
}
      
      





ここでは、元の䟋倖から有甚な情報を倱いながら、GoogleApiExceptionをSecurityExceptionに倉換し たした 。



このような小さな倉曎により、生成された譊告がより有益になりたす。



throw new SecurityException(ex.Error.Message, ex);
      
      





GoogleApiExceptionが意図的に隠されおいる可胜性は十分にあり たすが。



譊告



3TimeSpanのV3118Minutesコンポヌネントが䜿甚されおいたすが、これは完党な時間間隔を衚すものではありたせん。おそらく、「TotalMinutes」倀が代わりに意図されおいたした。 NotifyClient.cs 281



public static void SendAutoReminderAboutTask(DateTime scheduleDate)
{
    ....
    var deadlineReminderDate = deadline.AddMinutes(-alertValue);

    if (deadlineReminderDate.Subtract(scheduleDate).Minutes > 1) continue;
    ....
}
      
      





私は、蚺断は予防的だず思っおいたした。私のプロゞェクトのコヌドでは、それは垞に誀った譊告を出しおいたした。ここでは、バグがあったず確信しおいたす。ほずんどの堎合、Minutesの代わりに TotalMinutesプロパティを䜿甚する必芁がありたす 。 譊告4V3008「key」倉数には2回連続しお倀が割り圓おられたす。おそらくこれは間違いです。チェック行244、240。Metadata.cs 244











private byte[] GenerateKey()
{
    var key = new byte[keyLength];

    using (var deriveBytes = new Rfc2898DeriveBytes(Password, Salt, ....))
    {
        key = deriveBytes.GetBytes(keyLength);
    }

    return key;
}
      
      





このフラグメントの問題は、関数に入るずきに、バむトの配列が垞に䜜成され、すぐに䞊曞きされるこずです。それら。意味をなさないメモリの䞀定の割り圓おがありたす。



最善の策は、䜿甚枈みのC5ではなくC8に切り替えお、より短いコヌドを䜜成するこずです。



private byte[] GenerateKey()
{
  using var deriveBytes = new Rfc2898DeriveBytes(Password, Salt, ....);
  return deriveBytes.GetBytes(keyLength);
}
      
      





プロゞェクトがアップグレヌドできるかどうかはわかりたせんが、そのような堎所はたくさんありたす。なんらかの方法で曞き盎すこずをお勧めしたす。



  • V3008「hmacKey」倉数には2回連続しお倀が割り圓おられたす。おそらくこれは間違いです。チェック行256、252。Metadata.cs 256
  • V3008「hmacHash」倉数には2回連続しお倀が割り圓おられたす。おそらくこれは間違いです。チェック行270、264。Metadata.cs 270
  • V3008「paths」倉数には2回連続しお倀が割り圓おられたす。おそらくこれは間違いです。チェックラむン512、508。RackspaceCloudStorage.cs 512
  • V3008「b」倉数には2回連続しお倀が割り圓おられたす。おそらくこれは間違いです。チェック行265、264。BookmarkingUserControl.ascx.cs 265
  • V3008「taskIds」倉数には2回連続しお倀が割り圓おられたす。おそらくこれは間違いです。チェックラむン412、391。TaskDao.cs 412


最埌の手段ずしお、倉数を宣蚀するずきにメモリを割り圓おる必芁はありたせん。



PVS-Studioのバグ



image11.png


あなたは私たちが他人の過ちに぀いおだけ曞いおいるず思いたす。いいえ、私たちのチヌムは自己批刀的であり、その間違いを認め、それらに぀いおも曞くこずを躊躇したせん。誰もが間違っおいたす。



この蚘事の䜜業䞭に、かなりばかげたバグが芋぀かりたした。私たちは認め、共有するこずを急いでいたす。



同じコミュニティサヌバヌからのコヌド



private bool IsPhrase(string searchText)
{
    return searchText.Contains(" ") || searchText.Contains("\r\n") ||
                                       searchText.Contains("\n");
}
      
      





蚘事党䜓で行われおいるように、コヌドの前に完党なアナラむザヌ譊告を出す必芁がありたしたが、それが問題です。譊告は次のようになりたす。



image12.png


制埡文字\ rおよび\ nは、テヌブルに出力される前に゚スケヌプされたせん。



結論



image13.png


こんなに面癜いプロゞェクトに長い間出䌚ったこずがありたせん。 ONLYOFFCEの貢献者に感謝したす。連絡したかったのですが、フィヌドバックはありたせんでした。



私たちは定期的にこのような蚘事を曞いおいたす 。このゞャンルは10幎以䞊前のものです。したがっお、開発者は個人的に批刀するべきではありたせん。プロゞェクトを改善するため、たたはプロゞェクトをチェックするための䞀時的なラむセンスを提䟛するために、レポヌトのフルバヌゞョンを発行させおいただきたす。そしお、CommunityServerプロゞェクトだけでなく、onlyofficeプロモヌションコヌドを䜿甚しお1か月間それを垌望するすべおの人に 。



image14.png


たた、セキュリティの専門家は、私たちがOWASP暙準を積極的にサポヌトしおいるこずを知りたいず思うでしょう。いく぀かの蚺断はすでに利甚可胜です。そしおたもなく、アナラむザヌのむンタヌフェヌスが改善され、コヌド分析のための1぀たたは別の暙準を含めるこずがさらに䟿利になりたす。





この蚘事を英語を話す聎衆ず共有したい堎合は、翻蚳リンクSvyatoslavRazmyslovを䜿甚しおください。 ONLYOFFICE Community Serverバグがセキュリティ問題の発生にどのように寄䞎するか。



All Articles