web安全自動化運(yùn)維
你有可能也會遇到這種情況,公司沒有任何安全設(shè)備,也沒有資金采購,應(yīng)付層層攻擊,那么作為公司唯有一個(gè)安全工程師,如何來自動化保障公司的web安全呢?
下面,結(jié)合我的一些經(jīng)驗(yàn),說說一些實(shí)際操作。由于全部是手工,維護(hù)web安全來做到實(shí)時(shí)過濾安全攻擊,那必須在web服務(wù)器前方做一個(gè)代理,或者在代碼層有一個(gè)代理
層,實(shí)時(shí)的檢測過來的請求,再傳給應(yīng)用,這涉及到編寫web應(yīng)用防火墻呢,相對而言,對一個(gè)獨(dú)立安全工程師比較復(fù)雜,簡單一點(diǎn)的呢?
其實(shí),退而求其次,我當(dāng)時(shí)做法分兩步走,第一步我是做自動化巡檢。第一次服務(wù)器的安全巡檢,網(wǎng)絡(luò)上有一些checklist,這就不列舉了,不是本文重點(diǎn),對每個(gè)核心
服務(wù)器一開始做一次全面詳細(xì)的checklist檢查,修復(fù)后,基本能做到基準(zhǔn)安全了。接下來,就是要每日巡檢了。那么巡檢的主題是什么呢?關(guān)鍵文件的變更。我當(dāng)時(shí)使用的ruby,
核心代碼改編于網(wǎng)上:
#ruby比較兩個(gè)文件
def cmpfile(source_file,tmp_file,security_type)
num=0
f1=File.open(source_file)
f2=File.open(tmp_file)
if !File.zero?("source_file") and !File.zero?("tmp_file") then
num1,f1array=getFileLine(f1)
num2,f2array=getFileLine(f2)
#ensure the numberof loop
if num1>num2
num=num1
else
num=num2
end
for i in(0..num-1)
mesg1="Exception:Maybe file"
mesg2="was not enough lines. Cant find the data when check line"
if f1array[i] != nil and f2array[i] != nil
if f1array[i] != f2array[i]
$rrp.write("\n--#{security_type}--num:"+i.to_s+"\n"+"source_file: "+f1array[i].to_s+"\n"+"new_file: "+f2array[i].to_s)
end
else
if f1array[i] = nil
$rrp.write("\n--#{security_type}--num:"+i.to_s+" "+mesg1+'1'+ mesg2+(i+1).to_s)
break
else if f2array[i] = nil
$rrp.write("\n--#{security_type}--num:"+i.to_s+" "+mesg1+'2'+ mesg2+(i+1).to_s)
break
else
break
end
end
end
end
end
f1.close
f2.close
end
def getFileLine(f)
farray=[]
num=0
f.each do |fi|
num+=1
farray+=[fi.strip]
end
return num,farray
end
我的思路即是,把所有需要監(jiān)控的東西,保留一個(gè)最初的原始文件,存于一個(gè)專門的文件夾內(nèi),每次巡檢,通過如下:
#執(zhí)行命令 提取源信息
def cmd_source(cmd,source_path_file)
cont=`#{cmd}`
if !File.exist?(source_path_file)
File.new(source_path_file,"w").write(cont)
end
end
#執(zhí)行命令 提取臨時(shí)信息
def cmd_tmp(cmd,tmp_path_file)
cont=`#{cmd}`
File.new(tmp_path_file,"w").write(cont)
end
這樣,每次手工檢測執(zhí)行命令的結(jié)果都存在專門文件里,跟原始文件進(jìn)行對比(內(nèi)容對比函數(shù)見上)。于是,再稍微優(yōu)化整理一下,一個(gè)自動化的檢測腳步就執(zhí)行了,
具體可以以一周為期限或碰到特殊情況變更原始文件,將新的標(biāo)準(zhǔn)文件換為原始文件進(jìn)行對比。做定時(shí)任務(wù),每5分鐘左右執(zhí)行一次檢測,那么一旦出現(xiàn)文件變更,掛馬等
則立刻能在生成的變更報(bào)告文件里檢測出來。
前期個(gè)人這樣搞了1個(gè)月,工作輕松很多,后來公司有一個(gè)哥們用ruby比較熟,于是公司需求做一個(gè)ruby的風(fēng)險(xiǎn)監(jiān)控系統(tǒng),那么,下面就是本文重點(diǎn)了,這個(gè)風(fēng)險(xiǎn)監(jiān)控系統(tǒng)
關(guān)鍵點(diǎn)就是把實(shí)施檢測的信息,發(fā)送給后端,顯示。我們用的消息平臺是amqp。先說一下整體思路,然后下面見核心代碼解釋。風(fēng)險(xiǎn)監(jiān)控系統(tǒng),可以將手工檢測生成的報(bào)文信息
與日志里重要的關(guān)鍵信息進(jìn)行提取整理,發(fā)送給后端,后端根據(jù)得到的數(shù)據(jù)進(jìn)行圖形或表格等不同展現(xiàn)。這分為三部分。信息的來源:原始日志與前期自動化檢測生成的信息;
信息的分析:對分析進(jìn)行整理,取出含有關(guān)鍵字的特殊信息進(jìn)行分類分級,放在不同管道里,發(fā)給后臺;信息的展現(xiàn):后臺先將發(fā)來的信息存于數(shù)據(jù)庫,再根據(jù)需要取出數(shù)據(jù)庫進(jìn)行
圖形或表格形式展現(xiàn)。
其實(shí)這個(gè)進(jìn)行詳細(xì)改編和優(yōu)化,就是一個(gè)完整的soc系統(tǒng)了,目前國內(nèi)的soc系統(tǒng)也不外如此,卻要買幾十萬甚至上百萬,甚鄙視之。其實(shí)soc核心的關(guān)鍵是規(guī)則關(guān)聯(lián)和
設(shè)備聯(lián)動,就是在咱們信息分析這一部分,定義大量規(guī)則進(jìn)行各種日志關(guān)聯(lián)分析,這個(gè)我在自己的程序里沒做,當(dāng)時(shí)就只搞apache、resin日志分析,都是些簡單的,整個(gè)系統(tǒng)
花了一兩個(gè)星期跟另一個(gè)工程師配合上線,運(yùn)行正常,后種種情況,擱置,但是給一些孤獨(dú)工程師提供一下借鑒思路,還是能省一些事情,希望能給一些人有所幫助。下面看
代碼,不多說:
#! /usr/local/ruby/bin/ruby
=begin
author:kn1ghtc
describe:that's true
=end
require 'rubygems'
require 'amqp'
#ruby是一時(shí)心血來潮搞了一下,其實(shí)還是不大懂,剛看完一本書就寫了這個(gè),所以有很多山寨用法,大牛勿笑,服務(wù)器上存的日志按時(shí)間每天自動追加到下面幾個(gè)文件,
#咱們先定義一下幾個(gè)變量,取源文件路徑
$con_source_path_ssl="../../access_ssl_#{Time.now.strftime("%Y%m%d")}.log"
$con_source_path_access="/../access_#{Time.now.strftime("%Y%m%d")}.log"
$con_source_path_error="/../error_#{Time.now.strftime("%Y%m%d")}.log"
#定義幾個(gè)變量,存一些規(guī)則過濾后的文件內(nèi)容,這里圖簡便,人為分配了四個(gè)等級和方向,讀者可自行優(yōu)化規(guī)則和分類
$tmp_scan=[]
$tmp_attack=[]
$tmp_app=[]
$tmp_abnormal=[]
#服務(wù)器上日志格式有自己生成的規(guī)則,沒關(guān)系,我們按我們的需要標(biāo)準(zhǔn)化成我們的格式,便于后面分析規(guī)則應(yīng)用與存儲展現(xiàn)
#log_format
def message_format(str_line,str_type,str_risk,str_format)
@str_time=""
@str_ip=""
@str_data=""
@str_result=""
@str_format=str_format
@str_line=str_line
if @str_format=="error"
@str_time=/[a-zA-Z]{3}.[a-zA-Z]{3}.\d{2}.\d{2}:\d{2}:\d{2}.\d{4}/.match(@str_line)
@str_ip=/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/.match(@str_line)
@str_data=@str_line.gsub(/^\[[a-zA-Z]{3}.[a-zA-Z]{3}.\d{2}.\d{2}:\d{2}:\d{2}.\d{4}\].\[[a-zA-Z]{3,5}\].\[[a-zA-Z]{6}.\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\]/,"").strip
else
@str_time=/\d{2}\/[a-zA-Z]{3}\/\d{4}:\d{2}:\d{2}:\d{2}/.match(@str_line)
@str_ip=/^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/.match(@str_line)
@str_data=@str_line.gsub(/^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}.....\[\d{2}\/[a-zA-Z]{3}\/\d{4}:\d{2}:\d{2}:\d{2}.\+\d{4}\]/,"").strip
end
@str_result=@str_ip.to_s+"=>"+@str_data.to_s+"=>"+@str_time.to_s+"=>"+str_type+"=>"+str_risk+"\n"
end
#取出日志里的每一行,放進(jìn)下面的函數(shù),設(shè)定規(guī)則,匹配規(guī)則的放進(jìn)相應(yīng)數(shù)組里
#line_rule_match
def line_rule(rule_line,rule_log_type)
@rule_str_line=rule_line
@rule_log_type=rule_log_type
@new_line=""
@rule_data=[]
if @rule_str_line=~/error/
$tmp_abnormal<<message_format(@rule_str_line,"abnormal","M",@rule_log_type)
else
@new_line=message_format(@rule_str_line,"unknown","unknown",@rule_log_type).split(/=>/)[1]
@rule_data=@new_line.split(/"/)
if @rule_data.size>6
$tmp_scan<<message_format(@rule_str_line,"scan","M",@rule_log_type)
else
if @rule_data.size>1
if @rule_data[3]=~/script>|<script|passWord|password/
$tmp_attack<<message_format(@rule_str_line,"attack","H",@rule_log_type)
#做了一些狀態(tài)號檢測
elsif @rule_data[1]=~/'|alert|script>|cat|union|order by|;|\.\.\/|and|--/ or @rule_data[2].lstrip.split(/\s/)[0]=~/400|403|404|500/
$tmp_scan<<message_format(@rule_str_line,"scan","M",@rule_log_type)
end
end
end
end
end
#log_ids
def invade_log(source_path_file,log_type)
@path_source=source_path_file
@log_type=log_type
i=0
@line_num_tmp=-1 www.hack6.com
#山寨想法,當(dāng)時(shí)各種服務(wù)器性能限制和原因,所有為了實(shí)時(shí)取日志內(nèi)容(web日志是實(shí)施增加的),服務(wù)器上設(shè)置定時(shí)任務(wù),每隔1分鐘運(yùn)行一次腳本
#每1分鐘運(yùn)行咱們的腳本,腳本便從建立的特殊文件夾里找特殊文件,文件里生成這次檢測原文件的行號,對比行號,從新行號開始往下掃描,原后存行號
if @log_type=="ssl"
File.open("/tmp/num_ssl","r") do |file|
@line_num_tmp=file.gets.to_i
end
elsif @log_type=="access"
File.open("/tmp/num_access","r") do |file1|
@line_num_tmp=file1.gets.to_i
end
else
File.open("/tmp/num_error","r") do |file2|
@line_num_tmp=file2.gets.to_i
end
end
File.open(@path_source) do |files|
files.each_line do |line|
begin
line.force_encoding("gbk")
if i>@line_num_tmp
line_rule(line,@log_type)
else
i=i+1
next
end
i=i+1
rescue
i=i+1
$tmp_app<<message_format(line,"app","L",@log_type)
end
end
end
@line_num_tmp=i
@file_log=File.open("/tmp/num_#{@log_type}","w")
@file_log.write(@line_num_tmp)
@file_log.close
end
#行號文件,解決實(shí)施檢測的問題
#time_question
#implement_method
def implement_meth(source_path_file,log_type)
@im_path_source=source_path_file
@im_log_type=log_type
@filename_old=""
if File.exist?(@im_path_source)
File.open("/tmp/time_#{@im_log_type}") do |file|
@filename_old=file.gets.to_s.strip
end
if @im_path_source!=@filename_old
@im_file_log=File.open("/tmp/num_#{@im_log_type}","w")
@im_file_log.write("-1")
@im_file_log.close
@im_file_log=File.open("/tmp/time_#{@im_log_type}","w")
@im_file_log.write(@im_path_source)
@im_file_log.close
end
invade_log(@im_path_source,@im_log_type)
end
end
#mq消息的應(yīng)用,定義信道相關(guān)信息,這里基本格式都是這樣,很好修改為自己的
#send_message_method_scan
def send_to_exchange_scan(message)
exchange=MQ.direct('guofubao')
exchange.publish message,:key=>'scan'
end
#send_message_method_atack
def send_to_exchange_attack(message)
exchange=MQ.direct('guofubao')
exchange.publish message,:key=>'attack'
end
#send_message_method_app
def send_to_exchange_app(message)
exchange=MQ.direct('guofubao')
exchange.publish message,:key=>'app'
end
#send_message_method_abnormal
def send_to_exchange_abnormal(message)
exchange=MQ.direct('guofubao')
exchange.publish message,:key=>'abnormal'
end
#test_data
#tmp_test="127.0.0.1=>kn1ghtc_test=>2012:12:31=>unknown=>unknown"+"\n"
#implement
implement_meth($con_source_path_access,"access")
implement_meth($con_source_path_error,"error")
implement_meth($con_source_path_ssl,"ssl")
#conn
#應(yīng)用消息發(fā)送和方法體
AMQP.start :host => '127.0.0.1', :port => 5672 do
event_loop=Thread.new do
EM.run do
EM.add_timer(1) do
EM.stop
end
end
end
send_to_exchange_abnormal $tmp_abnormal.join().to_s
send_to_exchange_app $tmp_app.join().to_s
send_to_exchange_attack $tmp_attack.join().to_s
send_to_exchange_scan $tmp_scan.join().to_s
event_loop.join
end
結(jié)束:個(gè)人比較懶,當(dāng)時(shí)寫的比較倉促,主體思路都在上面,后面系統(tǒng)因?yàn)槠渌蛳戮€,幾個(gè)月前寫的,現(xiàn)在也只記得思路,覺得有用的看官自己縷一縷吧。