Hosts用法两则

Hosts文件是好东西,还是跨平台的,不仅可以方便局域网名称的映射、加快域名解析速度,甚至还能让你访问某些无法解析域名的网站。今天我又琢磨出这个文件的另外两种用法。

利用Hosts文件隐藏公网地址

我有一个放在公网上的地址,用apache提供的web服务,但是我并不想让它公开,而是只允许我自己用;如果架在本机,那我出门或者回家就用不了了,同理只允许ip段的访问也不方便;用一个比较“深”的地址来访问也不是什么好办法,别说spider肯定能抓到,从这里点出去的链接,referer就把你给出卖了。

解决的思路就是,利用Hosts文件作出一个只有我自己能用的“域名”,当我使用这个域名访问的时候,由于本机的Hosts文件,它会解析到正确的地址,而别人或者蜘蛛或者从referer链接回来的访问,都会由于无法解析dns而失败。

当然这种做法只是一个小把戏而已,如果别人也知道域名和ip就不保密了,所以仍然需要结合其他限制访问方式使用。另外,对于只知道ip的访问,我们还是有办法的,将apache的conf如下设置:

NameVirtualHost *
<VirtualHost *>
    DocumentRoot /var/www/apache2-default/
    ServerName w3c.com
</VirtualHost>
<VirtualHost *>
    DocumentRoot /path/to/some/file/
    ServerName autobots.fwolf.com
</VirtualHost>

这样如果不知道autobots.fwolf.com这个秘密地址,就是知道ip也进不来。另外如果把autobots.fwolf.com这个域名再作个解析,就会更有意思了。

Hosts文件结合apache的VirtualHost创建易于调试的开发环境

这就比上面的简单多了,一般搭建项目调试环境的时候,如果管理不慎,会把服务器的设置搞得一塌糊涂,另外多项目并存的情况下,需要的环境、配置各不相同,有的还需要必须在网站根目录下运行,这种情况下,就可以用Hosts文件结合apache的virtualhost让一台开发服务器变身为多台。创意参考自虚拟主机的做法。

首先还是用hosts文件定义出别名来,比如computer1、computer2都指向一台服务器,但是这两个地址就像是在用两台电脑一样了;然后再在apache中用name方式的virtualhost定义project1、project2(每个project单独使用一个配置文件更方便,每个virtualhost还能够定制相应的环境)。

现在,知道该怎么使用了吧?要不怎么说hosts文件是个好东西呢。

Update @ 2007-08-09

MT主机用户可以在Domain -> DNS Settings中添加假的A记录,只要没有用MT来作dns服务器,那么这条A记录就只会在这台主机上有效。

Apache的Satisfy详解

最早接触Satisfy,是这样一个情况,一个目录启用了密码保护,想单独开放它的一个子目录,让这个子目录无须密码即可访问,那么需要这样设置:

<Directory "dir1">
    AllowOverride All
    AuthName "Fwolf's Vault"
    AuthType Basic
    AuthUserFile path/to/pwd/file
    require valid-user
</Directory>
<Directory "dir1/dir2">
    Allow from all
    Satisfy any
</Directory>

这样用户访问dir2的时候就不需要输入密码了。额外说明一点,dir1和dir2都是针对文件地址的,而非url。但这样实现的原理我以前真没想过,直到我碰到了一个蹊跷的问题。

环境和上面的一样,只是我在dir2下面,想禁止对一个文件的访问,于是dir2的设置变为:

<Directory "dir1/dir2">
    Allow from all
    Satisfy any
    <Files secret_file>
        order allow,deny
        deny from all
    </Files> 
</Directory>

deny from all换成allow from someip,即绝对禁止换成只允许指定ip地址,对我后面的描述没有影响)

好了,现在访问那个应该被禁止访问的文件secret_file,由于我本身不在允许ip范围之内,所以应该是不能访问的,可是。。。怎么弹出对话框让输入用户名和密码了?如果用wget访问的话,可以清楚的看到:

401 Authorization Required
Authorization failed.

等等,401错误是密码输入错误,可我明明是绝对禁止访问的设置嘛,而且如果用浏览器访问,并且输入正确的密码居然还是可以访问呢,哪里的设置出了问题?虽然一样都是无法访问,可问题一定要搞清楚才行。

其实,仔细看看Satisfy的官方文档,答案就在其中,Satisfy就是在同时启用了AllowRequire的情况下,指定相关策略的,一共有两个备选值,All表示用户必须同时满足AllowRequire的条件,而Any则是满足其中之一即可。

应用到我的问题上来,从上向下看,因为设置是从上级向下逐步解析的,最下层目录的配置具有最高的优先级,可以覆盖上级目录的设置,在.htaccess里也是一样。dir1中设置密码验证启用了Require;然后下级的dir2启用了Allow,并且用Satisfy any指定用户只要ip符合all或者输入正确密码就能访问,由于ip肯定会符合all这个条件,所以等同于取消了密码限制;最后的secret_file,把Allow进行了限制,注意,在这个时候,用户的ip不符合Allow,按照Satisfy any的定义,服务器自然会去查是否符合两个条件中的另外一个是否能够通过,所以就出现了密码输入框,而不是直接403 Forbidden

现在问题清楚了吧,只要在secret_file的配置中增加一句:

Satisfy All

覆盖掉dir2中的Satisfy any设置,就可以实现预期的403拒绝访问了。

PS: 记得以前<Files (fileA|fileB)>是可以用的,现在不起作用了,要改成正则才行:<Files ~ (fileA|fileB)>

查询并禁止apache中异常访问量的用户

Apache中的异常访问,通常指的是频繁、大量访问的用户,通过apache的log,结合linux下的几个命令,可以分析这些用户,并通过apache的配置文件来禁止他们访问。

首先,更改apache的log方式,不记录一些图像、css等文件,这样在log中每一行基本上都能对应一次访问,如果不去除图像等文件的记录,正常用户访问一个页面,同时也会下载页面上的图像、css等文件,会产生多条log记录,影响计数的结果。在apache的conf文件中增加如下配置:

SetEnvIfNoCase Request_URI \.css$ useless-file
SetEnvIfNoCase Request_URI \.gif$ useless-file
SetEnvIfNoCase Request_URI \.ico$ useless-file
SetEnvIfNoCase Request_URI \.jpg$ useless-file
SetEnvIfNoCase Request_URI \.js$ useless-file
CustomLog logs/fwolf.com/access.log combined env=!useless-file

这样就可以了,关于SetEnvIf的其他用法,可以参见Apache文档中SetEnvIfEnvironment Variables in Apache部分。

接下来,经过一段时间的运行,我们就可以分析log文件中访问量最大的用户了,只需要一条命令:

cat access.log |awk '{print $1}'| sort | uniq -c |sort -n

一点点的来看:

  • cat就不用说了;
  • awk的作用,就是把第一列,也就是客户端ip地址分拣出来;
  • 第一个sort,是把分拣出来的ip地址排序,这样相同的ip地址会排在一起
  • uniq是一个去除重复值的工具,但是带上-c参数,就可以统计出每个值出现了多少次
  • 最后的sort,把uniq产生的结果进行排序,按照-n这个参数的默认设置,最大的排在最下面。

所以,我们得到的结果应该是这样的:

......
2040 219.148.106.198
2047 218.12.78.14
2149 218.12.26.233
2205 121.28.4.34

第一列就是访问次数,第二列是ip地址,再回去对照log文件中的详细内容,如果发现哪个访问量大户是某某机器人的话,就可以禁止掉了,还是修改apache的conf文件:

<Directory />
    Order Deny,Allow
    Deny from 219.143.69.2
    Deny from 218.12.26.233
    Deny from 61.135.162.51
    Allow from all
</Directory>

如此反复监测、设置,直到没有人捣乱为止。

同理,如果想查看反复刷新查看某一页面的用户,可以用如下命令:

grep "GET /url/to/some/file" access.log |awk '{print $1}' |sort |uniq -c |sort -n

HTTP Referer二三事

什么是HTTP Referer

简言之,HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器我是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理。比如从我主页上链接到一个朋友那里,他的服务器就能够从HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。

Referer其实应该是英文单词Referrer,不过拼错的人太多了,所以编写标准的人也就将错就错了。

我的问题

我刚刚把feed阅读器改变为Gregarius,但他不像我以前用的liferea,访问新浪博客的时候,无法显示其中的图片,提示“此图片仅限于新浪博客用户交流与沟通”,我知道,这就是HTTP Referer导致的。

由于我上网客户端配置的特殊性,首先怀疑是squid的问题,但通过实验排除了,不过同时发现了一个Squid和Tor、Privoxy协同使用的隐私泄露问题,留待以后研究。

Gregarius能处理这个问题么?

答案是否定的,因为Gregarius只是负责输出html代码,而对图像的访问是有客户端浏览器向服务器请求的。

不过,安装个firefox扩展也许能解决问题,文中推荐的”Send Referrer”我没有找到,但发现另外一个可用的:”RefControl“,可以根据访问网站的不同,控制使用不同的Referer。

但是我不喜欢用Firefox扩展来解决问题,因为我觉得他效率太低,所以我用更好的方式——Privoxy。

Privoxy真棒

在Privoxy的default.action中添加两行:

{+hide-referrer{forge}}
.album.sina.com.cn

这样Gregarius中新浪博客的图片就出来了吧?+hide-referrer是Privoxy的一个过滤器,设置访问时对HTTP Referer的处理方式,后面的forge代表用访问地址当作Refere的,还可以换成block,代表取消Referer,或者直接把需要用的Referer网址写在这里。

用Privoxy比用Firefox简单的多,赶紧吧。

From https to http

我还发现,从一个https页面上的链接访问到一个非加密的http页面的时候,在http页面上是检查不到HTTP Referer的,比如当我点击自己的https页面下面的w3c xhtml验证图标(网址为http://validator.w3.org/check?uri=referer),从来都无法完成校验,提示:

No Referer header found!

原来,在http协议的rfc文档中有定义:

15.1.3 Encoding Sensitive Information in URI's

...

   Clients SHOULD NOT include a Referer header field in a (non-secure)
   HTTP request if the referring page was transferred with a secure
   protocol.

这样是出于安全的考虑,访问非加密页时,如果来源是加密页,客户端不发送Referer,IE一直都是这样实现的Firefox浏览器也不例外。但这并不影响从加密页到加密页的访问。

Firefox中关于Referer的设置

都在里,有两个键值:

  • network.http.sendRefererHeader (default=2) 设置Referer的发送方式,0为完全不发送,1为只在点击链接时发送,在访问页面中的图像什么的时候不发送,2为始终发送。参见Privacy Tip #3: Block Referer Headers in Firefox

  • network.http.sendSecureXSiteReferrer (default=true) 设置从一个加密页访问到另外一个加密页的时候是否发送Referer,true为发送,false为不发送。

利用Referer防止图片盗链

虽然Referer并不可靠,但用来防止图片盗链还是足够的,毕竟不是每个人都会修改客户端的配置。实现一般都是通过apache的配置文件,首先设置允许访问的地址,标记下来:

# 只允许来自domain.com的访问,图片可能就放置在domain.com网站的页面上
SetEnvIfNoCase Referer "^http://www.domain.com/" local_ref
# 直接通过地址访问
SetEnvIf Referer "^$" local_ref

然后再规定被标记了的访问才被允许:

<FilesMatch ".(gif|jpg)">
Order Allow,Deny
Allow from env=local_ref
</FilesMatch>

或者

<Directory /web/images>
   Order Deny,Allow
   Deny from all
   Allow from env=local_ref
</Directory>

这方面的文章网上很多,参考:

不要使用Rerferer的地方

不要把Rerferer用在身份验证或者其他非常重要的检查上,因为Rerferer非常容易在客户端被改变,不管是通过上面介绍的Firefox扩展,或者是Privoxy,甚至是libcurl的调用,所以Rerferer数据非常之不可信。

如果你想限制用户必须从某个入口页面访问的话,与其使用Referer,不如使用session,在入口页面写入session,然后在其他页面检查,如果用户没有访问过入口页面,那么对应的session就不存在,参见这里的讨论。不过和上面说的一样,也不要过于相信这种方式的“验证”结果。

个人感觉现在Rerferer除了用在防盗链,其他用途最多的就是访问统计,比如统计用户都是从哪里的链接访问过来的等等。

Ubuntu中Apache反向代理的使用

环境:Ubuntu 6.10 edgy, Apache 2.55

正常启用的方法很简单,首先要启用apache的proxy模块:

a2enmod proxy

然后在apache的conf中进行相应配置即可,比如我把/ubuntu反向代理到内网的另外一台主机,对公网进行服务:

<Location /ubuntu>
    ProxyPass http://svr5/ubuntu
    ProxyPassReverse http://svr5/ubuntu
</Location>

这样当公网用户访问我的主机的/ubuntu子目录时(注意/ubuntu这个Alias无需定义),Apache就会从访问内网服务器svr5的相应内容,并返回给用户。

以上是正常定义方式,不过在ubuntu下调用时却出现403错误,查看apache的error.log发现:

[Sun Apr 01 19:35:57 2007] [error] [client 121.*.*.*] client denied by server configuration: proxy:http://svr5/ubuntu/

查看svr5的log没有访问记录,就是本服务器的配置中的问题,再查看/etc/apache/mods-avaliable/proxy.conf,发现如下内容:

<IfModule mod_proxy.c>

    #turning ProxyRequests on and allowing proxying from all may allow 
    #spammers to use your proxy to send email.

ProxyRequests Off

<Proxy *>
    Order deny,allow
    Deny from all
    #Allow from .your_domain.com
</Proxy>

为了避免spammer使用服务器发送垃圾邮件,proxy模块启用后默认是关闭所有访问的,真想不通反向代理怎么就能够发送垃圾邮件了,不过找到问题之后,解决方法就有了,就是修改这个/etc/apache/mods-avaliable/proxy.conf文件,把#Allow from .your_domain.com修改为Allow from 用户地址,虽然可以用211.24来代表一定ip范围的用户,还是麻烦,干脆Allow from all也是可以的。如果觉得修改这个文件麻烦,也可以在httpd.conf中写上类似代码:

<proxy>
    Order deny,allow
    Allow from all
</proxy>

参考:

Web服务器内容的压缩输出

还在使用工具压缩 js 和 css 文件么?已经有些落伍啦,其实 web 服务器本身就具备压缩功能,可以把客户端所需要的内容压缩后再传输。当然这需要客户端浏览器支持压缩(通常是 gzip 方式),不过现在的主流浏览器比如 ie、firefox、opera、safari 都是支持滴。

首先可以用 Real-Time Compression Check 检查一下你现有的网站是否启用了压缩输出,如果没有,而且你使用的是 iis 系列 web 服务器的话,可以购买商业软件比如 ZipEnable 来实现;如果使用的是 apache 服务器,那你有福了,使用 mod_deflate 就可以了。

mod_deflate 的前身是 mod_gunzip 和 mod_gzip,他们大多用于 Apache 1.3分支,现在的 Apache 2.x系列中,就被 mod_deflate 替代了,并且功能更加丰富。mod_gunzip 和 mod_gzip 的区别在于:

mod_gunzip 需要服务器上的文件本身就是压缩存储的,如果浏览器不支持压缩的话,再由 web 服务器解压后传输给客户端,否则直接读取压缩文件传给客户端。这样作的好处就是不占用 cpu,并且大多数浏览器支持压缩,需要 web 服务器解压的不大。不过,碰到不支持压缩的机器人的话可就不好半喽。另外现在网站的内容大多是由程序动态生成的,在这种情况下 mod_gunzip 就没有用武之地了。

mod_gzip 则不需要文件预先压缩存储,而是在浏览器支持压缩的情况下,把数据压缩之后再传给客户端。这样会牺牲一些 cpu 代价,但是适用于网站内容由程序动态生成的情况,并且 gzip 的速度也是非常得快,每次 web 请求的数据量也都不大,所以实际的 cpu 占用并不会太多。并且如果文件本身就是压缩了的,比如 index.html.gz,那么这些文件会被直接输出,不用再压缩了(这样并不代表有了 index.html.gz,就能替代 index.html 作为目录索引页)。所以 mod_gzip 实际上覆盖和超越了 mod_gunzip 的功能。

mod_deflate 的启用在 ubuntu 中很简单,a2enmod deflate 就可以了,其他系统可以在 httpd.conf 中添加:

LoadModule deflate_module /usr/lib/apache2/modules/mod_deflate.so

mod_deflate 还需要配置一下,比如在 httpd.conf 中:

<ifmodule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php application/x-javascript text/css
</ifmodule>

指定对 html、txt、xml、php、js、css 等 mime 类型的文件进行压缩,application/x-httpd-php 和 application/x-javascript 是我加上的,不知道能否起作用。也可以按照文件扩展名或者浏览器类型进行压缩,还有更复杂的配置,可以查询官方的手册

参考:

Update @ 2008-03-31

今天在 apache 2.2.4 下看到还需要增加以下辅助配置:

AddEncoding x-gzip .gz	
AddType text/html .gz

主要是让 apache 能够正确设置和处理 .gz 文件,至于 MultiViews 选项我看设不设没什么影响,参考:The nGzip File Compressor