Алгоритм рекомендации Twitter через ec83d01 позволяет злоумышленникам вызвать отказ в обслуживании (снижение оценки репутации).

Сводка

Алгоритм рекомендации Twitter через ec83d01 позволяет злоумышленникам вызвать отказ в обслуживании (снижение оценки репутации), организовав несколько учетных записей Twitter для координации негативных сигналов в отношении целевой учетной записи, таких как отписка, отключение звука, блокировка и сообщение, как используется в дикие в марте и апреле 2023 г.

Как работает алгоритм Twitter?

Твиттер хочет предоставить вам самые большие текущие события, которые может предложить земной шар. Почти 500 миллионов твитов, которые публикуются каждый день, должны быть сокращены до небольшого количества лучших твитов, чтобы они отображались на временной шкале «Для вас» вашего устройства.

В этом посте мы подробно расскажем о многих взаимосвязанных услугах и задачах, составляющих рекомендательную систему. Хотя в приложении есть несколько мест, где предлагаются твиты (Поиск, Исследуйте, Объявления), в этом эссе основное внимание будет уделено ленте Для вас на главной временной шкале.

Сведения об уязвимости

Алгоритм рекомендаций Twitter через ec83d01 позволяет злоумышленникам управлять системой рекомендаций, координируя негативные сигналы с определенной учетной записью.

Алгоритм рекомендаций оценивает надежность и релевантность учетной записи, используя различные сигналы. Эти признаки включают количество подписчиков, уровень вовлеченности и частоту твитов.

Уязвимость возникает, когда многие учетные записи Twitter координируют негативные сообщения с целевой учетной записью. Эти негативные сообщения могут включать в себя отмену подписки, отключение звука, запрет и жалобу на целевую учетную запись.

Когда система рекомендаций распознает эти негативные сигналы, она снижает показатель репутации целевой учетной записи, что снижает вероятность ее появления в рекомендациях и результатах поиска.

Злоумышленники могут воспользоваться этой уязвимостью, создав множество учетных записей Twitter и направив ложные сигналы на одну целевую учетную запись. Этого можно добиться, используя ботов или наняв группу людей для выполнения неприятных действий.

Скоординированные негативные сигналы могут привести к значительному снижению репутации целевой учетной записи, что приведет к отказу в обслуживании.

Этапы воспроизведения

  1. Организуйте группу с несколькими друзьями (у меня есть группы из 40+)
  2. Найдите цель и выполните следующие задачи по порядку.
  3. Они должны подписаться на подготовку, а через несколько дней сначала отписаться [просто делать это с интервалом в 90 дней тоже больно]
  4. Затем они сообщат о нескольких «пограничных» сообщениях.
  5. Потом заглушат.
  6. Потом заблокируют.

Соответствующий код:

 val blocks: SCollection[InteractionGraphRawInput] = 
   GraphUtil.getFlockFeatures( 
     readSnapshot(FlockBlocksEdgesScalaDataset, sc), 
     FeatureName.NumBlocks, 
     endTs) 
  
 val mutes: SCollection[InteractionGraphRawInput] = 
   GraphUtil.getFlockFeatures( 
     readSnapshot(FlockMutesEdgesScalaDataset, sc), 
     FeatureName.NumMutes, 
     endTs) 
  
 val abuseReports: SCollection[InteractionGraphRawInput] = 
   GraphUtil.getFlockFeatures( 
     readSnapshot(FlockReportAsAbuseEdgesScalaDataset, sc), 
     FeatureName.NumReportAsAbuses, 
     endTs) 
  
 val spamReports: SCollection[InteractionGraphRawInput] = 
   GraphUtil.getFlockFeatures( 
     readSnapshot(FlockReportAsSpamEdgesScalaDataset, sc), 
     FeatureName.NumReportAsSpams, 
     endTs) 
  
 // we only keep unfollows in the past 90 days due to the huge size of this dataset, 
 // and to prevent permanent "shadow-banning" in the event of accidental unfollows. 
 // we treat unfollows as less critical than above 4 negative signals, since it deals more with 
 // interest than health typically, which might change over time. 
 val unfollows: SCollection[InteractionGraphRawInput] = 
   GraphUtil 
     .getSocialGraphFeatures( 
       readSnapshot(SocialgraphUnfollowsScalaDataset, sc), 
       FeatureName.NumUnfollows, 
       endTs) 
     .filter(_.age < 90) 
  
 // group all features by (src, dest) 
 val allEdgeFeatures: SCollection[Edge] = 
   getEdgeFeature(SCollection.unionAll(Seq(blocks, mutes, abuseReports, spamReports, unfollows))) 
  
 val negativeFeatures: SCollection[KeyVal[Long, UserSession]] = 
   allEdgeFeatures 
     .keyBy(_.sourceId) 
     .topByKey(maxDestinationIds)(Ordering.by(_.features.size)) 
     .map { 
       case (srcId, pqEdges) => 
         val topKNeg = 
           pqEdges.toSeq.flatMap(toRealGraphEdgeFeatures(hasNegativeFeatures)) 
         KeyVal( 
           srcId, 
           UserSession( 
             userId = Some(srcId), 
             realGraphFeaturesTest = 
               Some(RealGraphFeaturesTest.V1(RealGraphFeaturesV1(topKNeg))))) 
     } 
  
 // save to GCS (via DAL) 
 negativeFeatures.saveAsCustomOutput( 
   "Write Negative Edge Label", 
   DAL.writeVersionedKeyVal( 
     dataset = RealGraphNegativeFeaturesScalaDataset, 
     pathLayout = PathLayout.VersionedPath(opts.getOutputPath), 
     instant = Instant.ofEpochMilli(opts.interval.getEndMillis), 
     writeOption = WriteOptions(numOfShards = Some(3000)) 
   ) 
 ) 
  
 // save to BQ 
 val ingestionDate = opts.getDate().value.getStart.toDate 
 val bqDataset = opts.getBqDataset 
 val bqFieldsTransform = RootTransform 
   .Builder() 
   .withPrependedFields("dateHour" -> TypedProjection.fromConstant(ingestionDate)) 
 val timePartitioning = new TimePartitioning() 
   .setType("DAY").setField("dateHour").setExpirationMs(21.days.inMilliseconds) 
 val bqWriter = BigQueryIO 
   .write[Edge] 
   .to(s"${bqDataset}.interaction_graph_agg_negative_edge_snapshot") 
   .withExtendedErrorInfo() 
   .withTimePartitioning(timePartitioning) 
   .withLoadJobProjectId("twttr-recos-ml-prod") 
   .withThriftSupport(bqFieldsTransform.build(), AvroConverter.Legacy) 
   .withCreateDisposition(BigQueryIO.Write.CreateDisposition.CREATE_IF_NEEDED) 
   .withWriteDisposition( 
     BigQueryIO.Write.WriteDisposition.WRITE_TRUNCATE 
   ) // we only want the latest snapshot 
  
 allEdgeFeatures 
   .saveAsCustomOutput( 
     s"Save Recommendations to BQ interaction_graph_agg_negative_edge_snapshot", 
     bqWriter 
   ) 

Я понял, что может быть трудно точно предсказать негативные сигналы, особенно те, которые носят глобальный характер. Это происходит потому, что алгоритмы быстро понимают, как манипулировать отрицательной обратной связью, что приводит к быстрой сходимости к локальным минимумам. Когда вы суммируете все плохие комментарии и ваша репутация начинает падать до нуля, эта закономерность становится очевидной.

Мне стало известно, что противник может быстро манипулировать отрицательной обратной связью, чтобы заставить систему прийти в такое состояние.

Если вы не публикуете, вам плевать на дебустинг. Поскольку постоянная опасность может сделать это, она также выиграет от экономии за счет масштаба, поэтому средство защиты должно либо сделать ее экономически невыполнимой, либо ограничить ее потенциальные последствия.

При 10 000 человек ограничение количества блоков малоэффективно; даже со 100 блоками в год можно произвести миллион блоков. Вы должны как минимум изменить вес с таким же запасом (умножить на 0,000001), если один актор (ботнет) может выполнять то же количество задач за меньшие деньги.

Ссылка:





Твиттер