最近又被【现场破解共享单车系统】刷了一脸,不得不开始后怕:如何防止类似的情况发生?

想来想去,始终觉得将程序加密是最简单的做法。但是摩拜、ofo也有加密,为什么仍然被破解?那是因为请求在传输过程中被篡改了关键参数,从而导致服务器做出了错误的响应。

如何防止篡改?自然是使用加密,加密方式的不同会决定篡改的成本(开头故事里的哥们大概用了2天),目前大多数的加密方法是暴露其他参数,只加密一个token,然后服务端验证token是否有效。从我的角度来讲,这种加密方式虽然破解难度很高,但多半是用MD5、SHA1、hmacMd5或AES等方式进行加密,结果往往是不可逆的,并且固定内容加密——不管多少次结果总是相同的。

对于部分人来讲,只要你暴露出来的数据就可以做很多事情了,而且如果使用这种加密手段,那么服务端和客户端必须约定好加密手段和参与加密的字段(规则),这就相当于:我(服务端)和你(客户端)一起设计了钥匙,然后在需要的时候(发起请求)你就往指定地方(服务器)丢一个上了锁的箱子(加密后的数据),然后我制作钥匙打开锁。我也可以丢箱子,然后你用同样方式去打开。

大家可以打开的锁安全么?想想都不靠谱。

更好的是什么?我们设想一下:我(服务端)给你(客户端)制造锁的图纸,我有制造钥匙的图纸,在你需要的时候(发起请求)就做一把锁,然后把货物(原始数据)放在箱子里锁好(加密完成)丢在指定的地方(服务器),然后我制作出钥匙,开锁拿走货物(解密)。

现在感觉如何?就像情报机构,侦察员只负责按照规则传递密码,而总部按照另一套规则解读,这个过程中就算被监听到,第三方也无法解读——这种方式就是今天要说的RSA。

该怎么做呢?很简单:基于现在基本都是用http交互数据,相互传输数据用json再好不过,所以我会要求客户端将所有参数以json格式全部拼装起来,然后整串加密后传输到服务器,请求内容看上去是这样的:

完全不知道这是一串什么鬼(如果我不是服务器的话):实际上这是一次登陆请求,里面包含账户、密码、时间戳、执行方法等,在服务端解密后是这样的:

{"msg":{"method":"log_in","time":"2017-09-28 10:30:27","user_name":"test","user_pwd":"test"}}

这就完成了一次正确的数据提交。并且如果有人修改了密文都会导致服务端解密失败从而驳回请求。这种情况下,就算你破译了整套客户端,也仅仅能拿到一个公钥,而远在服务端的私钥还是好好的(除非你已经攻破了人家的服务器),从而阻止参数在传递过程中被篡改(下面上点干货)。

生成公私钥:

    /// <summary>
/// RSA 的密钥产生 产生私钥 和公钥
/// </summary>
/// <param name="priKeys">私钥</param>
/// <param name="pubKey">公钥</param>
public static void rsaKey(out string priKeys, out string pubKey)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
priKeys = rsa.ToXmlString(true);
pubKey = rsa.ToXmlString(false);
}

RSA加密:

    /// <summary>
/// RSA加密(不限长度)
/// </summary>
/// <param name="publicKey">公钥</param>
/// <param name="text">要加密的文本</param>
/// <param name="enc">编码方式</param>
/// <returns>密文</returns>
public static string rsaEncrypt(string pubKey, string text, Encoding enc)
{
using (var rsaProvider = new RSACryptoServiceProvider())
{
var inputBytes = enc.GetBytes(text);
rsaProvider.FromXmlString(pubKey);
int bufferSize = (rsaProvider.KeySize / ) - ;
var buffer = new byte[bufferSize];
using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream())
{
while (true)
{
int readSize = inputStream.Read(buffer, , bufferSize);
if (readSize <= )
{
break;
}
var temp = new byte[readSize];
Array.Copy(buffer, , temp, , readSize);
var encryptedBytes = rsaProvider.Encrypt(temp, false);
outputStream.Write(encryptedBytes, , encryptedBytes.Length);
}
return Convert.ToBase64String(outputStream.ToArray());
}
}
}

RSA解密:

    /// <summary>
/// rsa解密
/// </summary>
/// <param name="priKey">私钥</param>
/// <param name="encrypted">要解密的字符串</param>
/// <param name="enc">编码方式</param>
/// <returns>明文</returns>
public static string rsaDecrypt(string priKey, string encrypted, Encoding enc)
{
if (string.IsNullOrEmpty(encrypted))
{
return string.Empty;
} if (string.IsNullOrWhiteSpace(priKey))
{
throw new ArgumentException("Invalid Private Key");
} using (var rsaProvider = new RSACryptoServiceProvider())
{
var inputBytes = Convert.FromBase64String(encrypted);
rsaProvider.FromXmlString(priKey);
int bufferSize = rsaProvider.KeySize / ;
var buffer = new byte[bufferSize];
using (MemoryStream inputStream = new MemoryStream(inputBytes),
outputStream = new MemoryStream())
{
while (true)
{
int readSize = inputStream.Read(buffer, , bufferSize);
if (readSize <= )
{
break;
} var temp = new byte[readSize];
Array.Copy(buffer, , temp, , readSize);
var rawBytes = rsaProvider.Decrypt(temp, false);
outputStream.Write(rawBytes, , rawBytes.Length);
}
return enc.GetString(outputStream.ToArray());
}
}
}

因为本人对js也有一些兴趣,所以尝试着利用js与服务器交互,然鹅发现js需要java格式的公私钥,于是找到了转换公私钥的代码,亲测有效,在这里搬运下,利己利他:

    /// <summary>
/// RSA公钥格式转换(net转java)
/// </summary>
/// <param name="pubKey">net格式公钥</param>
/// <returns>java格式的公钥</returns>
public static string rsaPubKeyNet2Java(string pubKey)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(pubKey);
BigInteger m = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[].InnerText));
BigInteger p = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[].InnerText));
RsaKeyParameters pub = new RsaKeyParameters(false, m, p);
SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);
byte[] serializedPublicBytes = publicKeyInfo.ToAsn1Object().GetDerEncoded();
return Convert.ToBase64String(serializedPublicBytes);
} /// <summary>
/// RSA私钥格式转换(net转java)
/// </summary>
/// <param name="priKey">net格式私钥</param>
/// <returns>java格式的私钥</returns>
public static string rsaPriKeyNet2Java(string priKey)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(priKey);
BigInteger m = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[].InnerText));
BigInteger exp = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[].InnerText));
BigInteger d = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("D")[].InnerText));
BigInteger p = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("P")[].InnerText));
BigInteger q = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Q")[].InnerText));
BigInteger dp = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DP")[].InnerText));
BigInteger dq = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("DQ")[].InnerText));
BigInteger qinv = new BigInteger(, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("InverseQ")[].InnerText));
RsaPrivateCrtKeyParameters privateKeyParam = new RsaPrivateCrtKeyParameters(m, exp, d, p, q, dp, dq, qinv);
PrivateKeyInfo privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(privateKeyParam);
byte[] serializedPrivateBytes = privateKeyInfo.ToAsn1Object().GetEncoded();
return Convert.ToBase64String(serializedPrivateBytes);
}

进行转换需要用到一个三方类库,我的百度网盘里可以下载 http://pan.baidu.com/s/1b9Wy42,可以直接使用。

js的RSA加密也要用到一个库,在这里捎带贴一下:

    <script type="text/javascript" src="https://passport.cnblogs.com/scripts/jsencrypt.min.js"></script>
<script>
//实例化对象
var crypt = new JSEncrypt();
//设置私钥
crypt.setPrivateKey('你的RSA公钥'); $('#log_in').click(function () {
//拼装发送数据
var post_data = crypt.encrypt('{"msg":{"method":"log_in","time":"' + Tool.getNowTime() + '","user_name":"' + $('.name').val() + '","user_pwd":"' + $('.pwd').val() + '"}}');
//在这里发起你的请求
});
</script>

因为js可以被看到,所以你更需要RSA,因为全部被看到也无所谓了。希望对大家有帮助~

NET应用——你的数据安全有必要升级的更多相关文章

  1. 看完SQL Server 2014 Q/A答疑集锦:想不升级都难!

    看完SQL Server 2014 Q/A答疑集锦:想不升级都难! 转载自:http://mp.weixin.qq.com/s/5rZCgnMKmJqeC7hbe4CZ_g 本期嘉宾为微软技术中心技术 ...

  2. 升级为iOS9后,默认请求类型为https,如何使用http进行请求会报错(引用他人的)

    升级为iOS9后,默认请求类型为https,如何使用http进行请求会报错 The resource could not be loaded because the App Transport Sec ...

  3. IPv6 VS IPv4,谈谈升级 IPv6 的必要性

    11月26日,中办.国办印发了<推进互联网协议第六版(IPv6)规模部署行动计划>,提出国内要在 5~10 年的时间形成下一代互联网自主技术体系和产业生态,建成全球最大规模的 IPv6 商 ...

  4. 开启 IPv6 新时代,升级后的 IPv6 厉害在哪?

    IPv6,Internet Protocol Version 6,从字面翻译 “互联网协议第 6 版”,它是IETF设计的用于替代现行版本 IP 协议-IPv4 协议,被称作“下一代互联网协议”.早在 ...

  5. linux下mysql升级

    最近漏洞扫描,扫描出了数据库存在中高危漏洞,于是迫切需要进行数据库升级.上网查了各种资料,说法很多,也到自己虚拟机上试了好多方法,终于倒腾出来,做下小总结记录一下. 升级操作: 1.到mysql官网h ...

  6. redis实战笔记(4)-第4章 数据安全与性能保障

    本章主要内容 4.1 将数据持久化至硬盘 4.2 将数据复制至其他机器 4.3 处理系统故障 4.4 Redis事务 4.5 非事务型流水线( non-transactional pipeline) ...

  7. FinTech领域实践:乐维监控助力西南某上市城商行IT运维转型升级!

    FinTech领域实践:乐维监控助力西南某上市城商行IT运维转型升级! 项目背景 随着信息化的逐步深入,企业业务运营活动对IT的依赖程度越来越高,传统的局部.粗放.碎片化的IT运维管理模式已经无法满足 ...

  8. 详解Linux运维工程师高级篇(大数据安全方向).

    hadoop安全目录: kerberos(已发布) elasticsearch(已发布)http://blog.51cto.com/chenhao6/2113873 knox oozie ranger ...

  9. 今天升级Xcode 7.0 bata发现网络访问失败。

    今天升级Xcode 7.0 bata发现网络访问失败.输出错误信息 The resource could not be loaded because the App Transport Securit ...

随机推荐

  1. matlab特殊符号表

    Character Sequence Symbol Character Sequence Symbol Character Sequence Symbol \alpha α \upsilon υ \s ...

  2. nginx 报invalid pid number

    /opt/ibis/sbin/nginx -c /opt/ibis/conf/nginx.conf 这是make make install之后生成的文件夹和文件. -c c是configure的缩写 ...

  3. [zabbix]zabbix2.0apt源安装

    http://www.sysadminworld.com/2013/install-zabbix-2-on-ubuntu-12-04-precise/

  4. ACM1753大明A+B

    ACM1753_link 这道题利用了大数相加,但是需要将小数和整数部分分开,独立相加,然后组合成一个数: 数据的可能性: 1.两个整数相加,没有小数点 2.一个整数一个小数相加 3.两个小数相加 要 ...

  5. 02.树的序列化与反序列化(C++)

    1.二叉树的序列化 输入的一棵树: //二叉树的先序遍历-序列化 #include <iostream> #include <string> #include <sstr ...

  6. 【C++ STL】Stack

    1.定义 class stack<> 实作出一个stack(也成为LIFO,后进先出),你可以使用push()将任意数量的元素置入stack中,也可以使用pop()将元素依次插入次序反序从 ...

  7. js高阶函数--判断数据类型、函数胡柯里化;

    一.判断数据类型: 常见的判断有typeof.instanceof. constructor. prototype,先来看typeof: var a = "hello world" ...

  8. Druid连接池及监控在spring中的配置

    Druid连接池及监控在spring配置如下: <bean id="dataSource" class="com.alibaba.druid.pool.DruidD ...

  9. 用reduce实现简单的pipe

    function pipe(src, ...fns){ return fns.reduce(function(fn1, fn2){ return fn2(fn1) }, src); } undefin ...

  10. JS高级之面试必须知道的几个点

    1.函数的3种定义方法 1.1 函数声明 //ES5 function getSum(){} function (){}//匿名函数 //ES6 ()=>{}//如果{}内容只有一行{}和ret ...