标签: 网站

  • SSL/TLS 协议运行机制的概述

    互联网的通信安全,建立在SSL/TLS协议之上。

    本文简要介绍SSL/TLS协议的运行机制。文章的重点是设计思想和运行过程,不涉及具体的实现细节。如果想了解这方面的内容,请参阅RFC文档

    be87125057d2eba95318914303391f76

    一、作用

    不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。

    (1) 窃听风险(eavesdropping):第三方可以获知通信内容。

    (2) 篡改风险(tampering):第三方可以修改通信内容。

    (3) 冒充风险(pretending):第三方可以冒充他人身份参与通信。

    SSL/TLS协议是为了解决这三大风险而设计的,希望达到:

    (1) 所有信息都是加密传播,第三方无法窃听。

    (2) 具有校验机制,一旦被篡改,通信双方会立刻发现。

    (3) 配备身份证书,防止身份被冒充。

    互联网是开放环境,通信双方都是未知身份,这为协议的设计带来了很大的难度。而且,协议还必须能够经受所有匪夷所思的攻击,这使得SSL/TLS协议变得异常复杂。

    二、历史

    互联网加密通信协议的历史,几乎与互联网一样长。

    1994年,NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布。

    1995年,NetScape公司发布SSL 2.0版,很快发现有严重漏洞。

    1996年,SSL 3.0版问世,得到大规模应用。

    1999年,互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版。

    2006年和2008年,TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版

    目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。但是,主流浏览器都已经实现了TLS 1.2的支持。

    TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。

    三、基本的运行过程

    SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。

    但是,这里有两个问题。

    (1)如何保证公钥不被篡改?

    解决方法:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。

    (2)公钥加密计算量太大,如何减少耗用的时间?

    解决方法:每一次对话(session),客户端和服务器端都生成一个”对话密钥”(session key),用它来加密信息。由于”对话密钥”是对称加密,所以运算速度非常快,而服务器公钥只用于加密”对话密钥”本身,这样就减少了加密运算的消耗时间。

    因此,SSL/TLS协议的基本过程是这样的:

    (1) 客户端向服务器端索要并验证公钥。

    (2) 双方协商生成”对话密钥”。

    (3) 双方采用”对话密钥”进行加密通信。

    上面过程的前两步,又称为”握手阶段”(handshake)。

    四、握手阶段的详细过程

    6c37780313051e51974a7e8e26c37e7e

    “握手阶段”涉及四次通信,我们一个个来看。需要注意的是,”握手阶段”的所有通信都是明文的。

    4.1 客户端发出请求(ClientHello)

    首先,客户端(通常是浏览器)先向服务器发出加密通信的请求,这被叫做ClientHello请求。

    在这一步,客户端主要向服务器提供以下信息。

    (1) 支持的协议版本,比如TLS 1.0版。

    (2) 一个客户端生成的随机数,稍后用于生成”对话密钥”。

    (3) 支持的加密方法,比如RSA公钥加密。

    (4) 支持的压缩方法。

    这里需要注意的是,客户端发送的信息之中不包括服务器的域名。也就是说,理论上服务器只能包含一个网站,否则会分不清应该向客户端提供哪一个网站的数字证书。这就是为什么通常一台服务器只能有一张数字证书的原因。

    对于虚拟主机的用户来说,这当然很不方便。2006年,TLS协议加入了一个Server Name Indication扩展,允许客户端向服务器提供它所请求的域名。

    4.2 服务器回应(SeverHello)

    服务器收到客户端请求后,向客户端发出回应,这叫做SeverHello。服务器的回应包含以下内容。

    (1) 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。

    (2) 一个服务器生成的随机数,稍后用于生成”对话密钥”。

    (3) 确认使用的加密方法,比如RSA公钥加密。

    (4) 服务器证书。

    除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供”客户端证书”。比如,金融机构往往只允许认证客户连入自己的网络,就会向正式客户提供USB密钥,里面就包含了一张客户端证书。

    4.3 客户端回应

    客户端收到服务器回应以后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。

    如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。

    (1) 一个随机数。该随机数用服务器公钥加密,防止被窃听。

    (2) 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

    (3) 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。

    上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称”pre-master key”。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把”会话密钥”。

    至于为什么一定要用三个随机数,来生成”会话密钥”,dog250解释得很好:

    “不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。

    对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。

    pre master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre master secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。”

    此外,如果前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息。

    4.4 服务器的最后回应

    服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的”会话密钥”。然后,向客户端最后发送下面信息。

    (1)编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。

    (2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。

    至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用”会话密钥”加密内容。

    8ac24c47a63c7eb487e019394dbb2e5d

    五、参考链接

    相关文章

    转载请注明:爱开源 » SSL/TLS 协议运行机制的概述

  • 天朝全静态路由

    背景介绍:
    由于近期“伟大的墙”越来越坚固,很多人不得不用上了VPN。但在VPN连接状态下,我们访问国内网站的速度会受到影响,同时也会造成VPN流量的浪费。

    有没有可能在系统中把所有天朝的静态路由都加上呢?这样,即使VPN连接状态下,所有的数据请求都会自动分流。
    答案是可以的,因为在APNIC上可以获取到所有天朝的公网IP段,我统计了一下总共有4000多个IP段,于是我写了一个脚本将其全部取出再循环添加到系统中,用起来效果真的很不错。

    脚本地址:https://github.com/mcsrainbow/shell-scripts/blob/master/scripts/smartroutes/smartroutes.sh

    安装部署:
    1. 下载smartroutes.sh
    2. 下载http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest
    3. 创建一个名为smartroutes的alias指向到smartroutes.sh
    注意:目前我的脚本仅支持Mac OS X,如果想要运行在Linux上,需要做一些简单的修改。

    操作示例:

    [dong@Dong-MacBookPro ~]$ smartroutes
    SmartRoutes is OFF
    
    [dong@Dong-MacBookPro ~]$ smartroutes on
    Adding the routes... Done
    
    [dong@Dong-MacBookPro ~]$ netstat -rn | head -n 20
    Routing tables
    
    Internet:
    Destination        Gateway            Flags        Refs      Use   Netif Expire
    default            10.192.168.1       UGSc           23        2    ppp0
    default            192.168.0.1        UGScI           1        0     en1
    1.0.1/24           192.168.0.1        UGSc            0        0     en1
    1.0.2/23           192.168.0.1        UGSc            0        0     en1
    1.0.8/21           192.168.0.1        UGSc            0        0     en1
    1.0.32/19          192.168.0.1        UGSc            0        0     en1
    1.1/24             192.168.0.1        UGSc            0        0     en1
    1.1.2/23           192.168.0.1        UGSc            0        0     en1
    1.1.4/22           192.168.0.1        UGSc            0        0     en1
    1.1.8/21           192.168.0.1        UGSc            0        0     en1
    1.1.16/20          192.168.0.1        UGSc            0        0     en1
    1.1.32/19          192.168.0.1        UGSc            0        0     en1
    1.2/23             192.168.0.1        UGSc            0        0     en1
    1.2.2/24           192.168.0.1        UGSc            0        0     en1
    1.2.4/24           192.168.0.1        UGSc            0        0     en1
    1.2.5/24           192.168.0.1        UGSc            0        0     en1
    
    [dong@Dong-MacBookPro ~]$ smartroutes off
    Deleting the routes... Done
    

    转载请注明:爱开源 » 天朝全静态路由

  • php的curl函数怎么样请求https的网站

    说起curl请求https的网站,网上的教程很多,无非都是说在你没有证书的情况下,加上下面两句就可以了

    <?php
    curl_setopt_array($handle, array(
                CURLOPT_SSL_VERIFYPEER => false,
                CURLOPT_SSL_VERIFYHOST => false,
    
            ));
    

    诚然,很多人在这么处理后就OK了,但我遇到的情况不一样,这两个加上之后,还是不能访问。

    于是问了vampire,他让我试了一下在命令行下加参数访问,如:curl -3 https://xxxxx.com,顺利的得到了结果,https还是有version的。于是在上述的脚本里再加上一句:

    CURLOPT_SSLVERSION     => 3,
    

    指定sslversion。

    当然,这个值 不一定是3,只是我正好是在version为3的情况下访问正常罢了。实际情况还需实际对待。

    话说回来,我在curl在访问的时候报的错是:

    curl: (35) error:14077458:SSL routines:SSL23_GET_SERVER_HELLO:reason(1112)
    

    Over。

    转载请注明:爱开源 » php的curl函数怎么样请求https的网站

  • 大型新闻网站点击量的技术方案

    网友提问:

    1.问题主题

    如何记录用户发表的文章的点击量

    2.问题补充描述

    当并发非常小的时候可以直接存在这个文章表里面,叫一个click_count,但是如果网站的访问量很高,那这样数据库肯定要累死,各位大牛有什么好的解决办法么?

    回答:

    我们只讨论访问量很高的情况,例如:每天1亿及以上PV的新闻网站,建议做法可以分为2种方式:

    1.使用缓存系统,比如Redis非常适合做计数器,异步的方式同步到MySQL数据库 或者Redis直接持久化的方式;

    2.变相直接更新数据库的方式,每个应用程序服务都有一个自己内部的全局计数器,默认每隔10秒或者缓存计数达到10则一次性更新同步到数据库中,也即采用延迟非实时更新的方式,同时把计数器单独保存到一张表中,设计成父子表的模式;

    上述2种方式作者都曾实施过,典型的场景是微博类的统计量太大,故采用第一种方式;阿里旺旺的新闻网站则采用第二种方式。

    转载请注明:爱开源 » 大型新闻网站点击量的技术方案

  • 如何正确识别Baiduspider

    经常听到有人抱怨百度蜘蛛爬的太频繁导致服务器被跑挂了,大部分情况下那些不是真的百度蜘蛛,而是一些采集站点来爬内容,这里替百度觉得冤。辨别爬虫是否是百度的,不单单看主机头,毕竟浏览器头信息是可以伪造的,一般我们通过DNS反向解析能更好的判断当前IP是否为真实的百度spider。

    当然不能排除有些站点确实是被搜索引擎spider拖垮的,不过不能只抱怨爬虫,能被拖垮,说明自身做得不够好,检查下程序哪里有瓶颈,该优化的优化该加机器的加机器,如果你不是靠搜索引擎活下来的,那么你可以毫不犹豫的直接屏蔽搜索引擎。

    想更好了解网站情况,可以加入百度站长(zhanzhang.baidu.com),可以设置索引压力、提交sitemap以及站点状况信息等等。

    如下内容摘自百度站长,关于如何辨别真实百度spider的方法。

    上周百度站长平台接到某站长求助,表示误封禁了Baiduspider的IP,询问是否有办法获得Baiduspider的所有IP,打算放入白名单加以保护,防止再次误封。在此要告诉各位站长,Baiduspider的IP池是不断变动的,我们无法提供IP全集。

    除此之外,之前还有站长发来质疑说Baiduspider光顾过于频繁,已超越服务器承受能力。而百度站长平台追查发现,Baiduspider对该站点的抓取并无异常,那只spider极有可能是个李鬼。

    那么,站长该如何通过IP来判断此spider是不是来自百度搜索引擎的呢?

    可以通过DNS反查方式来解决这个问题。根据平台不同验证方法不同,如linux/windows/os三种平台下的验证方法分别如下:

    1、在linux平台下,您可以使用host ip命令反解ip来判断是否来自Baiduspider的抓取。Baiduspider的hostname以 .baidu.com 或 .baidu.jp 的格式命名,非 .baidu.com 或 .baidu.jp 即为冒充。

    维日.jpg

    2、在windows平台或者IBM OS/2平台下,您可以使用nslookup ip命令反解ip来 判断是否来自Baiduspider的抓取。打开命令处理器 输入nslookup xxx.xxx.xxx.xxx(IP地 址)就能解析ip, 来判断是否来自Baiduspider的抓取,Baiduspider的hostname以.baidu.com 或.baidu.jp 的格式命名,非 .baidu.com 或 .baidu.jp 即为冒充。

    3、在mac os平台下,您可以使用dig 命令反解ip来 判断是否来自Baiduspider的抓取。打开命令处理器 输入dig xxx.xxx.xxx.xxx(IP地 址)就能解析ip, 来判断是否来自Baiduspider的抓取,Baiduspider的hostname以 .baidu.com 或.baidu.jp 的格式命名,非 .baidu.com 或 .baidu.jp 即为冒充。

    转自:http://zhanzhang.baidu.com/wiki/251

    转载请注明:爱开源 » 如何正确识别Baiduspider

  • python网站自动登录post数据的实例分析

    有个网友百度到了我的文章《python模拟登陆登陆二:获取和处理发送post request和head数据》,其中就遇到post的部分参数动态改变的问题,因为部分参数是javascript运行得到的结果。

    网友的邮件如下:

    很有幸能看到你的文章《python模拟登陆登陆一:验证码与cookies的同步处理思路》。

    学习了你的代码以后,我尝试着登陆我们学校的教务网站。目前遇到了一个比较麻烦的问题,这个问题我不懂。所以来请教一下,希望你能给我指点一二。谢谢。

    具体问题如下:

    学校教务网站:jwc1.mnu.cn

    登陆页面:http://jwc1.mnu.cn/_data/index_LOGIN.aspx

    在构造postData数据时,遇到两个变量参数,无法构造。

    参数名1:dsdsdsdsdxcxdfgfg

    参数名2:fgfggfdgtyuuyyuuckjg

    位于:

    ssf8wef4sf484w84

    如图:

    7E235B18@3C6DFB27.4A38E854

    目前我仔细分析,发现网页中有一段md5加密和这个参数有关。

    其代码是:

    function chkpwd(obj) {
      if(obj.value!='')
          { var     s=md5(document.all.txt_asmcdefsddsd.value+md5(obj.value).substring(0,30).toUpperCase()+'10639').substring(0,30).toUpperCase();   document.all.dsdsdsdsdxcxdfgfg.value=s;}
    else { document.all.dsdsdsdsdxcxdfgfg.value=obj.value;} }
    
    function chkyzm(obj) {
    if(obj.value!='')
    {   var s=md5(md5(obj.value.toUpperCase()).substring(0,30).toUpperCase()+'10639').substring(0,30).toUpperCase();   document.all.fgfggfdgtyuuyyuuckjg.value=s;}
    else {    document.all.fgfggfdgtyuuyyuuckjg.value=obj.value.toUpperCase();}}
    

    位于:

    BCC9483E@3C6DFB27.4A38E854

    登录帐号:1109030201

    密码:123456

    我的回复:

    首先要跟你说下抱歉,今天晚上才看到你的邮件。因为我博客上留下的是163邮箱,但是不知道为什么,你发的邮件并没有转发到我的主邮箱。我今天有事偶然登录网易账户才看到你的邮件。不知道你现在是否已经解决了你的问题,这里我说下我的解决思路吧。解决思路:你的教学网站将你输入的分别用chkpwd(obj) 来加密得到参数 dsdsdsdsdxcxdfgfg 和 chkyzm(obj)来加密得到参数fgfggfdgtyuuyyuuckjg。而这两个加密函数都调用了md5.js这个js文件参与加密。我们无须管md5.js的加密过程,只需要调用它得到结果就行了。

    function chkpwd(obj) {  if(obj.value!='')  {    var s=md5(document.all.txt_asmcdefsddsd.value+md5(obj.value).substring(0,30).toUpperCase()+'10639').substring(0,30).toUpperCase();   document.all.dsdsdsdsdxcxdfgfg.value=s;} else { document.all.dsdsdsdsdxcxdfgfg.value=obj.value;} }
    

    解释:
    document.all.txt_asmcdefsddsd.value 这个参数的值就是学号
    obj.value 其实就是你的登录密码

    function chkyzm(obj) {  if(obj.value!='') {   var s=md5(md5(obj.value.toUpperCase()).substring(0,30).toUpperCase()+'10639').substring(0,30).toUpperCase();   document.all.fgfggfdgtyuuyyuuckjg.value=s;} else {    document.all.fgfggfdgtyuuyyuuckjg.value=obj.value.toUpperCase();}}
    

    解释:
    obj.value 就是验证码的值

    由此可以,第一个post需要的参数dsdsdsdsdxcxdfgfg的值肯定是固定的,因为帐号和密码是不变的。
    而 第二个post需要的参数fgfggfdgtyuuyyuuckjg的值是随着验证码改变而时刻变化的。所以,我建议采取的方案是我们直接调用这两个加密 的函数,而无需管它加密的过程,我们只要结果就行,我们唯一需要做的就是将帐号,密码,验证码作为参数传递给这两个加密函数就可以了。

    那么,现在的问题就是如何在python中调用这js代码了,有两种方法。
    第一种就是直接在python中调用js代码,涉及到python与javascript的交互,需要用到一些模块。所以加密函数的代码要稍微改改。
    第二种方法,用python实现这两个加密函数。这两个加密函数很容易用python实现,其中的md5.js作用就是使用md5加密字符串,python很容易实现,其余的就是转换大小写,截取字符长度等,一样容易实现。

    ==================================
    分析完毕,上面的思路希望对你有用。如果你已经解决,可以分享下你的思路。

    附上我验证两个加密函数作用的测试文件:,直接解压运行index.html就可以了。

    自动登录思路

    index.htnl

    <html>
    <head>
    <script type="text/javascript"  src="md5.js"></script>
    <script type="text/javascript">
    
    //这里我假设帐号是1109030201
    //登录密码是123456
    function chkpwd() {
    var s=md5('1109030201'+md5('123456').substring(0,30).toUpperCase()+'10639').substring(0,30).toUpperCase();
    
    window.alert(s);
    
    }
    
    chkpwd();
    
    </script
    
    </head>
    <body></body>
    
    </html>
    

    两个加密函数.js:

    function chkpwd(obj) {  if(obj.value!='')  {    var s=md5(document.all.txt_asmcdefsddsd.value+md5(obj.value).substring(0,30).toUpperCase()+'10639').substring(0,30).toUpperCase();   document.all.dsdsdsdsdxcxdfgfg.value=s;} else { document.all.dsdsdsdsdxcxdfgfg.value=obj.value;} }
    
    function chkyzm(obj) {  if(obj.value!='') {   var s=md5(md5(obj.value.toUpperCase()).substring(0,30).toUpperCase()+'10639').substring(0,30).toUpperCase();   document.all.fgfggfdgtyuuyyuuckjg.value=s;} else {    document.all.fgfggfdgtyuuyyuuckjg.value=obj.value.toUpperCase();}}
    

    md5.js:

    function md5js(pass, code, uin) {
        var I = hexchar2bin(md5(pass));
        var H = md5(I + uin);
        var G = md5(H + code.toUpperCase());
        return G
    }
    var hexcase = 1;
    var b64pad = "";
    var chrsz = 8;
    var mode = 32;
    
    function md5(A) {
        return hex_md5(A)
    }
    
    function hex_md5(A) {
        return binl2hex(core_md5(str2binl(A), A.length * chrsz))
    }
    
    function str_md5(A) {
        return binl2str(core_md5(str2binl(A), A.length * chrsz))
    }
    
    function hex_hmac_md5(A, B) {
        return binl2hex(core_hmac_md5(A, B))
    }
    
    function b64_hmac_md5(A, B) {
        return binl2b64(core_hmac_md5(A, B))
    }
    
    function str_hmac_md5(A, B) {
        return binl2str(core_hmac_md5(A, B))
    }
    
    function core_md5(K, F) {
        K[F >> 5] |= 128 << ((F) % 32);
        K[(((F + 64) >>> 9) << 4) + 14] = F;
        var J = 1732584193;
        var I = -271733879;
        var H = -1732584194;
        var G = 271733878;
        for (var C = 0; C < K.length; C += 16) {
            var E = J;
            var D = I;
            var B = H;
            var A = G;
            J = md5_ff(J, I, H, G, K[C + 0], 7, -680876936);
            G = md5_ff(G, J, I, H, K[C + 1], 12, -389564586);
            H = md5_ff(H, G, J, I, K[C + 2], 17, 606105819);
            I = md5_ff(I, H, G, J, K[C + 3], 22, -1044525330);
            J = md5_ff(J, I, H, G, K[C + 4], 7, -176418897);
            G = md5_ff(G, J, I, H, K[C + 5], 12, 1200080426);
            H = md5_ff(H, G, J, I, K[C + 6], 17, -1473231341);
            I = md5_ff(I, H, G, J, K[C + 7], 22, -45705983);
            J = md5_ff(J, I, H, G, K[C + 8], 7, 1770035416);
            G = md5_ff(G, J, I, H, K[C + 9], 12, -1958414417);
            H = md5_ff(H, G, J, I, K[C + 10], 17, -42063);
            I = md5_ff(I, H, G, J, K[C + 11], 22, -1990404162);
            J = md5_ff(J, I, H, G, K[C + 12], 7, 1804603682);
            G = md5_ff(G, J, I, H, K[C + 13], 12, -40341101);
            H = md5_ff(H, G, J, I, K[C + 14], 17, -1502002290);
            I = md5_ff(I, H, G, J, K[C + 15], 22, 1236535329);
            J = md5_gg(J, I, H, G, K[C + 1], 5, -165796510);
            G = md5_gg(G, J, I, H, K[C + 6], 9, -1069501632);
            H = md5_gg(H, G, J, I, K[C + 11], 14, 643717713);
            I = md5_gg(I, H, G, J, K[C + 0], 20, -373897302);
            J = md5_gg(J, I, H, G, K[C + 5], 5, -701558691);
            G = md5_gg(G, J, I, H, K[C + 10], 9, 38016083);
            H = md5_gg(H, G, J, I, K[C + 15], 14, -660478335);
            I = md5_gg(I, H, G, J, K[C + 4], 20, -405537848);
            J = md5_gg(J, I, H, G, K[C + 9], 5, 568446438);
            G = md5_gg(G, J, I, H, K[C + 14], 9, -1019803690);
            H = md5_gg(H, G, J, I, K[C + 3], 14, -187363961);
            I = md5_gg(I, H, G, J, K[C + 8], 20, 1163531501);
            J = md5_gg(J, I, H, G, K[C + 13], 5, -1444681467);
            G = md5_gg(G, J, I, H, K[C + 2], 9, -51403784);
            H = md5_gg(H, G, J, I, K[C + 7], 14, 1735328473);
            I = md5_gg(I, H, G, J, K[C + 12], 20, -1926607734);
            J = md5_hh(J, I, H, G, K[C + 5], 4, -378558);
            G = md5_hh(G, J, I, H, K[C + 8], 11, -2022574463);
            H = md5_hh(H, G, J, I, K[C + 11], 16, 1839030562);
            I = md5_hh(I, H, G, J, K[C + 14], 23, -35309556);
            J = md5_hh(J, I, H, G, K[C + 1], 4, -1530992060);
            G = md5_hh(G, J, I, H, K[C + 4], 11, 1272893353);
            H = md5_hh(H, G, J, I, K[C + 7], 16, -155497632);
            I = md5_hh(I, H, G, J, K[C + 10], 23, -1094730640);
            J = md5_hh(J, I, H, G, K[C + 13], 4, 681279174);
            G = md5_hh(G, J, I, H, K[C + 0], 11, -358537222);
            H = md5_hh(H, G, J, I, K[C + 3], 16, -722521979);
            I = md5_hh(I, H, G, J, K[C + 6], 23, 76029189);
            J = md5_hh(J, I, H, G, K[C + 9], 4, -640364487);
            G = md5_hh(G, J, I, H, K[C + 12], 11, -421815835);
            H = md5_hh(H, G, J, I, K[C + 15], 16, 530742520);
            I = md5_hh(I, H, G, J, K[C + 2], 23, -995338651);
            J = md5_ii(J, I, H, G, K[C + 0], 6, -198630844);
            G = md5_ii(G, J, I, H, K[C + 7], 10, 1126891415);
            H = md5_ii(H, G, J, I, K[C + 14], 15, -1416354905);
            I = md5_ii(I, H, G, J, K[C + 5], 21, -57434055);
            J = md5_ii(J, I, H, G, K[C + 12], 6, 1700485571);
            G = md5_ii(G, J, I, H, K[C + 3], 10, -1894986606);
            H = md5_ii(H, G, J, I, K[C + 10], 15, -1051523);
            I = md5_ii(I, H, G, J, K[C + 1], 21, -2054922799);
            J = md5_ii(J, I, H, G, K[C + 8], 6, 1873313359);
            G = md5_ii(G, J, I, H, K[C + 15], 10, -30611744);
            H = md5_ii(H, G, J, I, K[C + 6], 15, -1560198380);
            I = md5_ii(I, H, G, J, K[C + 13], 21, 1309151649);
            J = md5_ii(J, I, H, G, K[C + 4], 6, -145523070);
            G = md5_ii(G, J, I, H, K[C + 11], 10, -1120210379);
            H = md5_ii(H, G, J, I, K[C + 2], 15, 718787259);
            I = md5_ii(I, H, G, J, K[C + 9], 21, -343485551);
            J = safe_add(J, E);
            I = safe_add(I, D);
            H = safe_add(H, B);
            G = safe_add(G, A)
        }
        if (mode == 16) {
            return Array(I, H)
        } else {
            return Array(J, I, H, G)
        }
    
    }
    
    function md5_cmn(F, C, B, A, E, D) {
        return safe_add(bit_rol(safe_add(safe_add(C, F), safe_add(A, D)), E), B)
    }
    
    function md5_ff(C, B, G, F, A, E, D) {
        return md5_cmn((B & G) | ((~B) & F), C, B, A, E, D)
    }
    
    function md5_gg(C, B, G, F, A, E, D) {
        return md5_cmn((B & F) | (G & (~F)), C, B, A, E, D)
    }
    
    function md5_hh(C, B, G, F, A, E, D) {
        return md5_cmn(B ^ G ^ F, C, B, A, E, D)
    }
    
    function md5_ii(C, B, G, F, A, E, D) {
        return md5_cmn(G ^ (B | (~F)), C, B, A, E, D)
    }
    
    function core_hmac_md5(C, F) {
        var E = str2binl(C);
        if (E.length > 16) {
            E = core_md5(E, C.length * chrsz)
        }
        var A = Array(16),
            D = Array(16);
        for (var B = 0; B < 16; B++) {
            A[B] = E[B] ^ 909522486;
            D[B] = E[B] ^ 1549556828
        }
        var G = core_md5(A.concat(str2binl(F)), 512 + F.length * chrsz);
        return core_md5(D.concat(G), 512 + 128)
    }
    
    function safe_add(A, D) {
        var C = (A & 65535) + (D & 65535);
        var B = (A >> 16) + (D >> 16) + (C >> 16);
        return (B << 16) | (C & 65535)
    }
    
    function bit_rol(A, B) {
        return (A << B) | (A >>> (32 - B))
    }
    
    function str2binl(D) {
        var C = Array();
        var A = (1 << chrsz) - 1;
        for (var B = 0; B < D.length * chrsz; B += chrsz) {
            C[B >> 5] |= (D.charCodeAt(B / chrsz) & A) << (B % 32)
        }
        return C
    }
    
    function binl2str(C) {
        var D = "";
        var A = (1 << chrsz) - 1;
        for (var B = 0; B < C.length * 32; B += chrsz) {
            D += String.fromCharCode((C[B >> 5] >>> (B % 32)) & A)
        }
        return D
    }
    
    function binl2hex(C) {
        var B = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
        var D = "";
        for (var A = 0; A < C.length * 4; A++) {
            D += B.charAt((C[A >> 2] >> ((A % 4) * 8 + 4)) & 15) + B.charAt((C[A >> 2] >> ((A % 4) * 8)) & 15)
        }
        return D
    }
    
    function binl2b64(D) {
        var C = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        var F = "";
        for (var B = 0; B < D.length * 4; B += 3) {
            var E = (((D[B >> 2] >> 8 * (B % 4)) & 255) << 16) | (((D[B + 1 >> 2] >> 8 * ((B + 1) % 4)) & 255) << 8) | ((D[B + 2 >> 2] >> 8 * ((B + 2) % 4)) & 255);
            for (var A = 0; A < 4; A++) {
                if (B * 8 + A * 6 > D.length * 32) {
                    F += b64pad
                } else {
                    F += C.charAt((E >> 6 * (3 - A)) & 63)
                }
    
            }
    
        }
        return F
    }
    
    function hexchar2bin(str) {
        var arr = [];
        for (var i = 0; i < str.length; i = i + 2) {
            arr.push("\\x" + str.substr(i, 2))
        }
        arr = arr.join("");
        eval("var temp = '" + arr + "'");
        return temp
    }
    

    转载请注明:爱开源 » python网站自动登录post数据的实例分析

  • 新型任意文件读取漏洞的研究

    0x00 前言


    早前发现boooom在乌云上发了很多个任意文件读取的漏洞,都是形如

    http://target/../../../../etc/passwd
    

    这样。当时感觉很新奇,因为正常情况下,通常的服务器中间件是不允许直接读取web目录以外的文件的,为什么这样的漏洞却出现在了很多案例中。

    后来在lijiejie的文章给出了解释:http://www.lijiejie.com/python-django-directory-traversal/ ,原来是python这种新型web开发方式造成的问题。然后翻了下我自己以前用web.py、tornado开发的一些应用,果然也存在这样的问题。

    这个问题就像lijiejie说的那样,一方面是低版本django框架自身的一些漏洞,另一方面,就是开发者自身的疏忽造成的问题。

    这不得不提到现今开发框架与以前的一些区别。不管是python还是node、ruby的框架,都是一个可以自定义URL分配的框架,不再是像php或asp中那样根据目录结构来请求文件。所有的请求由用户定义规则,而框架内核部分解析、配发、执行。比如我们请求的“/login/”这个URL,很可能是被配发给一个LoginHandler类去处理了,而不是请求到/login/index.php上。

    这时候造成了一个问题,如果我们就是想去请求一个真实的文件,比如css、js等静态文件,怎么办?

    一般也会有一些区分,一些要求比较高的应用,多是采用了CDN缓存或负载均衡,nginx作为负载分配的处理器。当发现我们请求的url是一个静态文件的话,就直接由CDN或nginx返回相应文件。如下图:

    2015030401212143740

    那么这之中也存在这一个定义问题,什么请求才说明是要请求“静态文件”?只要以.css、.js结尾就可以吗?当然这也是一种方法,但一般应用会定义一个目录,如/static/,所有请求匹配“/static/(.*)”的会被认为是静态文件,所以开发者一般将静态文件放在这个目录下,我们用户就能够直接请求到他了。

    如果不存在CDN、nginx等平台,其实类似web.py、tornado这样的框架自己也定义了静态目录,在web.py下,默认的静态目录都是/static/,也就是在这个目录下的请求是不会经过URLPath的。如web.py文档中说到的:

    http://webpy.org/cookbook/staticfiles.zh-cn

    这时候,我就会有这个思考,框架内部如果是以/static/(.*)来匹配请求的话,如果我们求

    /static/../../../../../etc/passwd
    

    是不是就可以读取到/etcs/passwd文件?

    0x01 web.py下可能的任意文件读取漏洞研究


    我们先来看看web.py是怎样处理这种请求的:

    /static/../../../../../etc/passwd
    

    我们在web/httpserver.py中可以看到这样的代码:

    def do_GET(self):
        if self.path.startswith('/static/'):
            SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
        else:
            self.run_wsgi_app()
    

    当请求以/static/开头的话,就直接交给SimpleHTTPServer处理了。SimpleHTTPServer是python自带的一个简单的HTTP Server,我们在任意一个目录下执行

    python -m SimpleHTTPServer
    

    都会启动一个web服务器,可以直接通过HTTP协议访问目录下的文件。

    web.py的Server其实就是对SimpleHTTPServer的一个继承与重写,这里它简单的把这个请求交给父类SimpleHTTPServer处理,而这个HTTP Server当然不会允许请求到web目录(也就是./static/)以外的地方去,所以得到的回复是404:

    2015030401212152015

    框架本身保证了静态文件不会造成任意文件读取。但复杂的逻辑关系应用中,开发者往往不满足于/static/一种静态目录。比如,网站允许用户上传、下载文件,可能我们会新建一个uploadfile目录,按日期、时间专门保存上传的文件。

    那么,开发者为了让/uploadfile目录下的文件也能被直接访问,往往会这样写:

    #!/usr/bin/python
    import web
    
    urls = (
        '/uploadfile/(.*)', 'download',
        '/', 'hello',
    )
    app = web.application(urls, globals())
    
    class hello:
        def GET(self, name):
            if not name:
                name = 'World'
            return 'Hello, ' + name + '!'
    
    class download:
        def GET(self, filepath):
            try:
                with open("./uploadfile/%s" % filepath, "rb") as f:
                    content = f.read()
                return content
            except:
                return web.notfound("Sorry, the file you were looking for was not found.")
    
    if __name__ == "__main__":
        app.run()
    

    有个download类专门解析这类请求,直接在GET方法中读取文件,并作为response写入HTTP数据包。

    我们请求一个正常的文件/uploadfile/01.txt,是可以得到它的内容的:

    2015030401212283650

    但我们请求一个非uploadfile目录下的文件,却发现也能读取,导致了一个任意文件读取漏洞:

    2015030401212244694

    这就是由于开发者的失误,并没有检查我们传入的path是否合法而导致,与框架无关。

    0x02 Tornado下可能的任意文件读取漏洞研究


    tornado是一个全异步的web框架,它允许我们在配置中定义静态目录static_path。在tornado中,专门给出了一个方法来验证我们的请求是否在允许的目录内:

    def validate_absolute_path(self, root, absolute_path):
        """Validate and return the absolute path.
    
        ``root`` is the configured path for the `StaticFileHandler`,
        and ``path`` is the result of `get_absolute_path`
    
        This is an instance method called during request processing,
        so it may raise `HTTPError` or use methods like
        `RequestHandler.redirect` (return None after redirecting to
        halt further processing).  This is where 404 errors for missing files
        are generated.
    
        This method may modify the path before returning it, but note that
        any such modifications will not be understood by `make_static_url`.
    
        In instance methods, this method's result is available as
        ``self.absolute_path``.
    
        .. versionadded:: 3.1
        """
        root = os.path.abspath(root)
        # os.path.abspath strips a trailing /
        # it needs to be temporarily added back for requests to root/
        if not (absolute_path + os.path.sep).startswith(root):
            raise HTTPError(403, "%s is not in root static directory",
                            self.path)
        if (os.path.isdir(absolute_path) and
                self.default_filename is not None):
            # need to look at the request.path here for when path is empty
            # but there is some prefix to the path that was already
            # trimmed by the routing
            if not self.request.path.endswith("/"):
                self.redirect(self.request.path + "/", permanent=True)
                return
            absolute_path = os.path.join(absolute_path, self.default_filename)
        if not os.path.exists(absolute_path):
            raise HTTPError(404)
        if not os.path.isfile(absolute_path):
            raise HTTPError(403, "%s is not a file", self.path)
        return absolute_path
    

    这样,一旦请求不在我们定义的静态目录下,就会抛出“is not in root static directory”错误:

    2015030401212211169

    那么如果tornado中,也需要定义一个”/uploadfile/“作为用户上传目录,那么我们怎么做?

    文档中也提到了,我们只需要自定义一个URLPath即可,tornado内部有专门处理静态文件的控制器web.StaticFileHandler:

    application = web.Application([
    
        (r"/uploadfile/(.*)", web.StaticFileHandler, {"path": "/var/www"}),
    
    ])
    

    就算不考虑安全问题,作为一个异步的框架,如果我们还用同步的read、write这些IO函数自己去处理静态文件,也是不可取的。

    不过,在后面的研究中,我也发现了tornado的处理方式并不算完美,更多详情可以等这个洞公开后查看:

    WooYun: Python开源框架Tornado某缺陷可能造成文件读取漏洞

    0x03 Django中的问题


    Django低版本自身存在的漏洞导致的任意文件读取,实际上就是犯了我之前说的静态文件未检查的问题,如这个09年的BUG:

    https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2009-2659

    2015030401212396741

    正如我在web.py中说到的,如果django也单纯使用open、read来读取文件,而不检查PATH的合法性,同样能够造出任意文件读取。

    比如我们django的view如此写:

    from django.shortcuts import render
    from django.http import HttpResponse
    
    # Create your views here.
    
    def index(request, path):
        with open(path, "rb") as f:
            content = f.read()
        return HttpResponse(content)
    

    没有验证path的合法性,依旧可以出现任意文件读取的现象:

    2015030401212353218

    如上图,直接读取了sqlite数据库内容,拿下管理员账号密码。

    那么我们怎么在这样的应用中防御任意文件读取?我们简单修改django的view防御这个漏洞:

    from django.shortcuts import render
    from django.http import HttpResponse
    from django.http import Http404
    import os
    
    # Create your views here.
    
    def index(request, path):
        static = "uploadfile"
        root = os.path.join(os.getcwd(), static) + os.path.sep
        path = os.path.abspath(root + path)
        if not (path).startswith(root):
            raise Http404
        else:
            with open(path, "rb") as f:
                content = f.read()
            return HttpResponse(content)
    

    其实有些框架都有自己检查静态文件的方式,django自身应该也有的。像web.py这样的轻型框架没有自带函数检查的情况下,可以考虑用我上面写的这个方法来剔除不合法的静态文件路径。

    0x04 PHP等语言会不会出现这个问题?


    这个问题其实是有思考价值的。

    最开始我就提到了,现代的python/node/ruby等web开发框架与老式的php、asp等语言的区别,也是造成这个漏洞的原因之一就是因为URL的分配导致静态文件不能被直接访问到,所以需要自定义静态文件的访问方式。但一旦访问参数未检查,就造成任意文件读取问题。

    但传统php应用就是一个以目录形式访问的,静态文件访问应该不会经过php的,这确实是一个很大的区别。

    先不论我们的请求会不会经过php,看到zblog最新版中一个实际的案例。

    /zb_system/function/c_system_event.php
    

    大概415行:

    if (isset($_SERVER['SERVER_SOFTWARE']) && (strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS') !== false) && (isset($_GET['rewrite']) == true)){
        //iis+httpd.ini下如果存在真实文件
        $realurl = $zbp->path . urldecode($url);
        if(is_readable($realurl)&&is_file($realurl)){
            die(file_get_contents($realurl));
        }
        unset($realurl);
    }
    

    这里判断了

    $_SERVER['SERVER_SOFTWARE']
    

    是否包含Microsoft-IIS,当服务器中间件是IIS,而且

    $_GET['rewrite']
    

    的话,就进入这个if语句。再判断$url指向的文件是否存在,存在就把它使用file_get_contents读取并显示出来。

    实际上这段代码所做的工作和我们之前看到的python代码是一样的。为什么php也需要这样的工作,我们url请求的文件不应该就直接由webserver返回给用户了吗?

    实际上,这里开发者也考虑了url重写造成的问题。zblog重写的规则是将所有请求都指向index.php去处理,最后由index.php去处理,有可能我们的静态文件就被rewrite到index.php去了,这里的工作就是把被重写到php里的这个静态文件直接显示出来。

    可惜我还是才疏学浅,不知道怎么写rewrite规则才能让静态文件请求被重写到index.php里,所以也做不到任意文件读取了。

    但我们这里很明显的可以发现,zblog的开发者也没有检查这个$url是否在网站目录内,或是否在静态文件目录内,也是直接读取显示了。导致我们请求http://10.211.55.3/zblog/index.php?action=&rewrite=1是可以读取index.php的源码的(因为我没有IIS环境,我手工将SERVER_SOFTWARE改成IIS了~):

    2015030401212379345

    所以,传统PHP应用下是否可能存在这样的安全漏洞,这个问题还是有待继续研究的。理论上,我们如果写出一个这样的rewrite规则:将所有请求都交给index.php处理。那么,index.php的功能实际上就和之前的python框架主文件功能类似了。

    0x05 结语


    最后,自己也只是浅显得研究和讨论了几种python框架、php某个特殊情况的漏洞,但现代的开发技术,包括企业级的一些环境我并不熟悉也没怎么接触过,所以有什么欠考虑和不完善的地方,也需要各位去补充与纠正。

    转载请注明:爱开源 » 新型任意文件读取漏洞的研究

  • python模拟登陆登陆二:获取和处理发送post request和head数据

    上篇文章《python模拟登陆登陆一:验证码与cookies的同步处理思路》 ,我验证了下自动登录的过程,以cookies与验证码如何同步的问题。

    今天这篇文章说下如何获取和处理发送post request和head数据。

    工具:

    firefox浏览器及firebug插件。

    (其他的如httpfox,live http head ,fiddler,httpwatch 也行)

    1.查看分析登陆页面html代码,看是否有iframe

    我们写一个自动登录的脚本的时候,先要分析出需要post request和head数据,以及post的网址等。这里,我们先打开firebug开始监控,然后打开网站的登陆页面http://zhuzhou2013.feixuelixm.teacher.com.cn/IndexPage/Index.aspx 。用firebug查看页面的html代码,发了输入账号和密码的那个登陆窗口居然是个iframe 。其实,在第一篇文章的时候,有经验的就会发现post的url地址与登陆的url太不 一样了 。我自己也因为懒得查看源码和分析,直接写了,运行的时候,其实已经登陆进去了,但是因为是python脚本返回的网页html代码里iframe不会出现,所以总司验证失败,以为自己代码有问题。所以,建议写之前先分析网页是否有iframe ,这样可以减少步骤和代码。 我这里就当做自己没分析出源登陆地址,还是在http://zhuzhou2013.feixuelixm.teacher.com.cn/IndexPage/Index.aspx这个地址来获得post的数据。

    2.输入密码登陆网站,查看post的数据和head

    登陆网站,我们可以通过firebug的“网络(network)”,找到我们所需要的post的地址和request及response数据。如下图在头信息中我们能看到post request head 和 response head 的信息,见图一:

    post的地址:http://zhuzhou2013.feixuelixm.teacher.com.cn/GuoPeiAdmin/Login/Login.aspx

    headinfo2

    在post中,我们能看到post所需要的数据,如下图2(账户和密码我都去掉了):

    postdata3

    可见,在post request head(请求头信息)中,需要调用cookies的值。而我们打开网站登录页面,准备登陆的时候,会获得很多cookies,可以从firebug中“cookies”标签中看到,有下面一些cookies,如图3:

    lotcookies4

    这么多cookies,那些才是需要的呢.?

    这个很简单,firebug既有查看cookies的功能,又有删除cookies的功能,在某个cookies上单击鼠标右键,选择删除 。然后再登陆,如果删除的这个cookies不是必须的,那么我们就能登陆成功。如此重复,就知道哪些是必须的。这里说下,如果cookies的域都不是我们要登陆的网站的url,那么可以肯定这个cookies不是必须的,如上图中的,looyu_id ,looyu_23945 。

    同理,post的 request head 有部分也是不需要的,如X-FireLogger,x-kn-appId,x-fsn,x-Firelogger等,都是不必要的的,因为这个既可能是网站估计迷惑我们,也有可能是网页其他元素干扰的。这些也可以通过排除法来,写代码来测试。当然,一般开始写都尽量全部写。

    其实,我们还可以先屏蔽一些网页上无关的页面元素,用abp直接屏蔽doyoo.net 和looyu.net 两个域名,同时屏蔽了这两个网站的cookies。这样,我们就不会受这两个网站的cookies的误导,然后测试登陆,可以一次测试就排除多个cookies,减少时间和步骤。

    post request head还有一个麻烦的地方,就是它包含了cookies的值,见图1 。这就需要我们对cookiejar的值进行处理,因为cookiejar的格式不符合要求,cookiejar格式如下:

    <_MozillaCookieJar.MozillaCookieJar[<Cookie .ASPXANONYMOUS=vLho9JXszwEkAAAAODc4NTcwYTQtYWFiNy00Mzg5LThkNzEtNjYxYjcxNmM3Nzdk-H-RHMNhr0fVc7UTHulz8qUfBOU1 for zhuzhou2013.feixuelixm.teacher.com.cn/>, <Cookie ASP.NET_SessionId=reg0ik55hffagzjabwo3xu45 for zhuzhou2013.feixuelixm.teacher.com.cn/>, <Cookie feixuelixmweb=r-feixuelixm_7.86 for zhuzhou2013.feixuelixm.teacher.com.cn/>]>
    

    而post request head中 “Cookie” 的值则为下面的格式:

    cookies: .ASPXANONYMOUS=vLho9JXszwEkAAAAODc4NTcwYTQtYWFiNy00Mzg5LThkNzEtNjYxYjcxNmM3Nzdk-H-RHMNhr0fVc7UTHulz8qUfBOU1;ASP.NET_SessionId=reg0ik55hffagzjabwo3xu45;feixuelixmweb=r-feixuelixm_7.86
    

    所以要使用下面的代码处理:

    #打印未处理的过的cookies
    
    print cookiejar
    
    cookies = ''
    
    #打印每个cookies的项目和值,整理格式
    
    for index, cookie in enumerate(cookiejar):
    
    print '[',index, ']';
    
    print cookie.name;
    
    print cookie.value;
    
    print "###########################"
    
    cookies = cookies+cookie.name+"="+cookie.value+";";
    
    print "###########################"
    
    cookie = cookies[:-1]
    
    #打印整理的好的cookie
    
    print "cookies:",cookie
    

    所以头部请求是:

    headers = {
    
        'Accept' :'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    
        'Accept-Language': 'zh-cn,en-us;q=0.8,zh;q=0.5,en;q=0.3',
    
        'Accept-Encoding': 'gzip, deflate',
    
        'Host':    'zhuzhou2013.feixuelixm.teacher.com.cn',
    
        'Cookie':cookies,
    
        'User-Agent' : 'Mozilla/5.0 (Windows NT 5.1; rv:29.0) Gecko/20100101 Firefox/29.0',
    
        'Referer' : 'http://zhuzhou2013.feixuelixm.teacher.com.cn/GuoPeiAdmin/Login/Login.aspx',
    
        #'Content-Type':'application/x-www-form-urlencoded',
    
        #'Content-Length' :474,
    
        'Connection' : 'Keep-Alive'
    
    }
    

    开始构造post请求头部,post的数据中要求有验证码,这里验证码使用第一篇中的方法来得到为text 。

    然后,post的数据中,有些东西的值是不会变的,比如:__EVENTTARGET ,_EVENTARGUMENT,__VIEWSTATE 。这些就要自己观察和对比后才知道。

    #用户名,密码
    
    username = "XXXXXXXXXXXXx"
    
    password = "YYYYfasdfas"
    
    postData = {
    
        '__EVENTTARGET':'',
    
        '__EVENTARGUMENT':'',
    
        '__VIEWSTATE': '/wEPDwUKLTcyMzEyMTY2Nw8WAh4LTG9naW5lZFBhZ2UFEExvZ2luZWRQYWdlLmFzcHgWAmYPZBYCZg8PZBYGHgV0aXRsZQUg55So5oi35ZCNL+WtpuS5oOeggS/ouqvku73or4Hlj7ceB29uZm9jdXMFEGNoZWNrSW5wdXQodGhpcykeBm9uYmx1cgUNcmVzdG9yZSh0aGlzKWQYAQUeX19Db250cm9sc1JlcXVpcmVQb3N0QmFja0tleV9fFgEFC0ltZ2J0bkxvZ2luckJjpNhrusWhtPuT33UJ1dBUkvw=',
    
        'txtUserName':username,
    
        'txtPassWord':password,
    
        'txtCode':text,
    
        'ImgbtnLogin.x':44 ,
    
        'ImgbtnLogin.y':14,
    
        'ClientScreenWidth':1180
    
    }
    

    3.发送post请求

    然后,将构造好的post的数据及头部整合成一个post request请求,然后发送请求:

    #合成post数据
    
    data = urllib.urlencode(postData)
    
    print "data:###############"
    
    print  data
    
    #创建request
    
    #构造request请求
    
    request = urllib2.Request(  PostUrl,data,headers  )
    
    try:
    
        #访问页面
    
        response = urllib2.urlopen(request)
    
        #cur_url =  response.geturl()
    
        #print "cur_url:",cur_url
    
        status = response.getcode()
    
        print status
    
    except  urllib2.HTTPError, e:
    
         print e.code
    
    #将响应的网页打印到文件中,方便自己排查错误
    
    #必须对网页进行解码处理
    
    f= response.read().decode("utf8")
    
    outfile =open("rel_ip.txt","w")
    
    print >> outfile , "%s"   % ( f)
    
    #打印响应的信息
    
    info = response.info()
    
    print info
    

    4.检验是否登录成功

    正如刚才你说的,因为返回网页(http://zhuzhou2013.feixuelixm.teacher.com.cn/IndexPage/Index.aspx)有iframe ,所以我们不能抓取到我们要的字符串(返回的网页html代码在rel_ip.txt 文件中,我们是看不到iframe中的内容的)

    我们可以直接去一个没有iframe的,又包含可以验证我们登陆成功的字符串的网页,也就是iframe源网站 :

    #测试登陆是否成功,因为在testurl只有登陆后才能访问
    
    ​#还有一个原因是因为post返回得到的网页中含有iframe ,而要搜索的信息刚好在iframe ,所以要去iframe的原来的地址验证
    
    testurl = "http://zhuzhou2013.feixuelixm.teacher.com.cn/GuoPeiAdmin/Login/LoginedPage.aspx"
    
    try:
    
        response = urllib2.urlopen(testurl)
    
    except  urllib2.HTTPError, e:
    
         print e.code
    
    #因为后面要从网页查找字符来验证登陆成功与否,所以要保证查找的字符与网页编码相同,否则无非得到正确的结论。建议用英文查找,如css中的 id, name 之类的。
    
    f= response.read().decode("utf8").encode("utf8")
    
    outfile =open("out_ip.txt","w")
    
    print >> outfile , "%s"   % ( f)
    
    #在返回的网页中,查找“你好” 两个字符,因为只有登陆成功后才有两个字,找到了即表示登陆成功。建议用英文
    
    tag = '你好'.encode("utf8")
    
    if  re.search( tag,f):
    
        #登陆成功
    
        print 'Logged in successfully!'
    
    else:
    
        #登陆失败
    
        print 'Logged in failed, check result.html file for details'
    
    response.close()
    
    #这个代码很随意,但是容易看,需要的活,可以写成函数。还有就是urlopen()在大量登陆及检验过程中,可能read(0因为网络阻塞而timeout(超时) ,需要设置urlopen() 的超时时间,或者多次发送请求
    

    #######################################################

    教程玩!!

    麻痹的,最烦python的缩进作为语法的设定,好蛋疼,无论是粘贴复制代码,还是在编辑器中写代码,是不是就被缩进没对齐。而且粘贴代码是,一旦缩进打乱了,这代码算还要自己去重新整理。写的时候也不爽,还是没有C那样随便。

    转载请注明:爱开源 » python模拟登陆登陆二:获取和处理发送post request和head数据

  • NGINX 配置404错误页面转向

    什么是404页面

    如果碰巧网站出了问题,或者用户试图访问一个并不存在的页面时,此时服务器会返回代码为404的错误信息,此时对应页面就是404页面。404页面的默认内容和具体的服务器有关。如果后台用的是NGINX服务器,那么404页面的内容则为:404 Not Found

    为什么要自定义404页面

    在访问时遇到上面这样的404错误页面,我想99%(未经调查,估计数据)的用户会把页面关掉,用户就这样悄悄的流失了。如果此时能有一个漂亮的页面能够引导用户去他想去的地方必然可以留住用户。因此,每一个网站都应该自定义自己的404页面。

    NGINX下如何自定义404页面

    IIS和APACHE下自定义404页面的经验介绍文章已经非常多了,NGINX的目前还比较少,为了解决自家的问题特地对此作了深入的研究。研究结果表明,NGINX下配置自定义的404页面是可行的,而且很简单,只需如下几步:

    1.创建自己的404.html页面

    2.更改nginx.conf在http定义区域加入: fastcgi_intercept_errors on;

    3.更改nginx.conf(或单独网站配置文件,例如在nginx -> sites-enabled下的站点配置文件 )

    中在server 区域加入: error_page 404 /404.html 或者 error_page 404 =http://www.xxx.com/404.html

    4.更改后重启nginx,,测试nginx.conf正确性: /opt/nginx/sbin/nginx –t

    #502 等错误可以用同样的方法来配置。

    error_page 500 502 503 504 /50x.html;

    注意事项:

    1.必须要添加:fastcgi_intercept_errors on; 如果这个选项没有设置,即使创建了404.html和配置了error_page也没有效果。fastcgi_intercept_errors 语法: fastcgi_intercept_errors on|off 默认: fastcgi_intercept_errors off 添加位置: http, server, location 默认情况下,nginx不支持自定义404错误页面,只有这个指令被设置为on,nginx才支持将404错误重定向。这里需要注意的是,并不是说设置了fastcgi_intercept_errors on,nginx就会将404错误重定向。在nginx中404错误重定向生效的前提是设置了fastcgi_intercept_errors on,并且正确的设置了error_page这个选项(包括语法和对应的404页面)

    2.不要出于省事或者提高首页权重的目的将首页指定为404错误页面,也不要用其它方法跳转到首页。

    3.自定义的404页面必须大于512字节,否则可能会出现IE默认的404页面。例如,假设自定义了404.html,大小只有11个字节(内容为:404错误)。

    转载请注明:爱开源 » NGINX 配置404错误页面转向

  • DNS攻击原理与防范

    随着网络的逐步普及,网络安全已成为INTERNET路上事实上的焦点,它关系着INTERNET的进一步发展和普及,甚至关系着INTERNET的生存。可喜的是我们那些互联网专家们并没有令广大INTERNET用户失望,网络安全技术也不断出现,使广大网民和企业有了更多的放心,下面就网络安全中的主要技术作一简介,希望能为网民和企业在网络安全方面提供一个网络安全方案参考。

    DNS的工作原理

    DNS分为Client和Server,Client扮演发问的角色,也就是问Server一个Domain Name,而Server必须要回答此Domain Name的真正IP地址。而当地的DNS先会查自己的资料库。如果自己的资料库没有,则会往该DNS上所设的的DNS询问,依此得到答案之后,将收到的答案存起来,并回答客户。

    DNS服务器会根据不同的授权区(Zone),记录所属该网域下的各名称资料,这个资料包括网域下的次网域名称及主机名称。

    在每一个名称服务器中都有一个快取缓存区(Cache),这个快取缓存区的主要目的是将该名称服务器所查询出来的名称及相对的IP地址记录在快取缓存区中,这样当下一次还有另外一个客户端到次服务器上去查询相同的名称 时,服务器就不用在到别台主机上去寻找,而直接可以从缓存区中找到该笔名称记录资料,传回给客户端,加速客户端对名称查询的速度。例如:

    当DNS客户端向指定的DNS服务器查询网际网路上的某一台主机名称 DNS服务器会在该资料库中找寻用户所指定的名称 如果没有,该服务器会先在自己的快取缓存区中查询有无该笔纪录,如果找到该笔名称记录后,会从DNS服务器直接将所对应到的IP地址传回给客户端 ,如果名称服务器在资料记录查不到且快取缓存区中也没有时,服务器首先会才会向别的名称服务器查询所要的名称。例如:

    DNS客户端向指定的DNS服务器查询网际网路上某台主机名称,当DNS服务器在该资料记录找不到用户所指定的名称时,会转向该服务器的快取缓存区找寻是否有该资料 ,当快取缓存区也找不到时,会向最接近的名称服务器去要求帮忙找寻该名称的IP地址 ,在另一台服务器上也有相同的动作的查询,当查询到后会回复原本要求查询的服务器,该DNS服务器在接收到另一台DNS服务器查询的结果后,先将所查询到的主机名称及对应IP地址记录到快取缓存区中 ,最后在将所查询到的结果回复给客户端

    常见的DNS攻击包括:

    1) 域名劫持

    通过采用黑客手段控制了域名管理密码和域名管理邮箱,然后将该域名的NS纪录指向到黑客可以控制的DNS服务器,然后通过在该DNS服务器上添加相应域名纪录,从而使网民访问该域名时,进入了黑客所指向的内容。

    这显然是DNS服务提供商的责任,用户束手无策。

    2) 缓存投毒

    利用控制DNS缓存服务器,把原本准备访问某网站的用户在不知不觉中带到黑客指向的其他网站上。其实现方式有多种,比如可以通过利用网民ISP端的DNS缓存服务器的漏洞进行攻击或控制,从而改变该ISP内的用户访问域名的响应结果;或者,黑客通过利用用户权威域名服务器上的漏洞,如当用户权威域名服务器同时可以被当作缓存服务器使用,黑客可以实现缓存投毒,将错误的域名纪录存入缓存中,从而使所有使用该缓存服务器的用户得到错误的DNS解析结果。

    最近发现的DNS重大缺陷,就是这种方式的。只所以说是“重大”缺陷,据报道是因为是协议自身的设计实现问题造成的,几乎所有的DNS软件都存在这样的问题。

    3)DDOS攻击

    一种攻击针对DNS服务器软件本身,通常利用BIND软件程序中的漏洞,导致DNS服务器崩溃或拒绝服务;另一种攻击的目标不是DNS服务器,而是利用DNS服务器作为中间的“攻击放大器”,去攻击其它互联网上的主机,导致被攻击主机拒绝服务。

    4) DNS欺骗

    DNS欺骗就是攻击者冒充域名服务器的一种欺骗行为。

    原理:如果可以冒充域名服务器,然后把查询的IP地址设为攻击者的IP地址,这样的话,用户上网就只能看到攻击者的主页,而不是用户想要取得的网站的主页了,这就是DNS欺骗的基本原理。DNS欺骗其实并不是真的“黑掉”了对方的网站,而是冒名顶替、招摇撞骗罢了。

    现在的Internet上存在的DNS服务器有绝大多数都是用bind来架设的,使用的bind版本主要为bind 4.9.5+P1以前版本和bind 8.2.2-P5以前版本.这些bind有个共同的特点,就是BIND会缓存(Cache)所有已经查询过的结果,这个问题就引起了下面的几个问题的存在.

    DNS欺骗

    在DNS的缓存还没有过期之前,如果在DNS的缓存中已经存在的记录,一旦有客户查询,DNS服务器将会直接返回缓存中的记录

    防止DNS被攻击的若干防范性措施

    互联网上的DNS放大攻击(DNS amplification attacks)急剧增长。这种攻击是一种数据包的大量变体能够产生针对一个目标的大量的虚假的通讯。这种虚假通讯的数量有多大?每秒钟达数GB,足以阻止任何人进入互联网。

    与老式的“smurf attacks”攻击非常相似,DNS放大攻击使用针对无辜的第三方的欺骗性的数据包来放大通讯量,其目的是耗尽受害者的全部带宽。但是,“smurf attacks”攻击是向一个网络广播地址发送数据包以达到放大通讯的目的。DNS放大攻击不包括广播地址。相反,这种攻击向互联网上的一系列无辜的第三方DNS服务器发送小的和欺骗性的询问信息。这些DNS服务器随后将向表面上是提出查询的那台服务器发回大量的回复,导致通讯量的放大并且最终把攻击目标淹没。因为DNS是以无状态的UDP数据包为基础的,采取这种欺骗方式是司空见惯的。

    这种攻击主要依靠对DNS实施60个字节左右的查询,回复最多可达512个字节,从而使通讯量放大8.5倍。这对于攻击者来说是不错的,但是,仍没有达到攻击者希望得到了淹没的水平。最近,攻击者采用了一些更新的技术把目前的DNS放大攻击提高了好几倍。

    当前许多DNS服务器支持EDNS。EDNS是DNS的一套扩大机制,RFC 2671对次有介绍。一些选择能够让DNS回复超过512字节并且仍然使用UDP,如果要求者指出它能够处理这样大的DNS查询的话。攻击者已经利用这种方法产生了大量的通讯。通过发送一个60个字节的查询来获取一个大约4000个字节的记录,攻击者能够把通讯量放大66倍。一些这种性质的攻击已经产生了每秒钟许多GB的通讯量,对于某些目标的攻击甚至超过了每秒钟10GB的通讯量。

    要实现这种攻击,攻击者首先要找到几台代表互联网上的某个人实施循环查询工作的第三方DNS服务器(大多数DNS服务器都有这种设置)。由于支持循环查询,攻击者可以向一台DNS服务器发送一个查询,这台DNS服务器随后把这个查询(以循环的方式)发送给攻击者选择的一台DNS服务器。接下来,攻击者向这些服务器发送一个DNS记录查询,这个记录是攻击者在自己的DNS服务器上控制的。由于这些服务器被设置为循环查询,这些第三方服务器就向攻击者发回这些请求。攻击者在DNS服务器上存储了一个4000个字节的文本用于进行这种DNS放大攻击。

    现在,由于攻击者已经向第三方DNS服务器的缓存中加入了大量的记录,攻击者接下来向这些服务器发送DNS查询信息(带有启用大量回复的EDNS选项),并采取欺骗手段让那些DNS服务器认为这个查询信息是从攻击者希望攻击的那个IP地址发出来的。这些第三方DNS服务器于是就用这个4000个字节的文本记录进行回复,用大量的UDP数据包淹没受害者。攻击者向第三方DNS服务器发出数百万小的和欺骗性的查询信息,这些DNS服务器将用大量的DNS回复数据包淹没那个受害者。

    如何防御这种大规模攻击呢?首先,保证你拥有足够的带宽承受小规模的洪水般的攻击。一个单一的T1线路对于重要的互联网连接是不够的,因为任何恶意的脚本少年都可以消耗掉你的带宽。如果你的连接不是执行重要任务的,一条T1线路就够了。否则,你就需要更多的带宽以便承受小规模的洪水般的攻击。不过,几乎任何人都无法承受每秒钟数GB的DNS放大攻击。

    因此,你要保证手边有能够与你的ISP随时取得联系的应急电话号码。这样,一旦发生这种攻击,你可以马上与ISP联系,让他们在上游过滤掉这种攻击。要识别这种攻击,你要查看包含DNS回复的大量通讯(源UDP端口53),特别是要查看那些拥有大量DNS记录的端口。一些ISP已经在其整个网络上部署了传感器以便检测各种类型的早期大量通讯。这样,你的ISP很可能在你发现这种攻击之前就发现和避免了这种攻击。你要问一下你的ISP是否拥有这个能力。

    最后,为了帮助阻止恶意人员使用你的DNS服务器作为一个实施这种DNS放大攻击的代理,你要保证你的可以从外部访问的DNS服务器仅为你自己的网络执行循环查询,不为任何互联网上的地址进行这种查询。大多数主要DNS服务器拥有限制循环查询的能力,因此,它们仅接受某些网络的查询,比如你自己的网络。通过阻止利用循环查询装载大型有害的DNS记录,你就可以防止你的DNS服务器成为这个问题的一部分。

    结束语:网络攻击越来越猖獗,对网络安全造成了很大的威胁。对于任何黑客的恶意攻击,都有办法来防御,只要了解了他们的攻击手段,具有丰富的网络知识,就可以抵御黑客们的疯狂攻击。一些初学网络的朋友也不必担心,因为目前市场上也已推出许多网络安全方案,以及各式防火墙,相信在不久的将来,网络一定会是一个安全的信息传输媒体。特别需要强调的是,在任何时候都应将网络安全教育放在整个安全体系的首位,努力提高所有网络用户的安全意识和基本防范技术。这对提高整个网络的安全性有着十分重要的意义。

    转载请注明:爱开源 » DNS攻击原理与防范