zoukankan      html  css  js  c++  java
  • C# 如何防止重放攻击(转载)

    转载地址:http://www.cnblogs.com/similar/p/6776921.html

    重放攻击

          重放攻击是指黑客通过抓包的方式,得到客户端的请求数据及请求连接,重复的向服务器发送请求的行为。 比如你有一个 “购买” 的操作,当你点击购买按钮时,向服务器发送购买的请求。而这时黑客对你的请求进行了抓包,得到了你的传输数据。 因为你填写的都是真实有效的数据,是可以购买成功的,因此他不用做任何改变,直接把你的数据再往服务器提交一次就行了。这就导致了,你可能只想购买一个产品的,结果黑客重放攻击,你就购买了多次。如果是用户操作的话,肯定就会莫名奇妙:怎么购买了那么多同样的产品,我只买了一个啊? 所以,重放攻击的危害还是挺大的,特别是涉及到金钱交易时,因此防重放攻击在电商项目中是必不可少的。

    解决方案

    时间戳(tamp) + 数字签名(sign)。 也就是说每次发送请求时多传两个参数,分别为 tamp 和 sign。比如

    原先请求为 http://127.0.0.1/api/buyproduct

    修改之后为 http://127.0.0.1/api/buyproduct?tamp=1403149835&sign=945bf36r046bd84df2985ad625c9f92415eccd1w

          数字签名的作用是为了确保请求的有效性。因为签名是经过加密的,只有客户端和服务器知道加密方式及Key,所以第三方模拟不了。我们通过对sign的验证来判断请求的有效性,如果sign验证失败则判定为无效的请求,反之有效。 但是数字签名并不能阻止重放攻击,因为黑客可以抓取你的tamp和sign(不需做任何修改),然后发送请求。这个时候就要对时间戳进行验证。

          时间戳的作用是为了确保请求的时效性。我们将上一次请求的时间戳进行存储,在下一次请求时,将两次时间戳进行比对。如果此次请求的时间戳和上次的相同或小于上一次的时间戳,则判定此请求为过时请求,无效。因为正常情况下,第二次请求的时间肯定是比上一次的时间大的,不可能相等或小于。

          有人会问,我直接用时间戳不就行了,为什么还要数字签名?因为黑客可能对请求进行抓包,然后修改时间戳为有效的时间戳值。我们的数字签名采用 tamp+key 进行组合加密,即使黑客修改了 tamp ,但是由于黑客不知道key,所以 sign 验证这步就成功的阻止了黑客的请求。

    实例代码

    加密方式采用 SHA1,SHA1加密方法进行了封装,写成了string的扩展方法。

    复制代码
    /// <summary>
    /// 数字签名
    /// </summary>
    /// <param name="tamp">时间戳(由客户端传入)</param>
     /// <param name="key">Key</param>
    /// <returns></returns>
    private string Sign(string tamp, string key)
    {
          string txt = tamp + "|" + key; //在每个参数中间加了个 "|" ,增加复杂度
          string sign = txt.GetSha1();
          return sign;
    }
    复制代码

    验证方法, 客户端的加密方式和服务端是一样的,如果两者的加密结果不一致,则验证失败。 如果客户端是js,一定要对js做代码混淆,禁止右键等。因为我是用Session存储上一次请求的时间戳的,而Session是会过时的,当Session过时时黑客再进行攻击,就会得手,所以限制请求有效期为30秒。

    复制代码
    /// <summary>
    /// 检查请求是否有效,防重放
    /// </summary>
    /// <param name="tamp">时间戳(由客户端传入)</param>
    /// <param name="key">Key</param>
    /// <param name="sign">验签(由客户端传入)</param>
    /// <returns></returns>
    private bool CheckRequest(string tamp, string key, string sign)
    {
         //验签(比对客户端的加密结果和服务端的加密结果,如果不相等,则验签失败)
         if (sign.ToUpper() != Sign(tamp, key).ToUpper()) return false;
    
         //得到当前时间戳
         DateTime DateStart = new DateTime(1970, 1, 1, 8, 0, 0);
         int nowTamp = Convert.ToInt32((DateTime.Now - DateStart).TotalSeconds);
       if ((nowTamp - int.Parse(tamp)) > 30) return false; //因为Session可能过时,所以限定请求有效时间为30秒
    //得到上一次的时间戳 string prevTamp = Session["tamp"] as string;
    //判断是否为空,为空说明是第一次请求 if (!string.IsNullOrWhiteSpace(prevTamp)) { if (int.Parse(tamp) > int.Parse(prevTamp)) { Session["tamp"] = tamp; return true; } else { return false; } } else { Session["tamp"] = tamp; return true; } }
  • 相关阅读:
    Core Animation 文档翻译—附录C(KVC扩展)
    Core Animation 文档翻译—附录B(可动画的属性)
    Core Animation 文档翻译—附录A(Layer样貌相关属性动画)
    Core Animation 文档翻译 (第八篇)—提高动画的性能
    Core Animation 文档翻译 (第七篇)—改变Layer的默认动画
    Core Animation 文档翻译 (第六篇)—高级动画技巧
    Core Animation 文档翻译 (第五篇)—构建Layer的层次结构
    用Markdown快速排版一片文章
    Core Animation 文档翻译 (第四篇)—让Layer的content动画起来
    Core Animation 文档翻译(第三篇)—设置Layer对象
  • 原文地址:https://www.cnblogs.com/huangzelin/p/6814324.html
Copyright © 2011-2022 走看看