在管理extmail的过程中,管理员开通的邮箱账号都是使用初始密码。懒惰的用户往往不做修改就直接使用。时间一长,管理员初始密码就容易被暴力破解。然后某一个账户就变成垃圾中转站了。
下面的三个脚本,是在不会改Perl代码的情况下,用bash加固一下服务器。
====================第一个脚本:用来强制用户24小时之内修改密码======================
复制内容到剪贴板
代码:
#!/bin/bash
#
#
# 脚本名:extmail-change-your-password.sh
# 功能简介:一个强制用户修改密码的小脚本。使用cron安排在每天晚上24点之后执行。
# 禁用掉没有登录webmail修改密码的用户
#
# 作者: 刘西洋
# <xiyangliu1987@gmail.com>
# http://www.xiyang-liu.com
#
# 脚本自由,版权没有
#
# 创建时间:2012年11月27日一个阳光明媚的上午
#
#------程序设计思路-------------------
# 用户第一次使用webmail登录时,extmail在用户的Maildir文件夹下面
#创建一个maildirsize文件,用来记录磁盘限额信息。管理员邮箱收到的
#磁盘使用汇总报告显示,部分用户Maildir目录下没有maildirsize文件。
#这说明,这部分用户从没有登录过webmail界面,也没有修改过密码。
# 为了便于管理用户的磁盘限额信息,用户邮箱使用情况。以用户没有修改
#密码的名义,强制用户登录webmail界面。否则在当日24时后,禁用用户邮箱。
#--------结束-----------------------------
##设置全局变量
DB_HOSTNAME="localhost"
DB_PORT="3306"
DB_USERNAME="root"
DB_PASSWORD=""
DB_NAME="extmail"
COUNT_FILE="/var/log/extmail-passwd-check-count.txt"
LOG_FILE="/var/log/extmail-passwd-change.log"
TEMP_FILE=/tmp/extmail-count-tmp.tmp
SEND_SMS_EXEC=/var/password-manager/send-mas-sms.sh
declare domains=( `mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select domain from domain;" | xargs` )
dlen=${#domains[@]}
di=0
while [ $di -lt "$dlen" ]
do
declare users=( `mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select uid from mailbox where active='1' and domain='${domains[$di]}'; " | xargs` )
ulen=${#users[@]}
ui=0
while [ $ui -lt "$ulen" ]
do
MAILDIRPATH=/home/domains/${domains[$di]}/${users[$ui]}/
if [ -f $MAILDIRPATH/Maildir/maildirsize ];then
echo USER ${users[$ui]}@${domains[$di]} OK!
else
COUNT=`grep ${users[$ui]} $COUNT_FILE | cut -d ":" -f 2`
if [ -z $COUNT ];then
COMM_MESSAGE="尊敬的企业邮箱用户,您好!您已开通用户名为${users[$ui]}@${domains[$di]}的企业邮箱账号,但是您还未登录修改密码.请您在24小时内登录企业邮箱,并修改密码.超过7天未修改密码的账户,将被自动注销.企业邮箱登录地址http://mail.xiyang-liu.com"
$SEND_SMS_EXEC "${users[$ui]}" "$COMM_MESSAGE"
echo User ${users[$ui]}@${domains[$di]} do not change password in 24 hours!
else
mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "update mailbox set active=0 where username='${users[$ui]}@${domains[$di]}'; "
let COUNT++
echo "${users[$ui]}@${domains[$di]}:$COUNT" >>$TEMP_FILE
echo User ${users[$ui]}@${domains[$di]} keep DEFAULT password for $COUNT times now!
fi
fi
let ui++
done
let di++
done
cat $TEMP_FILE> $COUNT_FILE
> $TEMP_FILE
##程序结束
笔者运行的环境有多个域。同时,有多个域管理员。被禁用后的用户的激活问题可以交给域管理员。
====================第二个脚本:用于提醒用户60天修改一次密码============================
这个脚本的本意,是防止用户长时间不修改密码,如果凑巧了,用户的密码再比较简单。被不怀好意的人猜透的可能性就大增。指不定哪天邮件服务器负载飙到8-9了,疯狂的往外发垃圾邮件,处理倒是不难。可,企业邮箱声誉降下来了,或者被RBL加黑了,申诉就灰常折磨人了。
复制内容到剪贴板
代码:
#!/bin/bash
#
#本脚本需要一个密码权值对应文件。文件内容为:用户名@域名#密码#权值
#先将用户账号#密文#初始权值1 导出到一个文件中。
#通过mysql查询最新的密码,和文件中的旧密码进行比对,如果密码相同,则说明未修改密码。将权值加1。
#如果密码不同,则将新的密码更新到文件中,同时将权值重置为1。
#本脚本每周一执行,如果权值超过8,则短信提醒用户修改密码。同时将权值重置为4,以保证仅每个月提醒一次。
# 刘西洋 <xiyangliu1987@163.com>
# http://www.xiyang-liu.com
DB_HOSTNAME="localhost"
DB_PORT="3306"
DB_USERNAME="root"
DB_PASSWORD=""
DB_NAME="extmail"
#指定初始化密码文件
USER_PASSWD_FILE=/var/password-manager/user-password-file.txt
SEND_SMS_EXEC=/var/password-manager/send-mas-sms.sh
TEMP_FILE=/tmp/extmail-passwd-check-tmp.tmp
#如果不存在密码文件,则导出现有用户名密码,来创建一个新的密码文件
if [ ! -s $USER_PASSWD_FILE ] ; then
mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select username,password from mailbox;" | sed -e 's/\t/#/g' -e 's/$//g' > $USER_PASSWD_FILE
fi
#查询域名,创建数组
declare domains=( `mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select domain from domain;" | xargs` )
#初始化指针
dlen=${#domains[@]}
di=0
#进入循环,每次处理一个域的用户
while [ $di -lt "$dlen" ]
do
#查询域下用户,创建数组
declare users=( `mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select uid from mailbox where domain='${domains[$di]}';" | xargs` )
#初始化指针
ulen=${#users[@]}
ui=0
#进入子循环,逐个处理用户
while [ $ui -lt "$ulen" ]
do
#分别查询密码文件和数据库中的密文密码
FILE_PASSWD=`grep "^${users[$ui]}@${domains[$di]}#" $USER_PASSWD_FILE | cut -d "#" -f 2`
SQL_PASSWD=`mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select password from mailbox where username='${users[$ui]}@${domains[$di]}';" | xargs`
#如果两个密码不同,则将新密码追加到临时文件中。否则将计数器加1
if [ "$FILE_PASSWD" != "$SQL_PASSWD" ];then
echo "${users[$ui]}@${domains[$di]}#$SQL_PASSWD#1" >> $TEMP_FILE
else
COUNT=`grep "^${users[$ui]}@${domains[$di]}#" $USER_PASSWD_FILE | cut -d "#" -f 3`
#如果记录不存在,初始化此记录
if [ -z $COUNT ];then
echo "${users[$ui]}@${domains[$di]}#$SQL_PASSWD#1" >> $TEMP_FILE
#如果计数器大于8,发送短信提醒用户修改密码
elif [ $COUNT -gt 8 ];then
COMM_MESSAGE="尊敬的企业邮箱用户,您好!您用户名为${users[$ui]}@${domains[$di]}的企业邮箱账号已经超过60天没有修改密码.为了您账户的安全.请您在24小时内登录企业邮箱修改密码.企业邮箱登录地址http://mail.xiyang-liu.com"
$SEND_SMS_EXEC "${users[$ui]}" "$COMM_MESSAGE"
#将计数器置4,并将密码追加到临时文件
echo "${users[$ui]}@${domains[$di]}#$SQL_PASSWD#4" >> $TEMP_FILE
else
#否则计数器加1,并将密码追加到临时文件,并输出调试信息。
let COUNT++
echo "${users[$ui]}@${domains[$di]}#$SQL_PASSWD#$COUNT" >> $TEMP_FILE
echo User ${users[$ui]}@${domains[$di]} does not change password for $COUNT times now!
fi
fi
let ui++
done
let di++
done
#用临时文件内容覆盖密码文件内容
cat $TEMP_FILE > $USER_PASSWD_FILE
#清空临时文件
> $TEMP_FILE
上面脚本中,用了一个发短信的脚本。这个每个公司的情况都不一样。我的脚本不具有普适性。
脚本功能:接受用户邮箱账号和短信内容为输入变量,根据账号从一个账号手机号对应文件中提取用户的手机号。然后给用户发送一条短信。
需要借鉴发短信的脚本,请访问:http://www.xiyang-liu.com/2013/1 ... s-through-cmcc-mas/
============================第三个脚本:用户暴力破解用户简单密码=========================
通过前几个脚本,禁止了用户使用默认密码,禁止了用户不更改密码。如果用户改了一个符合规则的弱密码怎么办?或者如果用户两个密码反复用该如何办?
与其被别人破解,还不如自己破解!
破解思路:extmail系统使用了md5crypt加密用户密码,然后存放在数据库中。php的crypt函数可以将明文加上一个salt值声称密文密码。通过md5crypt加密弱密码后,与数据库中的密码进行比对。如果相同,则表示密码已经破解。用户密码被破解后,系统使用12位伪随机数重置用户密码。并短信告知用户新的密码。
复制内容到剪贴板
代码:
#!/bin/bash
DB_HOSTNAME="localhost"
DB_PORT="3306"
DB_USERNAME="extmail"
DB_PASSWORD=""
DB_NAME="extmail"
WEAK_PASS_FILE=/var/sysmgmt-script/password-manager/weak-passwd-dict.txt
SEND_SMS_EXEC=/var/sysmgmt-script/password-manager/send-mas-sms.sh
LOG_FILE=/var/log/reset-password-xiyang-liu.com.log
function F_RESET_PASS()
{
#从命令行获取用户名全名:用户名@域名
USERNAME=$1
#查询用户的旧密码
OLD_EN_PASSWD=`mysql -sN -h $MAIL_DB_HOSTNAME -P $MAIL_DB_PORT -u $MAIL_DB_USERNAME -p$MAIL_DB_PASSWORD $MAIL_DB_NAME -e "select password from mailbox where username='$USERNAME'"`
#提取密码计算随机数
USER_SALT=`STR="$OLD_EN_PASS"; echo ${STR:0:12}`
#随机生成12位新密码
NEW_PLAIN_PASSWD=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 12`
#加密新密吗
NEW_EN_PASSWD=`php -r "echo crypt('$NEW_PLAIN_PASSWD','$USER_SALT');"`
#修改用户密码为新密码
mysql -sN -h $MAIL_DB_HOSTNAME -P $MAIL_DB_PORT -u $MAIL_DB_USERNAME -p$MAIL_DB_PASSWORD $MAIL_DB_NAME -e "update mailbox set password='$NEW_EN_PASSWD',active='1' where username='$USERNAME'; "
echo USER $USERNAME has NEW PASSWORDD : $NEW_PLAIN_PASSWD
#短信通知用户
COMM_MESSAGE="尊敬的企业邮箱用户,您好!您的账户$USERNAME使用了可破解的弱密码,为了您的账户安全.我们已将您的密码修改为$NEW_PLAIN_PASSWD,请您及时修改密码.企业邮箱登录地址http://mail.xiyang-liu.com."
$SEND_SMS_EXEC $USERNAME "$COMM_MESSAGE"
echo "$USERNAME" "$OLD_EN_PASSWD" "$NEW_PLAIN_PASSWD" "$NEW_EN_PASSWD" >> $LOG_FILE
}
declare users=( `mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select username from mailbox;" | xargs` )
ulen=${#users[@]}
ui=0
while [ $ui -lt "$ulen" ]
do
ti=0
tlen=12
while [ $ti -lt $tlen ]
do
{
U_NAME=${users[$ui]}
U_PASS=`mysql -sN -h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p$DB_PASSWORD $DB_NAME -e "select password from mailbox where username='$U_NAME'"`
echo $U_NAME
TRY_SALT=`STR="$U_PASS"; echo ${STR:0:12}`
cat $WEAK_PASS_FILE | while read LINE
do
EN_PASS=`php -r "echo crypt('$LINE','$TRY_SALT');"`
#echo $EN_PASS
#echo $LINE
if [ "$EN_PASS" == "$U_PASS" ]
then
F_RESET_PASS "$U_NAME"
fi
done
} &
let ui++
let ti++
done
#等待12个进程都执行再进行下一个处理过程
wait
done
上面脚本中使用了多线程计算。根据弱密码字典的大小会造成一定程度上的系统负载。建议找一个性能好点的服务器远程执行计算。
千万不要去掉wait,否则会把服务器搞死的!