// Copyright (C) 2022 Check Point Software Technologies Ltd. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "FpMitigation.h" #include #include #include #define DEFAULT_SCORE 10.0 #define TRUE_POSITIVE_REPUTATION_THRESHOLD 1.5 #define FALSE_POSITIVE_REPUTATION_THRESHOLD 5 USE_DEBUG_FLAG(D_WAAP); using namespace std::chrono; FpMitigationScore::FpMitigationScore(const std::string& backupFilePath) : SerializeToFilePeriodically(duration_cast(minutes(10)), backupFilePath), m_policyDataUrl(), m_policyDataParam(), m_history(), m_counter(0) { dbgTrace(D_WAAP) << "False positive mitigation constructor"; restore(); } FpMitigationScore::~FpMitigationScore() { reset(); } void FpMitigationScore::reset() { m_policyDataParam.clear(); m_policyDataUrl.clear(); m_history.clear(); } void FpMitigationScore::serialize(std::ostream& stream) { cereal::JSONOutputArchive archive(stream); archive(cereal::make_nvp("version", 1), cereal::make_nvp("policyDataUrl", m_policyDataUrl), cereal::make_nvp("policyDataParam", m_policyDataParam)); } void FpMitigationScore::deserialize(std::istream& stream) { cereal::JSONInputArchive archive(stream); size_t version = 0; try { archive(cereal::make_nvp("version", version)); } catch (std::runtime_error & e) { archive.setNextName(nullptr); version = 0; dbgDebug(D_WAAP) << "Can't load file version: " << e.what(); } switch (version) { case 0: archive(cereal::make_nvp("m_policyDataUrl", m_policyDataUrl), cereal::make_nvp("m_policyDataParam", m_policyDataParam)); break; case 1: archive(cereal::make_nvp("policyDataUrl", m_policyDataUrl), cereal::make_nvp("policyDataParam", m_policyDataParam)); break; default: dbgWarning(D_WAAP) << "unknown file format version: " << version; break; } } double FpMitigationScore::calculateFpMitigationScore(const std::string& shortUri, const std::string& canonisedParam) { double urlScore = DEFAULT_SCORE, paramScore = DEFAULT_SCORE; if (m_policyDataUrl.find(shortUri) != m_policyDataUrl.end()) { urlScore = m_policyDataUrl[shortUri]->getScore(); } if (m_policyDataParam.find(canonisedParam) != m_policyDataParam.end()) { paramScore = m_policyDataParam[canonisedParam]->getScore(); } return ((int)(paramScore * 2) / 3 + 3.3) * ((int)(urlScore * 2) / 3 + 3.3) / 10; } template bool hasElement(std::vector vec, T& elem) { return (std::find(vec.begin(), vec.end(), elem) != vec.end()); } void FpMitigationScore::learnFalsePositive( const std::vector& keywordMatches, PolicyCounterType rep, const std::string& shortUri, const std::string& canonisedParam) { static std::string probing = "probing"; if (keywordMatches.size() > 3 && hasElement(keywordMatches, probing)) { return; } if (rep != UNKNOWN_TYPE) { if (m_policyDataUrl.find(shortUri) == m_policyDataUrl.end()) { m_policyDataUrl[shortUri] = std::make_shared(); } if (m_policyDataParam.find(canonisedParam) == m_policyDataParam.end()) { m_policyDataParam[canonisedParam] = std::make_shared(); } incrementCounter(shortUri, canonisedParam, rep); m_counter++; if (m_counter % FP_SCORE_CALCULATION_INTERVALS == 0) { dbgTrace(D_WAAP) << "evaluating fp mitigation scores"; evaluatePolicyDataCounterScore(); } } } PolicyCounterType FpMitigationScore::IdentifyFalseTruePositive(double relativeReputation, const std::string& shortUri, const std::string& canonisedParam, const std::string& userAgentIp) { std::string uriParamCat = shortUri + canonisedParam; if (relativeReputation < TRUE_POSITIVE_REPUTATION_THRESHOLD && m_history.find(uriParamCat) == m_history.end()) { m_history.insert(uriParamCat); return TRUE_POSITIVE; } if (relativeReputation > FALSE_POSITIVE_REPUTATION_THRESHOLD && m_history.find(userAgentIp) == m_history.end()) { m_history.insert(userAgentIp); return FALSE_POSITIVE; } return UNKNOWN_TYPE; } void FpMitigationScore::incrementCounter(const std::string& shortUri, const std::string& canonisedParam, PolicyCounterType counterType) { // It is assumed that m_policyDataUrl contains shortUrl and // m_policyDataParam contains canonisedParam. See caller. std::shared_ptr urlCounter = m_policyDataUrl[shortUri]; std::shared_ptr paramCounter = m_policyDataParam[canonisedParam]; urlCounter->incrementCounter(counterType); paramCounter->incrementCounter(counterType); } void FpMitigationScore::evaluatePolicyDataCounterScore() { for (auto urlPolicy : m_policyDataUrl) { urlPolicy.second->evaluateScore(); } for (auto paramPolicy : m_policyDataParam) { paramPolicy.second->evaluateScore(); } } PolicyDataCounter::PolicyDataCounter() : falsePositive(0), truePositive(0), score(10.0) { } double PolicyDataCounter::getScore() { return score; } void PolicyDataCounter::incrementCounter(PolicyCounterType counterType) { switch (counterType) { case UNKNOWN_TYPE: // add assert break; case FALSE_POSITIVE: case HTML_CONTENT: falsePositive++; break; case TRUE_POSITIVE: case SPAM: truePositive++; break; default: break; } } void PolicyDataCounter::evaluateScore() { size_t tp = truePositive + 50 + 1, fp = falsePositive; score = (double)(10.0 * tp) / (10.0 * fp + tp); }