7月, 2010

使用DenyHosts阻止SSH暴力破解

DenyHosts是Python语言写的一个程序,它会分析SSHD的日志文件,当发现重复的攻击时就会记录IP到/etc/hosts.deny文件,从而达到自动屏蔽IP的功能。

DenyHosts官方网站为:http://denyhosts.sourceforge.net

以下是安装记录(以CentOS 4.4, DenyHosts 2.5 为例)

# cd /usr/local/src
# wget http://mesh.dl.sourceforge.net/sourceforge/denyhosts/DenyHosts-2.6.tar.gz
# wget http://jaist.dl.sourceforge.net/project/denyhosts/denyhosts/2.6/DenyHosts-2.6.tar.gz
# tar -zxvf DenyHosts-2.5.tar.gz
# cd DenyHosts-2.5
# python setup.py install

# cd /usr/share/denyhosts/
# cp denyhosts.cfg-dist denyhosts.cfg
# vi denyhosts.cfg

# cp daemon-control-dist daemon-control
# vi daemon-control

DENYHOSTS_BIN = “/usr/bin/denyhosts.py”
DENYHOSTS_LOCK = “/var/lock/subsys/denyhosts”
DENYHOSTS_CFG = “/usr/share/denyhosts/denyhosts.cfg”

# chown root daemon-control
# chmod 700 daemon-control

# ./daemon-control start
# cd /etc/init.d
# ln -s /usr/share/denyhosts/daemon-control denyhosts
# chkconfig –add denyhosts
# chkconfig –level 2345 denyhosts on

DenyHosts配置文件说明:
SECURE_LOG = /var/log/secure

#sshd日志文件,它是根据这个文件来判断的,不同的操作系统,文件名稍有不同。

HOSTS_DENY = /etc/hosts.deny

#控制用户登陆的文件

PURGE_DENY = 5m

#过多久后清除已经禁止的

BLOCK_SERVICE = sshd

#禁止的服务名

DENY_THRESHOLD_INVALID = 1

#允许无效用户失败的次数

DENY_THRESHOLD_VALID = 10

#允许普通用户登陆失败的次数

DENY_THRESHOLD_ROOT = 5

#允许root登陆失败的次数

HOSTNAME_LOOKUP=NO

#是否做域名反解

DAEMON_LOG = /var/log/denyhosts

#DenyHosts的日志文件

单点登录(SSO)的实现—通行证的基本原理

问题起源:想做一个面向校园的网站,因为势单力薄。部分模块采用整合其它开源系统的方案,比如BBS系统和BLOG系统。首先面临的就是用户身份认证的方式。由于这些不是自己开发的系统,都分别有自己的用户系统,于是面临统一身份认证的过程。

以前看过企业级的Web service方案,主要是通过XML,SOAP,WSDL和UDDI来实现。将应用服务都注册到UDDI服务器中,通过SOAP协议使用XML传递信息(当然需经过加密)。由于涉及到很多服务部署的问题,用JAVA来做这样的项目肯定是再好不过的了。我的目的只是几个WEB系统的整合,肯定是要排除这么伟大的方案了。关于Web service有兴趣的朋友可以参考机械工业出版社出版的《Web Servides原理与研发实践》,里面有详细的介绍。

那么对于这样的小WEB系统的整合该怎么来实现?我们假设这是个从零开始的项目,除了自己开发的系统外,还要用到一些其它组织开发的开源系统:比如 BLOG,CMS,BBS。这些系统都有各自的用户系统。要把他们整合到一块有个原则,就是尽量不要破坏或者修改这些系统。那么要实现统一身份认证,我们必须要有一个用户信息库,然后吧这个数据库的信息映射到那些子系统的数据库中,在大型项目中,一般都会独立出一台单独的用户信息服务器,大部分高校采用 LDAP来存放用户信息,因为采用的是树状结构,对经常读取但很少修改的数据而言,它的性能是很高的。

LDAP用户库和各子系统用户库的映射有很多种方法,我这里只用最简单的直接映射,也就是帐号和密码都是相同的。

假设我的域名部署如下

http://news.domain.com 这是CMS系统的域名

http://bbs.domain.com 这是论坛的域名

http://blog.domain.com 这是博客域名

http://reg.domain.com 这是统一注册和登录页面的域名
首先是注册,我们让所有子系统注册页面都转向到一个注册页面上来(各种脚本语言都有转向函数),比如说当用户希望在http://blog.domain.com/reg.php注册是,reg.php把这个请求转向到http://reg.domain.com/reg.php.

在实现注册时,由于刚开始时候子系统并不多,注册时把用户注册信息写入主用户数据库的同时写入各子系统的用户库。以后若有新的子系统加入进来时可以通过帐号激活的方式来实现新系统的帐号激活。

用户登录的过程,可以参考如下来自IBM的图片


流程描述如下:(仅描述正常流程)

1. 用户使用在统一认证服务注册的用户名和密码(也可能是其他的授权信息,比如数字签名等)登陆统一认证服务;

2. 统一认证服务创建了一个会话,同时将与该会话关联的访问认证令牌返回给用户;

3. 用户使用这个访问认证令牌访问某个支持统一身份认证服务的应用系统;

4. 该应用系统将访问认证令牌传入统一身份认证服务,认证访问认证令牌的有效性;

5. 统一身份认证服务确认认证令牌的有效性;

6. 应用系统接收访问,并返回访问结果,如果需要提高访问效率的话,应用系统可选择返回其自身的认证令牌已使得用户之后可以使用这个私有令牌持续访问。

上面所说的令牌我在WEB引用中可以用COOKIE或者SEESION来实现。

例如通过COOKIE来实现

1.用户在统一登录页登陆,通过查询主用户数据库判断用户是否合法,若是,则注册该用户的唯一COOKIE标识(可以通过加密用户名和密码得到,网上有很多算法)。

2.用户进入某子系统时,先判断COOKIE是否注册,若注册了,则解密该COOKIE得到用户名和帐号,并判断合法性。
3.如果合法,则立刻在该子系统中注册(可在原子系统的登录脚本种抽出登录的部分做成一个函数)

使用COOKIE的优点就是简单,只要设置一下就可以实现COOKIE的跨子域传递

例如

setcookie(NC_USER_COOKIE, 用户名, 失效时间, 作用路径, ‘.domain.com’);

就能实现在所有.domain.com子域下的传递。

但COOKIE也有他的缺点,首先就是安全级别不高,要提防COOKIE劫持的威胁,其次就是它只能跨子域传递,而不能跨完全不同的域。比如说 domain.com和fuck.com之间就不能传递。

然后再说下SESSION的方式,由于SESSION是存储在服务器端的,所以安全级别肯定要比COOKIE的级别高。但是由于SEESION存储的位置不同,造成了无法跨域传递。可以通过把SEESION村入数据库来解决这个问题。

由于PHP的SESSION需要用到标识SESSION的COOKIE,所以需要设置下COOKIE的作用域
ini_set(’session.cookie_domain’, ‘.domian.com’);

然后重写PHP的SESSION操作函数。

PHP 提供了session_set_save_handle() 函数来自定义 SESSION 的处理过程,先将 session.save_handler 改成 user

session_module_name(‘user’);

然后几可以重写SESSION的操作了,下面是PHP牛人NIO写的SESSION操作

define(‘MY_SESS_TIME’, 3600);    //SESSION 生存时长
//类定义
class My_Sess
{
     function init()
     {
         $domain = ‘.infor96.com’;
         //不使用 GET/POST 变量方式
         ini_set(’session.use_trans_sid’,     0);
         //设置垃圾回收最大生存时间
         ini_set(’session.gc_maxlifetime’,    MY_SESS_TIME);

         //使用 COOKIE 保存 SESSION ID 的方式
         ini_set(’session.use_cookies’,       1);
         ini_set(’session.cookie_path’,       ‘/’);
         //多主机共享保存 SESSION ID 的 COOKIE
         ini_set(’session.cookie_domain’,     $domain);

         //将 session.save_handler 设置为 user,而不是默认的 files
         session_module_name(‘user’);
         //定义 SESSION 各项操作所对应的方法名:
         session_set_save_handler(
             array(‘My_Sess’, ‘open’),    //对应于静态方法 My_Sess::open(),下同。
             array(‘My_Sess’, ‘close’),
             array(‘My_Sess’, ‘read’),
             array(‘My_Sess’, ‘write’),
             array(‘My_Sess’, ‘destroy’),
             array(‘My_Sess’, ‘gc’)
         );
     }    //end function
     function open($save_path, $session_name) {
         return true;
     }    //end function

     function close() {
         global $MY_SESS_CONN;

         if ($MY_SESS_CONN) {     //关闭数据库连接
             $MY_SESS_CONN->Close();
         }
         return true;
     }    //end function

     function read($sesskey) {
         global $MY_SESS_CONN;

         $sql = ‘SELECT data FROM sess WHERE sesskey=’ . $MY_SESS_CONN->qstr($sesskey) . ‘ AND expiry>=’ . time();
         $rs =& $MY_SESS_CONN->Execute($sql);
         if ($rs) {
             if ($rs->EOF) {
                 return ‘’;
             } else {     //读取到对应于 SESSION ID 的 SESSION 数据
                 $v = $rs->fields[0];
                 $rs->Close();
                 return $v;
             }    //end if
         }    //end if
         return ‘’;
     }    //end function

     function write($sesskey, $data) {
         global $MY_SESS_CONN;
      
         $qkey = $MY_SESS_CONN->qstr($sesskey);
         $expiry = time() + My_SESS_TIME;     //设置过期时间
      
         //写入 SESSION
         $arr = array(
             ’sesskey’ => $qkey,
             ‘expiry’   => $expiry,
             ‘data’     => $data);
         $MY_SESS_CONN->Replace(’sess’, $arr, ’sesskey’, $autoQuote = true);
         return true;
     }    //end function

     function destroy($sesskey) {
         global $MY_SESS_CONN;

         $sql = ‘DELETE FROM sess WHERE sesskey=’ . $MY_SESS_CONN->qstr($sesskey);
         $rs =& $MY_SESS_CONN->Execute($sql);
         return true;
     }    //end function

     function gc($maxlifetime = null) {
         global $MY_SESS_CONN;

         $sql = ‘DELETE FROM sess WHERE expiry. time();
         $MY_SESS_CONN->Execute($sql);
         //由于经常性的对表 sess 做删除操作,容易产生碎片,
         //所以在垃圾回收中对该表进行优化操作。
         $sql = ‘OPTIMIZE TABLE sess’;
         $MY_SESS_CONN->Execute($sql);
         return true;
     }    //end function
}    ///:~

//使用 ADOdb 作为数据库抽象层。
require_once(‘adodb/adodb.inc.php’);
//数据库配置项,可放入配置文件中(如:config.inc.php)。
$db_type = ‘mysql’;
$db_host = ‘192.168.212.1′;
$db_user = ’sess_user’;
$db_pass = ’sess_pass’;
$db_name = ’sess_db’;
//创建数据库连接,这是一个全局变量。
$GLOBALS[‘MY_SESS_CONN’] =& ADONewConnection($db_type);
$GLOBALS[‘MY_SESS_CONN’]->Connect( $db_host, $db_user, $db_pass, $db_name);
//初始化 SESSION 设置,必须在 session_start() 之前运行!!
My_Sess::init();

基本的原理差不多就这样,细节方面就看还有很多需要琢磨的地方..

如何防止linux服务器被暴力破解密码?

经常有人会利用ssh来暴力破解服务器密码,然后给服务器挂马,查看服务器的安全记录,

打开/var/logs/secure文件,会发现很多利用ssh来暴力破解登录的记录,如下

1: Aug 29 16:27:23 fgb sshd[31098]: Failed password for root from 189.205.132.145 port 49920 ssh2

2: Aug 29 16:27:28 fgb sshd[31100]: Failed password for root from 189.205.132.145 port 55661 ssh2

3: Aug 29 16:27:33 fgb sshd[31103]: Failed password for root from 189.205.132.145 port 33579 ssh2

4: Aug 29 16:27:37 fgb sshd[31106]: Failed password for root from 189.205.132.145 port 39344 ssh2

5: Aug 29 16:27:42 fgb sshd[31115]: Failed password for root from 189.205.132.145 port 45117 ssh2

6: Aug 29 16:27:46 fgb sshd[31124]: Failed password for root from 189.205.132.145 port 50881 ssh2

7: Aug 29 16:27:52 fgb sshd[31126]: Failed password for root from 189.205.132.145 port 56359 ssh2

8: Aug 29 16:27:57 fgb sshd[31128]: Failed password for root from 189.205.132.145 port 35882 ssh2

9: Aug 29 16:28:02 fgb sshd[31130]: Failed password for root from 189.205.132.145 port 41888 ssh2

10: Aug 29 16:28:08 fgb sshd[31132]: Failed password for root from 189.205.132.145 port 47882 ssh2

11: Aug 29 16:28:12 fgb sshd[31134]: Failed password for root from 189.205.132.145 port 53121 ssh2

12: Aug 29 16:28:17 fgb sshd[31136]: Failed password for root from 189.205.132.145 port 59014 ssh2

13: Aug 29 16:28:21 fgb sshd[31139]: Failed password for root from 189.205.132.145 port 36742 ssh2

有人破解,我们肯定要进行防范,使用以下的这段代码:

1: #!/bin/sh

2: SCANIP=`grep “Failed” /var/log/secure | awk ‘{print $(NF-3)}’ |sort|uniq -c|awk ‘{print $1″=”$2;}’`

3: for i in $SCANIP

4: do

5: NUMBER=`echo $i|awk -F= ‘{print $1}’`

6: SCANIP=`echo $i|awk -F= ‘{print $2}’`

7: echo “$NUMBER($SCANIP)”

8: if [ $NUMBER -gt 10 ] && [ -z “`iptables -vnL INPUT|grep $SCANIP`” ]

9: then

10: /sbin/iptables -I INPUT -s $SCANIP -m state –state NEW,RELATED,ESTABLISHED -j DROP

11: echo “`date` $SCANIP($NUMBER)” >> /var/log/scanip.log

12: fi

13: done

这段代码作用是:扫描secure安全日志文件,发现超过10次非法链接的ip,将其列入iptable防火墙禁止列表,并保存在记录文件中。

Mysql postfix socket ‘/var/lib/mysql/mysql.sock’ (2) 错误解决办法

ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2)

1、先查看 /etc/rc.d/init.d/mysqld status 看看m y s q l 是否已经启动.
另外看看是不是权限问题.

2、确定你的mysql.sock是不是在那个位置,
mysql -u 你的mysql用户名 -p -S /var/lib/mysql/mysql.sock

3、试试:service mysqld start

4、如果是权限问题,则先改变权限 #chown -R mysql:mysql /var/lib/mysql

[root@localhost ~]# /etc/init.d/mysqld start
启动 MySQL: [ 确定 ]
[root@localhost ~]# mysql -uroot -p

ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2)
原因是,/var/lib/mysql 的访问权限问题。

shell> chown -R mysql:mysql /var/lib/mysql

接着启动服务器

shell> /etc/init.d/mysql start

服务器正常启动后察看 /var/lib/mysql 自动生成mysql.sock文件。

但是我的问题仍然没有得到解决。

问题终于解决:
方法:    修改/etc/my.conf:
[mysqld]
datadir=/usr/local/mysql/data
socket=/var/lib/mysql/mysql.sock

[mysql.server]
user=mysql
basedir=/usr/local/mysql

If there is not currently a section called [client], add one at the bottom of the file and copy the socket= line under the [mysqld] section such as:
[client]
socket=/var/lib/mysql/mysql.sock

发现依旧如此,运行/etc/init.d/mysql start报错:    Starting MySQLCouldn’t find MySQL manager or server
是mysqld服务没启,运行/usr/local/mysql/bin/mysqld_safe &

CentOS 5.4 yum安装Apache+php+Mysql

Centos 里的 yum 在线安装很慢.以下是替换为中国CentOS镜像服务器!
中国官方镜像网站: http://centos.ustc.edu.cn/
/* 使用说明 */
cd /etc/yum.repos.d  [进入yum.repos.d目录]
mv CentOS-Base.repo CentOS-Base.repo.save   [修改源文件名称备份]
wget http://centos.ustc.edu.cn/CentOS-Base.repo.5  [下载]
mv CentOS-Base.repo.5 CentOS-Base.repo     [下载后的文件更名]

1. 更新系统内核到最新.
yum -y update
系统更新后,如果yum安装时提示错误信息,请执行以下命令修复.
rpm –import /etc/pki/rpm-gpg/RPM-GPG-KEY*

2. 安装Apahce, PHP, Mysql, 以及php连接mysql库组件

yum -y install httpd php mysql mysql-server php-mysql

//安装mysql扩展
yum -y install mysql-connector-odbc mysql-devel libdbi-dbd-mysql
//安装php的扩展
yum -y install php-gd php-xml php-mbstring php-ldap php-pear php-xmlrpc
//安装apache扩展
yum -y install httpd-manual mod_ssl mod_perl mod_auth_mysql

一次性粘贴安装:
yum -y install httpd php mysql mysql-server php-mysql httpd-manual mod_ssl mod_perl mod_auth_mysql php-mcrypt php-gd php-xml php-mbstring php-ldap php-pear php-xmlrpc mysql-connector-odbc mysql-devel libdbi-dbd-mysql

3. 启动服务配置

/sbin/chkconfig httpd on [设置apache为自启动]
/sbin/chkconfig mysqld on [mysqld服务]

/sbin/service httpd start [自启动 httpd 服务]
/sbin/service mysqld start [自启动mysqld服务]

4.设置mysql数据库root帐号密码。
mysqladmin -u root password ‘新密码’ [引号内填密码]

让mysql数据库更安全

代码:

mysql -u root -p [此时会要求你输入刚刚设置的密码,输入后回车即可

mysql> DROP DATABASE test; [删除test数据库]

mysql> DELETE FROM mysql.user WHERE user = ”; [删除匿名帐户]

mysql> FLUSH PRIVILEGES; [重载权限]

mysql> GRANT ALL PRIVILEGES ON *.* TO ‘root’@’192.168.1.13’ IDENTIFIED BY ‘密码’ WITH GRANT OPTION; [增加远程链接用户]

按照以上的安装方式, 配置出来的默认站点目录为/var/www/html/

测试环境

rpm -qa |grep mysql

rpm -qa |grep httpd

rpm -qa |grep php

5.配置文件存放的位置

apache配置文件存放的位置:/etc/httpd/conf/httpd.conf

php配置文件存放的位置:/etc/php.ini 和/etc/php.d里面的文件

mysql配置文件存放的位置:/etc/my.cnf

6、安装phpMyAdmin

进入phpMyAdmin官方下载(不要最新版本,下phpMyAdmin 2.11.9.5
就行了,3.1以上需php 5.2以上),上传到你的网站目录下,然后进行配置。只需几步即可搞定。

I. config.sample.inc.php更名为config.inc.php;

II. 打开config.inc.php文件,进行以下修改;

// $cfg[‘Servers’][$i][‘controluser’] = ‘pma’;

// $cfg[‘Servers’][$i][‘controlpass’] = ‘pmapass’;

// $cfg[‘Servers’][$i][‘pmadb’] = ‘phpmyadmin’;

// $cfg[‘Servers’][$i][‘bookmarktable’] = ‘pma_bookmark’;

// $cfg[‘Servers’][$i][‘relation’] = ‘pma_relation’;

// $cfg[‘Servers’][$i][‘table_info’] = ‘pma_table_info’;

// $cfg[‘Servers’][$i][‘table_coords’] = ‘pma_table_coords’;

// $cfg[‘Servers’][$i][‘pdf_pages’] = ‘pma_pdf_pages’

// $cfg[‘Servers’][$i][‘column_info’] = ‘pma_column_info’;

// $cfg[‘Servers’][$i][‘history’] = ‘pma_history’;

// $cfg[‘Servers’][$i][‘designer_coords’] = ‘pma_designer_coords’;

去掉每行前面的//;

II.$cfg[‘blowfish_secret’] = ”; |修改为| $cfg[‘blowfish_secret’] = ‘http’;

IV.$cfg[‘Servers’][$i][‘controluser’] = ‘pma’; |把’pma’修改为你的帐号|$cfg[‘Servers’][$i][‘controlpass’] = ‘pmapass’; |把’pmapass设置为你的mysql登录密码|

V. $cfg[‘blowfish_secret’] = ”; | 添加短语密码例如:$cfg[‘blowfish_secret’] = ‘onohot’;