如何在 Graylog 4.0 日志记录中找到两个字段的唯一组合?

问题描述

因此,我已将 Web 应用程序的身份验证日志转发到 graylog,现在我想为 brutefroce 实施一些警报。 从 webapp 发送到 Graylog 的每条记录都包含一些信息,其中包括用户名和哈希密码。 我想通过查询来查找对 login 和 hashed_pa​​ssword 字段的唯一组合进行计数的警报。

我知道如何设置警报,但找不到有效的查询

例如,我有这个日志:

... login=foo hashed_password=XXXX ...
... login=foo hashed_password=XXXX ...
... login=foo hashed_password=XXXX ...
... login=foo hashed_password=YYYY ...
... login=foo hashed_password=ZZZZ ...
... login=foo hashed_password=AAAA ...
... login=foo hashed_password=AAAA ...
... login=foo hashed_password=BBBB ...

我想找到一个查询,这两个字段的每个组合只打印一次。我试过很多次,但都没有成功。 从日志 aboce,它应该只打印:

... login=foo hashed_password=XXXX ...
... login=foo hashed_password=YYYY ...
... login=foo hashed_password=ZZZZ ...
... login=foo hashed_password=AAAA ...
... login=foo hashed_password=BBBB ...

我尝试过的一些查询如下:

auth:"error" AND distinct(login+hashed_password)
auth:"error" AND count(distinct(login+hashed_password))
auth:"error" AND count(login(hashed_password))

我运行的是graylog 4.0版本和Elastic Oss 7.10,所有服务器都是Centos7

解决方法

首先,坏消息。 Graylog 不支持 Elasticsearch v7.11。 7.10 是最新支持的版本。

第二,更多坏消息。 Graylog 不支持您尝试在查询中使用的任何功能。支持的语法可以在这里找到:https://docs.graylog.org/en/4.0/pages/searching/query_language.html?highlight=syntax#search-query-language

您可以使用仪表板做一些您想做的事情。我还建议尝试聚合事件(警报/事件)以在这些情况发生时提醒您。

,

我找到了解决问题的方法: 我只是按 IP 源和登录对查询结果进行分组,然后我添加了这个条件来创建事件:

/* eslint-disable no-shadow */ /* eslint-disable react-native/no-inline-styles */ /* eslint-disable prettier/prettier */ import React,{useState,useEffect} from 'react'; import {TextInput,SafeAreaView,ScrollView,TouchableWithoutFeedback,Text,View,StyleSheet,Image} from 'react-native'; import Header from '../components/Header'; import {localizdStrings} from '../LocalizedConstants'; import { useNavigation } from '@react-navigation/native'; import CheckBox from '@react-native-community/checkbox'; import LinearGradient from 'react-native-linear-gradient'; import { SocialIcon } from 'react-native-elements'; import AsyncStorage from '@react-native-community/async-storage'; import GradientHeader from 'react-native-gradient-header'; import LocalizedStrings from 'react-native-localization'; import { showMessage } from 'react-native-flash-message'; import API from '../Config/API'; import axios from 'axios'; const LoginScreen = ({props}) => { const navigation = useNavigation(); const [email,setEmail] = useState(''); const [password,setPassword] = useState(''); const [toggleCheckBox,setToggleCheckBox] = useState(false); const showErrorMessage = (message,type) => { return showMessage({ message: message,type: type,color: '#ffffff',fontSize: 14,fontWeight: 'bold',backgroundColor: '#0C254B',}); }; const ValidateEmail = (inputText) => { var mailformat = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/; if (inputText.match(mailformat)) { return true; } else { return false; } }; const validate = async () =>{ if (email === '' || email === undefined || email === null) { return showErrorMessage("Email can't be blank",'danger'); } else if (ValidateEmail(email) === false) { return showErrorMessage('Enter a valid Email address','danger'); } else if (password === '' || password === undefined || password === null) { return showErrorMessage("Password can't be blank",'danger'); } else { // const data = JSON.stringify({ // 'email': email,// 'password': password,// }); const options = { method: 'post',headers:{'Content-type': 'application/json'},url: 'http://localhost:8080/api/auth/signin',data: { email: email,password: password,},transformResponse: [(data) => { return data; }] }; const res = await axios(options).then((res)=>{ if (res.data.success === true){ //navigation.navigate('Auth'); navigation.navigate('Home'); // navigation.navigate('Service',{screen:'Doppler'}); //showErrorMessage(response.data.message,'success') } console.log(res.status); }).catch((error)=> { console.log(error); }); } }; return ( <SafeAreaView style={styles.outerContainer}> <View style={{flex:1}}> <Header /> <ScrollView style={styles.scrollView}> <TextInput placeholder= {localizdStrings.EmailOrMobile} style={styles.TextInput} onChangeText={(email) => setEmail(email)} value={email} keyboardType = {'email-address'} autoCapitalize = "none" autoCorrect = {false} /> <TextInput placeholder= {localizdStrings.Password} style={styles.TextInput} onChangeText={(password) => setPassword(password)} value={password} autoCapitalize = "none" returnKeyType={'done'} secureTextEntry={true} autoCorrect = {false} /> <View style={{flexDirection:'row',marginLeft:20,width:380,padding:10}}> <View style={{flexDirection:'row',justifyContent:'flex-start',alignItems:'center'}}> <View style={styles.rememberMe}> <CheckBox disabled={false} value={toggleCheckBox} tintColors={{true: '#ff0000'}} onValueChange={(newValue) => setToggleCheckBox(newValue)} /> <Text>{localizdStrings.RememberMe}</Text> </View> </View> <View style={{flexDirection:'row',justifyContent:'flex-end',alignItems:'center',marginLeft:50 }}> <View style={styles.forgetPass}> <Text style={{color:'grey'}}>{localizdStrings.ForgetThePassword}</Text> </View> </View> </View> <TouchableWithoutFeedback onPress={() => validate()}> <LinearGradient start={{x: 1,y: 0}} end={{x: 0,y: 0}} colors={['#29B6F6','#29B6F6','#0231C1']} style={styles.gradientStyle}> <View style={styles.Loginbutton}> <Text style={{fontWeight:'bold',fontSize:20,color:'white'}}>{localizdStrings.Login}</Text> </View> </LinearGradient> </TouchableWithoutFeedback> <View style={styles.quickAcess}> <Text style={{fontWeight:'bold',marginTop:5}}> {localizdStrings.OrQuickAccessWith} </Text> <View style={{ margin:5}}> <TouchableWithoutFeedback > <View style={styles.button}> <Image source={require('../images/facebook.png')} style={styles.socialIcon} /> <Text style={styles.socialLoginText}>{localizdStrings.Facebook}</Text> </View> </TouchableWithoutFeedback> <TouchableWithoutFeedback> <View style={styles.button}> <Image source={require('../images/google.png')} style={styles.socialIcon} /> <Text style={styles.socialLoginText}>{localizdStrings.Google}</Text> </View> </TouchableWithoutFeedback> <TouchableWithoutFeedback onPress={()=> navigation.navigate('RegisterScreen')}> <Text style={{marginLeft:95,marginRight:120,fontWeight:'bold',fontSize:18}}>{localizdStrings.CreateANewAccount}</Text> </TouchableWithoutFeedback> <View style={{marginLeft:150,marginRight:150}}> <Image source={require('../images/color_logo.png')} style={{resizeMode:'contain',height:80,width:100}} /> <Image source={require('../images/labelwhite.png')} style={{resizeMode:'contain',width:100}} /> </View> </View> </View> </ScrollView> </View> </SafeAreaView> ); }; export default LoginScreen; const styles = StyleSheet.create({ outerContainer:{ flex:1,Loginbutton: { alignItems: 'center',paddingLeft: 60,paddingRight:60,paddingBottom:10,paddingTop:10,borderRadius:20,marginBottom:10,marginLeft: 40,marginRight: 40,height:50,socialLoginText:{ color:'#00838F',//marginLeft:-50,paddingLeft:40,socialIcon:{ marginRight:50,marginLeft:-50,scrollView: { backgroundColor: 'white',marginTop:100,text: { fontSize: 42,TextInput: { height: 50,flex: 1,paddingLeft: 20,paddingRight: 20,borderColor: '#0277BD',borderRadius: 30,borderWidth: 1,marginBottom:5,marginTop:40,rememberMe:{ flexDirection:'row',padding:10,marginLeft: 5,forgetPass:{ flexDirection:'row',gradientStyle:{ alignItems: 'center',quickAcess:{ alignItems:'center',button: { alignItems: 'center',backgroundColor: 'white',borderWidth:1,borderColor:'#0277BD',borderRadius:8,flexDirection:'row',margin:15,});

具有类似警报的相同事物,我在其中放置了这些条件:

card(hashed_password) > 15

我用相同的声音分组的地方。

,

我建议使用 GROK 模式仅过滤掉包含您要查找的信息(登录并通过)的消息。

GROK 模式允许我修剪日志,删除所有无用信息并仅在特定字段名称下保留我需要的信息,这在您需要监控可疑登录时非常有用。

例如:在您的情况下,您可以创建一个名为 "login_failed_foo" 的字段,然后在该字段的出现次数达到某个点时设置警报。