ASP.NET MVC中使用JS实现不对称加密密码传输
来源:互联网 发布:mac os x10.11安装 编辑:程序博客网 时间:2024/06/05 05:55
摘要:ASP.NET MVC中登录页面中点击登录后,用户名、密码将被明文传输到Controller中,使用Fiddler等工具可以轻松截获并获取密码, 这是不安全的。 使用对称加密,如AES,密钥将被暴露前端代码,也是不安全的。使用不对称加密能够较好解决这个问题。本文以RSA不对称加密的形式,在JS端通过公钥对密码进行加密,将密文传输到后端后通过密钥进行解密。
关键字: 不对称加密;对称加密;RSA 算法;AES; 密钥;公钥
0 背景
登录是最常见的需求之一,在这个环节,安全问题不可避免,明文传输很容易被截获并暴露密码原文。如下图使用Fiddle中出现的情况。
为了避免这种情况,通常办法有1 使用HTTPS形式解决; 2 使用公钥和不对称加密对密文进行加密;3使用对称加密,比如AES。
这3种方案中,方案1是终极方案,但是需要克服证书获取和配置的问题, 本方案不是本文讨论重点,请有兴趣的自行查阅https://letsencrypt.org/。方案3, 以AES加密为例,必须把加密密钥存放在前端。 而前端对用户来说是开源的,很多开发者尝试把密钥藏的路径很深,但无疑这还是自欺欺人的。
方案2中,在JS端进行密码的RSA加密是有必要的,因为密码需要在用户点击“登录”按钮后被提交到服务器,这个过程被截获是很容易的。同时,防范CSRF类型攻击的特性也必须保留。这就要求:必须使用AJAX在JS端对密码加密,并向后台的AccountController中的LoginAction发起Post请求。而不能使用传统的FormSubmit的方案。
AJAX post请求中,需要注意问题: 由于需要防范CSRF攻击的同时保障密文传输安全。需要同时顾及如下问题
问题1: 如何通过AJAX向Controller发起ajax请求?
问题2:如何在ajax请求中加入AntiForgeryToken?
问题3: AJAX请求前,如何对密码进行RSA加密?
问题4: RSA的key format 有两种, pem格式和C#所支持的XML格式,通常JS支持pem, C#支持xml, 如何转换?
带着问题,进入操作步骤;
1 操作步骤
1.1 新建Web Application1.2 选择MVC, Authentication中选择”IndividualUser Accounts”
1.3 Views-> Account->Login.cshtml中代码修改如下
需要将目标URL设置为隐藏字段,供JS读取
@model WebApplication_JS_RSA.ViewModels.LoginViewModel@{ ViewBag.Title = "Log in"; Layout = "~/Views/Shared/_Layout.cshtml";}<div class="wrapper--login"> <div class="wrapper--login__body"> <h1>Login</h1> @using (@Html.BeginForm("Login", "Account", FormMethod.Post, new { id = "loginForm" })) { <div id="AccountLoginURL" class="hidden" data-url="@Url.Action("login", "account")"></div> @Html.AntiForgeryToken() <div class="val"> @Html.LabelFor(m => m.UserName) <div class="val__field"> @Html.TextBoxFor(model => model.UserName, new { @class = "form-control", id = "userNameTextBox" }) @Html.ValidationMessageFor(model => model.UserName) </div> </div> <div class="val"> @Html.LabelFor(m => Model.EncryptedPassword) <div class="val__field"> @Html.PasswordFor(model => model.EncryptedPassword, new { @class = "form-control", id = "passwordTextBox" }) @Html.ValidationMessageFor(model => model.EncryptedPassword) </div> </div> @Html.HiddenFor(model => model.ReturnUrl) @Html.HiddenFor(model => model.RedirectDomain) <div class="val__message" id="errorMsg"></div> <div class="row"> <input type="button" id="LoginButton" value="Login" class="button--primary"> </div> } <div id="PublicKey" class="hidden" data-val="@Model.PublicKey"></div> <div class="row line"> <span class="line__1"></span>or<span class="line__2"></span> </div> <div class="row"> <a href="@Url.Action("forgotpassword", "Account")" class="button--secondary">Forgot Password ?</a> </div> </div></div>@section Scripts { @Scripts.Render("~/Scripts/jquery-1.10.2.min.js") @Scripts.Render("~/Scripts/jquery.validate.min.js") @Scripts.Render("~/bundles/jqueryval") @Scripts.Render("~/Scripts/jsencrypt.js") @Scripts.Render("~/Scripts/Views/Account/Login.js")}
1.4 Scripts文件夹下新增 Login.js, jsencrypt.js文件
jsencrypt.js文件请从“参考链接3”中获取. Login.js代码如下:
var login = ( function ($) { $(document).ready( function () { $('#LoginButton').click(function () { var publicKey = $('#PublicKey').data("val"); var plainpassword = $('#passwordTextBox').val(); var AccountLoginURL = $('#AccountLoginURL').data("url"); var encryptedPassword; var formSelector = "#loginForm"; var form = $(formSelector); form.validate(); var isFormValid = form.valid(); //encrypt password if (plainpassword !== null && plainpassword !== "") { var crypt = new JSEncrypt(); crypt.setPublicKey(publicKey); encryptedPassword = crypt.encrypt(plainpassword); console.log(encryptedPassword); $('#passwordTextBox').val(encryptedPassword); } debugger; if (isFormValid) { //blockUI //showSpinner(); $.ajax({ type: "POST", url: AccountLoginURL, data: form.serialize(), success: function (data, textStatus, jqXHR) { if (data.RedirectUrl !== null) { window.location.href = data.RedirectUrl; } else { $('#errorMsg').text(data.ErrorMessage); } }, error: function (jqXhr, textStatus, errorThrown) { console.log('error: ' + jqXhr.responseText); }, complete: function (jqXHR, textStatus) { //hideSpinner(); } }); } }); }); }(jQuery));
1.5 配置publicKey和PrivateKey
通过“3参考链接“中连接2,生成公钥和私钥。 公钥保持pem格式,因为JS类库使用的需要。 把私钥通过“3参考链接“中连接4(转换器)转换成XML格式,因为.NET能够识别XML格式的私钥。
1.6 Controller端
Controllers->AccountController->Login()
// POST: /Account/Login [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login() { var form = Request.Form; string plainTextPassword = form["plainTextPassword"].ToString(); string encryptedPassword = form["encryptedPassword"].ToString(); //decrypt String privateKeyPathFile = AppDomain.CurrentDomain.BaseDirectory + @"\Content\PrivateKey.xml"; string RSAprivateKey = System.IO.File.ReadAllText(privateKeyPathFile); RSAEncryption rsaCryption = new RSAEncryption(); string decryptedPwd = rsaCryption.RSADecrypt(RSAprivateKey, encryptedPassword); return View(); }
1.7 RSAEncryption
如需代码,请参考链接: https://github.com/memoryfraction/CommonUsedFunctions/tree/master/Encryption%26Decryption
2 小结
2.1 小结
本文提出了在ASP.NETMVC中,密码传输安全问题,提出了3种可行解决方案。重点讲述了RSA不对称加密的实现方式,同时保留了微软自带的AntiForgeryToken, 以防止CSRF攻击。达到了密文传输密码的效果,即使被人截获,也无法得知密码明文。
作者知识和精力都有限,如有不足,欢迎指正。
2.2 补充
在更新版的3.1 范例代码中,更新使用了Form Serialization技术,优点: 可以直接对表单序列化,传输到后端; 能够使用C# Decoration验证; 建议前端后端同时验证, 双保险; 相见代码;
3 参考链接
1 本文详细代码,请参考GitHub:https://github.com/memoryfraction/CommonUsedFunctions/tree/master/WebApplication_JS_RSA
2 JS Encrypt的Demo,连接: http://travistidwell.com/jsencrypt/demo/
3 JSEncrypt的主页:http://travistidwell.com/jsencrypt/
4 Key的格式转换: https://superdry.apphb.com/tools/online-rsa-key-converter
5 《什么是CSRF攻击,如何在ASP.NET MVC网站中阻止这种攻击?》http://blog.csdn.net/fanrong1985/article/details/71701301
- ASP.NET MVC中使用JS实现不对称加密密码传输
- RSA实现登录页面密码加密(ASP.NET MVC)
- RSA实现登录页面密码加密(ASP.NET MVC)
- ASP.NET中密码加密
- 在ASP.NET MVC环境中使用加密与解密
- PHP+JS密码加密实现HTTP上安全地传输密码
- ASP.NET下实现密码MD5加密
- 使用FluentSecurity加密ASP.NET MVC
- asp.net密码加密
- asp.net中加密使用
- 密码传输中Rsa加密
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
- 在 Asp.NET MVC 中使用 SignalR 实现推送功能
- 75. could not initialize proxy
- HW--Church numerals
- 用了一年时间才大致把导师的 java 代码 看懂
- PyCharm中出现No tests were found的一种解决方法及PyCharm建立测试类的官方原文及翻译
- 线性表(List)---链式存储(循环链表)
- ASP.NET MVC中使用JS实现不对称加密密码传输
- JavaWeb学习总结(三十一)——国际化(i18n)
- JavaWeb学习总结(三十二)——JDBC学习入门
- JavaWeb学习总结(三十三)——使用JDBC对数据库进行CRUD
- JavaWeb学习总结(三十四)——使用JDBC处理MySQL大数据
- 1099树网的核
- 2017年集训队第四场选拔赛 -string Game
- Maven+ Redis+Spring开发实例
- 高并发程序设计读书笔记1