国内创业公司常用的服务

基础服务

  • Domain

    • Namecheap
    • NameSilo
    • Name.com
    • 别去 Godaddy
  • DNS

    • DNSPod
  • VPS

    • Linode
    • UCloud
    • QingCloud
    • 别用 Paas
  • CDN

    • 七牛

协作服务

  • Source Code Hosting

    • Stash
    • Github Organization
    • Github Enterprice
    • Bitbucket
  • Communication

    • Slack
    • HipChat
    • Google Hangout
    • Skype
    • QQ
  • Docs

    • Confluence
    • Google Docs
    • Redmine
    • Github wiki
    • MediaWiki
    • DokuWiki
  • Files

    • Dropbox
    • LayerVault (For Designer)
  • Enterprise Email

    • Google Apps
    • 腾讯企业邮箱
    • 网易企业邮箱
  • Task Management

    • Trello
    • Redmine
    • Asana
    • Basecamp
    • Fengche.co
    • Wunderlist
    • tower.im
    • Worktile
    • Teambition
  • Issue Tracking

    • Trello
    • JIRA
    • Redmine
    • GitHub
    • Lighthouse
  • CI

    • Bamboo
    • Jenkins
    • Travis CI
  • Beta Testing

    • HockeyApp
    • TestFlight
    • FIR.im

开发者服务

  • Monitor

    • Server
      • NewRelic
      • 监控宝
    • Server App
      • NewRelic
      • 监控宝
    • Website
      • Pingdom
      • DNSPod
  • Performance Tracking

    • Server App
      • NewRelic
    • App
      • 听云
      • OneAPM
  • Error Tracking

    • Server
      • Exceptional
      • Sentry
      • AirBrake
      • NewRelic
    • Mobile
      • HockeyApp
      • Flury
      • Crashlytics
      • 友盟
      • TalkingData
    • Frontend
      • TraceKit
      • Sentry
  • Analytics

    • Mobile
      • Google Analytics
      • Mixpanel
      • 友盟
    • Web
      • Google Analytics
  • Email

    • Mailgun
    • Mandrill
    • SendGrid
    • AmazonSES
    • SendCloud
  • SMS

    • 自建
  • Push Service

    • 个推
    • LeanCloud
    • JPush
  • Comment

    • Disqus
    • 多说
  • Express

    • 快递100
  • Realtime Communication

    • 环信
    • LeanCloud
  • Feedback

    • Olark
    • UserVoice
  • Support Ticket System

    • Zendesk
    • SupportFu
    • Udesk
    • 待客

转自http://miao.hu/

从如何获取可信赖的IP地址聊起

作者: joyqi 时间: October 11, 2014 分类: 编程开发,PHP
起因

写这篇文章缘起SF的一个问题 http://segmentfault.com/q/1010000000686700/a-1020000000687155。由此我想到了很多,就和大家随便聊聊吧
在PHP中获取ip地址有一段网上流传甚广的代码,还有它的各种变种

function real_ip()
{
    static $realip = NULL;

    if ($realip !== NULL)
    {
        return $realip;
    }

    if (isset($_SERVER))
    {
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
        {
            $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);

            /* 取X-Forwarded-For中第一个非unknown的有效IP字符串 */
            foreach ($arr AS $ip)
            {
                $ip = trim($ip);

                if ($ip != 'unknown')
                {
                    $realip = $ip;

                    break;
                }
            }
        }
        elseif (isset($_SERVER['HTTP_CLIENT_IP']))
        {
            $realip = $_SERVER['HTTP_CLIENT_IP'];
        }
        else
        {
            if (isset($_SERVER['REMOTE_ADDR']))
            {
                $realip = $_SERVER['REMOTE_ADDR'];
            }
            else
            {
                $realip = '0.0.0.0';
            }
        }
    }
    else
    {
        if (getenv('HTTP_X_FORWARDED_FOR'))
        {
            $realip = getenv('HTTP_X_FORWARDED_FOR');
        }
        elseif (getenv('HTTP_CLIENT_IP'))
        {
            $realip = getenv('HTTP_CLIENT_IP');
        }
        else
        {
            $realip = getenv('REMOTE_ADDR');
        }
    }

    preg_match("/[\d\.]{7,15}/", $realip, $onlineip);
    $realip = !empty($onlineip[0]) ? $onlineip[0] : '0.0.0.0';

    return $realip;
}

这段代码的原理是从HTTP_X_FORWARDED_FOR以及HTTP_CLIENT_IP还有REMOTE_ADDR中获取ip地址。

这段代码有问题吗?如果从功能角度来看,它是没有问题的,因为它保证了你可以尽可能地获取到一个ip地址,如果你地程序不需要验证这个ip地址是否正确,只是为了显示它的来源,那么你可以这样做没问题。

但是很多情况下我们需要确切地知道当前跟服务器连接的是哪个ip,那这段代码就不妥了。

在PHP的$_SERVER变量中,凡是以HTTP_开头的键值都是直接从客户端的HTTP请求头中直接解析出来的。

这句话啥意思,意思就是客户端告诉你ip是多少就是多少,伪造它的成本可以说非常低。除了这两个以HTTP_开头的地址外,还有一个REMOTE_ADDR地址,这个地址是与你的服务器进行实际tcp连接的地址,它是一个可信赖的地址。要伪造这个地址成本很高,这是由于TCP三次握手协议的原理决定的,除非你能骗过服务器端的路由,那这样的成本就太高了,还不如直接黑进你的服务器

衍生

上图是一个经过简化的TCP握手协议,握手就是指在你正式传输信息之前,客户端和服务端确认双方是否可信的过程,这三步可以通俗化成下面的过程

小C (Client) 向小S (Server) 发了一封信(SYN),告诉它我要寄一个包裹 (数据) 给你,但是为了保证这个包裹你能收到,我先发一封信确认下你的地址 (Server IP) 是不是正确的,怎么确认呢?收到这封信后请按信上的地址 (Client IP) 回信就行了。
小S收到了这封信以后一想,我不仅要回信确认我的地址是存在的(ACK),还要问问你的地址是不是正确的,要不然我咋知道你是不是靠谱的人。于是小S给小C发了一封回信,不仅在信里确认自己的地址是正确的(ACK),也要求(SYN)小C在收到信件后给自己发一封回信,以确认他的地址也是真实的。要不然我会拒绝你的连接
小C在收到小S的确认后,马上发了一封回信(ACK)告诉我已经收到信了
从此它们就可以愉快的通信了。

那么问题来了

如果小C没有收到小S的确认信,但他也发了一封回信骗小S说自己已经收到了,怎么破?

这个问题实际上在TCP协议中已经解决了,每个SYN请求都会包含一个随机数字,发送回信ACK的时候,必须一并将这个数字加一再发回去。这样我们收到ACK的时候只要确认这个值是否正确就行了。

如果邮政局被收买了怎么?

这确实是一个风险,这就是我刚才提到的,路由器被黑掉的可能。这也就是很多人为什么说互联网的基础是非常脆弱的,只要公网上的其中一个路由器说了假话,所有经过这个路由器的数据都会变成不可信的。

但同时这个成本非常高,因为路由器的安全级别通常是很高的,而且一般的路由器管理端口也不会向普通访问者开放。所以能做到这一点的黑客,通常会选择直接去黑你的服务器。

这就好比我为了寄点东西噁心你一下,还要去贿赂一整个邮政局,显然不划算。还不如我直接买张火车票,赶到你的住处,把你门撬了,放你桌上。

再发散下

SYN FLOOD攻击

也就是SYN洪水攻击,看了上面的解释你应该大概知道SYN是啥意思了,就是一个询问包。所谓SYN洪水攻击就是指Client发送第一个SYN包的时候,告诉Server的源地址是一个假的,当Server发送回执时会进入到一个half-open(半开)状态。

为啥叫半开,因为这个端口只开了一半,握手过程已经开始,但还有一半没完成。这个半开连接大家应该都听过,早先玩迅雷下载或者bt的时候都需要打一个windows半开连接数补丁,就是因为桌面操作系统的计算资源是优先倾向于GUI的,当系统处于半开连接时因为处于等待状态,是会消耗内存和计算资源的,所以操作系统会默认把这个值调低,限制你能接受的连接。而我们p2p下载又需要比较高的连接量,所以就有了这个补丁,把它的限制打开。

我花这段篇幅解释半开连接数并不是没事找事,这其实就是SYN FLOOD攻击的原理。因为当系统处于半开状态时要消耗资源,而服务器往往半开连接数限制都比较大(或者干脆没限制),因此接第一段的话,因为服务器得到的源地址是个假的,发送回执后肯定会收不到确认,因此就会进入到漫长的等待过程(相对于响应时间)。攻击者通过伪造大量的这种无效请求,使服务器端等待大量连接,从而耗尽服务器资源,以达到使其瘫痪的目的。

想看看你是不是受到了此种攻击,只需要在netstat时看看是不是有大量的SYN_RCVD连接即可。SYN_RCVD表示服务器处于握手的第二步。

随机数的漏洞

我先将刚刚讲过的一个问题引用下

如果小C没有收到小S的确认信,但他也发了一封回信骗小S说自己已经收到了,怎么破?
这个问题实际上在TCP协议中已经解决了,每个SYN请求都会包含一个随机数字,发送回信ACK的时候,必须一并将这个数字加一再发回去。
这段话段的关键在于“随机数”这三个字,因为我们这个验证过程的一大基础是Server生成的随机数是不可能被Client端提前知道的,这就好比打扑克时你不可能知道其他人的牌是什么,一个道理。

但如果这个Client是个赌神呢?

http://www.securityfocus.com/bid/25348/discuss
根据安全机构的研究,Linux内核中包含了一个可导致Dos和权限提升的漏洞,可以被黑客利用来运行攻击代码。这是一个核心内存的堆栈溢出问题,可以导致系统的崩溃,再特定的环境下,还可以提升权限。这个漏洞影响到了Linux内核2.6.22.3前的版本。
这是一个被爆出来的随机数漏洞,如果我知道了服务端的随机数漏洞,那么完全可以利用这一点来伪造出一个完美的假ip。

最后弱弱地总结下

那我们到底该相信哪个ip呢?

首选REMOTE_ADDR,因为虽然有如此多地伪造方法,但在语言层面你只能选一个最可靠地。
如果你地服务隐藏在负载均衡或者缓存系统后面,它通常会给你发一个Client-Ip或者X-Forwarded-For的HTTP头,告诉你跟它连接的客户端ip,这时的HTTP_CLIENT_IP和HTTP_X_FORWARDED_FOR是可信的,因为它是从前端服务器上的直接传递过来的,当然你必须在程序中指定只信任这一个来源,而不要像最开始的代码那样每个都检测一遍

修改了iptables规则

虽然没出什么大问题,但是为了安全起见还是采用了更加严格的iptables规则。
#lo
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
#Established
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
#http/https
-A INPUT -p tcp -m multiport --dports 80,443 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp -m multiport --dports 80,443 -m state --state NEW,ESTABLISHED -j ACCEPT
#ssh
-A INPUT -p tcp --dport [sshport] -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport [sshport] -m state --state ESTABLISHED -j ACCEPT
#dns
-A OUTPUT -p udp --dport 53 -j ACCEPT
#ss
-A INPUT -p tcp --dport [ssport] -j ACCEPT
#manage
-A INPUT -p tcp --dport [manageport] -j ACCEPT
-A OUTPUT -p tcp --dport [manageport] -j ACCEPT
#ping
-A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
-A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
#anti-ddos
-A INPUT -p tcp --dport 80 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT
#log
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
-A OUTPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
#default
-A INPUT -j DROP
-A OUTPUT -j DROP
-A FORWARD -j DROP

Windows下的Go编译

继续折腾多用户SS
由于客户端需要在win下使用,于是要在win下编译go语言的local客户端
去golang.org下载了官方的安装包,安装完成后配置了系统变量
GOROOT为安装目录
PATH变量加入GOROOT\bin
GOPATH为某个编译安装目录
然后进入使用go version查看版本

确定没问题了使用go build
编译local.go文件为local.exe
中间遇到了很多依赖包的问题,一一下载他们放到相应的目录里。
终于编译成功
然后配合shadowsocks-QT5版本做后端使用,速度棒棒的。