Csrf是什么

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

CSRF可以做什么

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账。造成的问题包括:个人隐私泄露以及财产安全。

CSRF的原理理解CSRF漏洞这篇文章就足够了

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  • 1.登录受信任网站A,并在本地生成Cookie。
  • 2.在不退出出A的情况下,访问危险网站B。看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会                  受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

1)你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

2)你不能保证你关闭浏览器了后,你 本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的 认为关闭浏览器就等于退出登录/结束会话了)

3)上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被 人 访问的网站。上面大概地讲了一下CSRF攻击的思想,下面我将举个例子详细说说具体的CSRF攻击,这里我以一个银行转账的操作 作为例子(仅仅是例子,真实的银行网站没这么傻:>)


案例场景

有这样一个转账业务场景,用户登录成功后服务会返回一个转账页面给用户进行转业务,那么此时如果黑客攻击者已经拿到了转账的action,并且我们正常用户的网站服务器存在csrf漏洞,黑客就可以利用转账的action诱导用户把用户的钱转给自己(攻击者),实现csrf攻击。

正常服务器返回给正常用户的转账页面为:do_trans_from_usr/,源码如下:

<form action="/do_trans_from_usr/" method="post">
    {% csrf_token %}
{#    this from normal server to serve user}
    <input type="text" name="key" value="asdfasdfasdfasdf" style="display: none">
    <p>
        转出:
        <input type="text" name="from">
    </p>
    <p>
        转入:
        <input type="text" name="into">
    </p>
    <p>
        转账金额:
        <input type="text" name="money">
    </p>

    <p>
        <input type="submit" value="确认转账">
    </p>

</form>

攻击者返回的转账页面为:do_trans_from_hacker,页面代码如下:

{#http://127.0.0.1:8000/login/由钓鱼方提供成为用户正常的网站#}

<form action="http://127.0.0.1:8000/do_trans_from_usr/" method="post">
{#    this from hacker server to induce user}#}
    <input type="text" name="key" value="asdfasdfasdfasdf" style="display: none">
    <p>
        转出:
        <input type="text" name="from">
    </p>
    <p>
        转入:
        <input type="text" name="">
{#        modified by hacker#}
        <input type="text" name="into" style="display: none" value="钓鱼者" >
    </p>
    <p>
        转账金额:
        <input type="text" name="money">
    </p>

    <p>
        <input type="submit" value="确认转账">
    </p>

</form>

那么这两个网页有什么区别呢,客户端解释后呈现给用户的界面都是一样的,没什么区别,用户就会无法辨别,那么此时我们的服务端实现转账业务,分别实现如下:

# this is from hacker
def do_trans_from_hacker(request):
    if request.method == "POST":
        __from = request.POST.get("from")
        __into = request.POST.get("into")
        __money = request.POST.get("money")
        print("{}-->给-->{}转了{}元,成功!".format(__from,__into,__money))
        return HttpResponse("钓鱼网站防伪标识: 转账成功!")
    return render(request,"do_trans_from_hacker.html")


# this if for normal user
def do_trans_from_usr(request):
    if request.method == "POST":
        __from = request.POST.get("from")
        __into = request.POST.get("into")
        __money = request.POST.get("money")

        print("{}-->给-->{}转了{}元,成功!".format(__from,__into,__money))
        return HttpResponse("正规网站防伪标识: 转账成功!")
    return render(request,"do_trans_from_usr.html")
实际上我们从action可以看出这个转账业务调的是用户服务器的正常接口,根本不会调do_trans_from_hacker接口。

我们起两个web服务来运行网站,表示一个是正常的网站,一个是钓鱼网站:

Starting development server at http://127.0.0.1:8000/

Starting development server at http://127.0.0.1:8090/

那么现在假如用户已经登录,用户访问第一个网站进行转账,那么提示的是转账成功,可以查看后台接口日志也知道

理解CSRF漏洞这篇文章就足够了

 

理解CSRF漏洞这篇文章就足够了

 

接口日志

 

理解CSRF漏洞这篇文章就足够了

 

此时此刻,该用户继续在登录的情况下访问第二个黑客提供的网站继续进行转账(假设我们用户不专业,没有看出破绽),那么就会把钱转到黑客的账户中

理解CSRF漏洞这篇文章就足够了

 

 

理解CSRF漏洞这篇文章就足够了

 

这里就转的不是user4了,而是钓鱼者了。

如何防御

这个地方明显网站域名都不一样了,如果是平时上网留点心就可以区分,这不是正规银行提供的网站域名,此时就需要警惕,需要确认后再转账。

另外,不能随便点击来历不明的链接,右键等,因为一个onclick不知道里面发生了什么,也不知道你自己给别人暴露了什么。

这里我们的服务首先本身就漏洞,他没有能力辨别该用户在第二次提交的请求是否合法,这里我们提供一种方案就是增加token,在隐藏的input域中增加token,增加一个token检测的中间件;每当用户刷新页面都会重新生成一个随机的token,每当我用户提交请求我都要检测携带过来的token是否和我提供给用的token一致,如果不一致我就禁止请求。

我们增加了token检测后继续再重复上面的两个动作。

用户正常转账

理解CSRF漏洞这篇文章就足够了

 

理解CSRF漏洞这篇文章就足够了

 

此时假设用户上钩了,访问的转账页面是钓鱼者提供的,此时还能转账成功吗?

 

理解CSRF漏洞这篇文章就足够了

为什么失败了呢,我们来查看下正网页是携带了token的,而我们的钓鱼者是不知道这个token的所有就不能实现跨站请求伪造了,呵呵

相关文章: