注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue 发布时间:2018/1/18 13:47:15
互联网上的网站,除了一些原始的只有HTML页面的网站,基本上都有必须经过身份验证才能访问的页面,如网站的内容管理后台,注册用户的登录、管理后台。这些页面在经过充分而安全的身份验证前,如果暴露给攻击者,后果可能是灾难性的。
而即使是同一后台页面,也往往需要对不同的用户进行权限上的区分。比如,一个管理后台对超级管理员、普通管理员、采编人员进行区分。一个面向普通用户的用户后台,需要区分普通用户和不同级别的VIP用户。根据其身份的不同,给予其不同的权限配置。
目前,主流采取的身份验证方式是Forms形式的验证,即通过表单机制,对用户传来的用户名和密码进行逻辑验证。也可以采用OAuth 2.0协议之类的,引用第三方验证,赋予权限。也有通过IIS之类的服务器软件进行身份验证的站点,但多为局域网(Intranet)应用,并非主流。
一、Forms(表单)+会话(session)方式验证
这是目前运用最为广泛的身份验证方式。其基本的原理是,服务器为某个session(会话空间)分配一个内存,这个会话空间里保存了众多连接到服务器的客户端对象。不同的客户端,在服务器端拥有私有的(如果允许多点登录,可能相同的)session值,这些值被服务器语言解释程序直接用来判定客户端的身份,因此,从安全角度说,session的获取条件必须尽可能的苛刻。
在获取session后,我们就可以获取某个客户端特定的session值,来给予其特定的权限。
需要注意的是,一个session的存活需要指定特定的客户端,特定的服务器端以及不中断的操作时间。如果一段时间不进行任何操作,则session就会被注销,这段时间通常被俗称为掉线时间,规范一点的称谓超时时间。这个时间一般为20-30分钟,在IIS、Apache等服务器软件上可以设置这个时间。
令人感到意外的是,虽然计算机世界充斥着各种语言和技术,每种语言对某些同样或类似的东西往往有着不同的称谓,但在session这个机制上,它们却是出奇的统一:从老式的微软ASP,到ASP.net,再到PHP、JSP,会话的规范名称都是“session”。
1、执行严格的赋予session的策略
session一旦被赋予,就会被服务器端赋予各种各样的权限。因此,应该对赋予条件进行充分的判断。
一般情况下,我们会将用户账号、加密过的密码保存在服务器的数据库中。当然,你也可以存在Active Directory(Windows平台)、注册表或者动态页面代码中,非常不推荐将账号密码存在文本文件或任意其他实体文件中。但目前主流是存储在数据库中。
存在服务器上的账号和密码,账号可以以公开形式存储,但密码必须经过特定程度、方式的加密。一种十分流行的做法是使用MD5加密来对密码进行各种变种加密。由于当前存在着暴力破解MD5值的网站(免费或收费),1-3层的MD5加密已经不是绝对安全了(3层的MD5加密即对某个字符串的MD5值再进行2次MD5加密),我们应该采取另外一种方式:即自己独特的变种加密。你或许认为,1-3层的MD5加密都不够,那么明文存储密码岂不是更不安全。然而,明文存储密码这样的事情却曾经出现在大名鼎鼎的技术网站CSDN上,简直令人惊叹。2011年底爆发的CSDN泄密事件曾经震惊了整个互联网。
自己独特的变种加密如何进行呢?例如,在加密方法中混入一个较长的字符,然后进行各种嵌套,各种混入。这个加密方法应该是如此的复杂,以至于短时间内除了目前尚未成型的量子计算机,否则根本找不到破解办法。这个加密方法在用户输入特定密码字符时就应该通过JS进行本地化的处理,这样,即使攻击者截获了用户的密码字符串,得到的也只是一堆加密字符(密文)。而使用前端JS加密后,就算攻击者知道加密方法和密文,也无法进行逆向破解。
当加密过的密码被送到服务器后,与服务器上存储的密文进行比对,比对成功,则赋予该客户端权限。
你还可以使用多重验证法,即除了比对数据库里面的用户名和密码,还可以将类似“密钥”的东西存储在程序代码中,用户输入“密钥”后,该输入被进行本地化加密,传送到服务器,用后端语言对其值和代码值进行比对,只有账号、密码完全正确的同时,密钥也正确,才赋予权限。这里特别要注意语法的正确性,如果语法不正确,服务器可能会将技术细节返回到客户端。即使“密钥”的内容就算被攻击者获取到了,如果没有开放站外登录,短时间内也难以破解,但始终这是一种隐患,因为它使得攻击者了解了正确的“密钥”是什么。
下面介绍各种语言的session赋予办法。
2、ASP
先从ASP说起,ASP创建一个名字为“admin”的session,并将其值设置为“myname”,语法如下:
- Session("admin")="myname"
一旦值被存入 session 变量,它就能被 ASP 应用程序中的任何页面使用:
- Welcome <%Response.Write(Session("admin"))%>
这段代码会显示为“Welcome myname”。
修改session和创建的语法一样。只要直接赋值就行。而删除session也很简单,其语法如下:
- <%
- Session.Abandon
- %>
更详细的使用方法,请点击这里:ASP Session。
3、ASP.net
ASP.net创建session的方式也很简单,直接赋值就行了,但要注意,session的名称要放到中括号里,而不是老式ASP中的圆括号:
- Sesssion["admin"] = "myname";
使用起来也很简单:
- string userName=Session["admin"].ToString();
修改session和创建的语法一样(这一点和老式ASP一样)。只要直接赋值就行。而删除所有session语法如下:
- Session.Clear();
4、PHP
在PHP中,在您把用户信息存储到 PHP session 中之前,首先必须启动会话。session_start() 函数必须位于 标签之前:
- <?php session_start();
- $_SESSION['admin']="myname";
- ?>
- <html>
- <body>
- </body>
- </html>
使用起来也很方便:
- echo $_SESSION['admin'];
如果您希望删除某些 session 数据,可以使用 unset() 或 session_destroy() 函数。
unset() 函数用于释放指定的 session 变量:
- <?php
- unset($_SESSION['admin']);
- ?>
也可以通过 session_destroy() 函数彻底终结所有 session:
- <?php
- session_destroy();
- ?>
有关PHPsession的章节:PHP Sessions。
5、JSP
JSP中,设置一个session的语法如下:
- HttpSession session = request.getSession();
- session.setAttribute("admin", "myname");
获取session的语法如下:
- request.getSession().getAttribute("admin");
如何删除session呢,可以调用public void removeAttribute("admin") 方法来移除指定的session,也可以调用public void invalidate()方法来使整个session无效。
更多关于JSP session的相关知识,请访问我们的JSP Session章节。
6、如何维持session
不同语言的session有着不同的超时时间,一旦过了这个时间,服务器端将会自动删除该客户端的session,客户端将会自动失去会话状态。而如何维持这个session呢?从代码角度说,有HTML/JS代码(即前端方法)和服务器端方法之分。
最常用的前端方法有2个:一是通过JS+HTML DOM,另一种则是通过meta标签。
JS+HTML DOM方法代码如下(这里刷新时间设定为10分钟):
- <script>
- functionrefresh(seconds){
- setTimeout("self.location.reload()",seconds*1000);
- }
- refresh(600);//调用方法启动定时刷新,数值单位:秒。
- </script>
通过meta标签方法的代码如下,可以将该代码放到<head>与</head>之间:
- <meta http-equiv="refresh" content="600" />
在上述两种方案中,较好的为第二种,因为如果当前页面是在IE浏览器的模式窗口中打开的,默认情况下,self.location.reload()方法将会失效,而refresh meta标签在IE模式窗口下仍然有效。但是,要注意的是,不要在主页面中直接使用这2种方法!因为如果直接使用它们,存在致命的缺陷。试想一下,如果用户在论坛敲了许多字,突然碰到自动刷新页面,那么用户体验将是极差的。如何避免这一缺陷呢?我们可以用两种方法:一是Ajax,定期的向服务器发出请求,来维持session,但缺点是在某些浏览器里会被屏蔽(如,微信的内置浏览器会屏蔽Ajax机制),二是通过插入一个宽高皆为0像素、看不到的iframe页面,但这样做叶有缺点,姑且不论会消耗更多客户端内存,而是现在的技术标准已经逐渐废弃了<iframe>标签,在可预见的未来,<iframe>标签将会被彻底的放弃。
从服务器端来说,又分为:服务器软件设定法和代码方法两种。服务器软件设定法即设置IIS、Apache等服务器软件中的超时时间,将超时时间设置长一点。这个方法虽然简洁,但有很大的缺陷:对所有类型的session都是一样的(我们往往只需要保持一种session长期在线),这样就会消耗大量服务器资源。
而代码方法大致又分为两种:一是修改页面超时时间。二是通过cookies保存账号密码。
先来说说通过代码方式修改页面超时时间。主流的语言,修改超时方式的语法都很简洁。首先我们看老式的ASP(时间为分钟):
- <%
- Session.Timeout=30
- %>
再来看下ASP.net。ASP.net有3种修改超时时间的办法:可以通过Machine.config、Web.config、单个页面代码修改。
Machine.config方式修改的是全局超时时间,即对所有服务器上的站点都一视同仁,因此缺乏灵活性。代码如下,这里设置为默认的90秒:
- <httpRuntime executionTimeout="90" maxRequestLength="4096" useFullyQualifiedRedirectUrl="false" minFreeThreads="8" minLocalRequestFreeThreads="4" appRequestQueueLimit="100" />
Web.config可以设置单个站点超时时间,这里设置的为720秒,前面的属性maxRequestLength为用户上传文件限制大小:
- <system.web>
- <httpRuntime maxRequestLength="102400" executionTimeout="720" />
- </system.web>
单个页面也可以规定本页面的超时时间,可以使用Server.ScriptTimeout来设定超时:
- Server.ScriptTimeout = 120;
如果在Web.config里设置了debug属性,此时,ScriptTimeout会被忽略:
- <compilation debug="true" targetFramework="4.0">
PHP修改页面超时时间有2种办法,一是通过设置php.ini配置文件,二是通过代码方法。先来看看php.ini的配置方法,通过设置session.gc_maxlifetime和session.cookie_lifetime节点属性值可以设置超时时间:
- ini_set('session.gc_maxlifetime', "3600"); // 秒
- ini_set("session.cookie_lifetime","3600"); // 秒
第二种方法即通过代码方法设置Session时间戳:
- $_SESSION['expiretime'] = time() + 3600; // 刷新时间戳,单位:秒
JSP可以调用 public void setMaxInactiveInterval(int interval) 方法来设置session超时。
页面session超时时间如果设置的很长,会拖慢服务器。一个更加长期保存session的办法是使用cookies保存账号密码,但是必须注意一点,保存到cookies中的密码,必须经过独特的加密(不要采取简单的公有方法,如1-3层的MD5等),取而代之,可以采取一种独特的可逆加密方法,这个方法的加密解密需要自己写。然后在用户再次访问页面的时候,将账号和加密过的密码送回服务器端,服务器端代码对加密后的密码进行解密,并将其与数据库或者存在其他地方的账密进行比对。比对成功,则重新赋予session。如果不知道从哪里下手,可以参照接下来的相关章节。但这种办法的安全性并不高,一旦机器中毒,cookies被恶意软件读取,就可以被攻击者利用,容易造成账号密码的丢失。
在这里,提供另一个思路,可以通过IP+cookies保存账号的办法,来维持登录状态。即如果处于特定IP下的机器,如果同时拥有特定的cookies,则赋予其登录状态。另一个更严苛的办法是,IP+cookies保存账号和密码。同上面的方法一样,由于cookies本身的特性,这种机制同样不是很安全。
二、ASP.net独有验证方式
1、设置ASP.net身份验证方式
ASP.net有3种方式进行身份验证:Windows身份验证、Forms身份验证、.net Passport身份验证。
Windows身份验证适合Intranet(局域网)应用。在一个机器中,所有的.net应用都共有一个Machine.config,这个文件的设置对该机器上所有.net应用有效,除非它被Web.config的设置覆盖。而Web.config位于每个.net应用程序中,它是该程序的设置,甚至连子目录都可以有自己的Web.config,且如果其覆盖了父级Web.config的设置,则其权限比父Web.config文件还高。
Web.config中的<system.web>节的子节<authentication>中,为该应用设置身份验证方法。mode参数被设置为以下值中的一种:Windows、Forms、Passport或none。下面的示例,将验证办法设置为Windows身份验证:
- <system.web>
- <authentication mode="Windows" />
- </system.web>
当然,除了设置身份验证模式,还可以设置其他更加详细的规则:
- <system.web>
- <authentication mode="Windows" />
- <authorization>
- <allow users="Domain\users" />
- <allow roles="Domain\group" />
- <deny users="*" />
- </authorization>
- </system.web>
如果这些设置被子目录的Web.config覆盖,则子目录会采用自己的设置。可以通过增加多个allow 和 deny来允许或拒绝某些用户或角色的访问。用户或角色的意义是由ASP.net Web应用程序配置使用的IIS身份验证类别来决定的。对于基本和集成的Windows身份验证,用户是计算机或域用户,角色是计算机或域组。
allow和deny的规则如下:
通配符 | <allow>部分 | <deny>部分 |
---|---|---|
* | 允许任何人访问 | 拒绝任何人访问 |
? | 允许匿名用户访问 | 拒绝未授权用户访问 |
例如,下面的代码拒绝所有未授权用户的访问:
- <authorization>
- <deny users="?" />
- </authorization>
下面的代码允许名为Bill和Tom的用户访问:
- <authorization>
- <allow users="Domain\Bill,Domain\Tom" />
- <deny users="*" />
- </authorization>
但是这只是演示例子,通过Web.config的方式对单个用户进行授权是不好的习惯,如果Web.config被暴露,将会透露重大的敏感信息。
可以通过roles属性来启用基于角色的安全性,如下面的代码只允许管理员组访问:
- <authorization>
- <allow roles="Domain\Administrators" />
- <deny users="*" />
- </authorization>
<allow>和<deny>出现的顺序是很重要的,先出现的规则将会拥有更高的权限。例如,下面的配置将禁止任何用户的访问:
- <authorization>
- <deny users="*" />
- <allow roles="Domain\Administrators" />
- </authorization>
那么,如何保证单个页面或文件夹的访问安全呢?这里可以使用<location>节来配置访问权限:
- <location path="manager.aspx">
- <system.web>
- <authorization>
- <deny users="?" />
- </authorization>
- </system.web>
- </location>
其中的path属性也可以设置为整个文件夹。一个Web.config文件中,可以配置多个<location>节。
2、Windows身份验证
在Intranet应用中,如果用户被分配了域账户和组,就可以使用Windows身份验证。在之前的教程中,我们已经简要介绍了IIS基本、摘要式或集成式身份验证。如果ASP.net采用Windows身份验证,那么IIS可以通过使用已配置的IIS身份验证机制来执行身份验证。
使用Windows身份验证有3个步骤:
- 配置IIS
- 在Web.config中设置身份验证方式为“Windows”
- 在Web.config中设置授权
第1步,配置IIS,使得IIS使用下面一种身份验证:匿名、基本、摘要式、集成Windows。通常可以启用基本身份验证,如果想让除IE外的其他浏览器兼容,那么必须使用基本身份验证方式。如果客户端没有通过防火墙或者代理服务器,就可以使用Windows身份验证。
第2步,设置身份验证:
- <system.web>
- <authentication mode="Windows" />
- </system.web>
第3步,设置授权:
- <location path="manager.aspx">
- <system.web>
- <authorization>
- <deny users="?" />
- </authorization>
- </system.web>
- </location>
3、Forms身份验证
为一个ASP.net程序启用Forms身份验证,必须完成4步:
- 配置IIS,使用匿名身份验证
- 在Web.config中设置身份验证方式为“Forms”
- 设置授权
- 创建一个登陆页面
第1步在之前的教程中已有介绍,每个版本的IIS版本,设置方式也有差异,但总体来说都很简单,这里不再赘述。
第2步,设置身份验证方式,需要在Web.config中进行如下配置:
- <system.web>
- <authentication mode="Forms">
- <forms loginUrl="login.aspx" />
- </authentication>
- </system.web>
在这里,login.aspx是被指定的跳转页,凡是未经授权的用户,都会跳转到这个页面。必须通过SSL(安全套接字层)来保护登录页面,即https://开头的页面地址。有关SSL的知识,将在后面的内容中介绍。
第3步,设置授权,和上述Windows身份验证方式一样,可以指定特定的页面或文件夹的访问权限。
第4步,创建登录页面。这个登录页面拥有一个可以输入用户名和密码的表单,可以通过比对用户的输入以及服务器上存储的用户名密码,来决定是否给该用户授权。
这个验证机制的前3步,之前已经介绍过了,现在介绍关键的第4步。
这个登录页login.aspx如何验证登录呢,是通过session判定吗?NO!session的方法上一节已经介绍过了。这里,我们可以使用.net的FormsAuthentication 对象的方法来验证用户。
如何判断用户已登录呢?使用Request.IsAuthenticated属性,如果值为true,则表示已登录。如果已登录,使用HttpContext.User.Identity.Name属性可获取登录名。
下面,我们正式开始实战。为了便于说明,我们采取.net的WebPages模型,可以利用.net服务器组件将其转换为Web Forms。这里,先准备一个简单的登录页面:
- <h1>用户状态</h1><form action="<%= Request.RawUrl %>" method="post">
- <% if( Request.IsAuthenticated ) { %>
- 当前用户已登录,登录名:<%= Context.User.Identity.Name.HtmlEncode() %> <br />
- <input type="submit" name="Logon" value="退出" />
- <% } else { %>
- <b>当前用户还未登录。</b>
- <% } %>
- </form>
可以看出,当前页面只能判断用户是否已登录,但目前还没有登录框供用户进行输入。下面我们加上登录框:
- <h1>普通登录</h1><form action="<%= Request.RawUrl %>" method="post">
- 登录名:<input type="text" name="loginName" style="width: 200px" value="DoctorWho" />
- <input type="submit" name="NormalLogin" value="登录" />
- </form>
现在,我们有了一个判断区域,和一个登录区域。但是,现在我们还缺少登录过程的代码:
- public void Logon()
- {
- FormsAuthentication.SignOut(); //FormsAuthentication 对象的 SignOut()方法可以实现退出登录
- }
- public void NormalLogin()
- {
- // -----------------------------------------------------------------
- // 注意:演示代码没有检查用户名与密码是否正确,检查用户名和密码是否正确的相关知识,参见上一节的介绍。
- // -----------------------------------------------------------------
- string loginName = Request.Form["loginName"];
- if( string.IsNullOrEmpty(loginName) )
- return;
- FormsAuthentication.SetAuthCookie(loginName, true);
- TryRedirect();
- }
现在,我们只要在登录区域的文本框中输入直接点击“登录”按钮(由于文本框有默认值DoctorWho),就可以实现登录。点击状态区域的退出按钮,就可以退出登录了。这里,我们总结一下登录与注销的方法:
- 登录:调用FormsAuthentication.SetAuthCookie()方法,传递一个登录名即可。
- 注销:调用FormsAuthentication.SignOut()方法。
基本的逻辑就是这样,如果想实现用户名、密码的验证,以及具体的用户登录权限,则需要结合.net的相关知识,通过C#或VB.NET 来实现具体的业务逻辑。
4、.net Passport身份验证
.net Passport身份验证是一种利用微软的账号,实现本地站点登录的身份验证方式。本质上,它与QQ账号登录、微信账号登录、微博账号登录一样,属于第三方账号登录。由于当前国内采取这种方式登录的站点比大熊猫还稀少,这里不再赘述。
三、第三方登录
第三方登录的基本过程是,用户客户端通过访问你的站点的页面,转到第三方平台(QQ、微信、微博)进行验证,第三方平台给用户带有票据信息的Cookies,再携带这个cookie访问你的站点,你的站点对其进行授权。
目前,国内主流的第三方登录有:QQ账号登录、微信账号登录、微博账号登录。支付宝/淘宝账号由于涉及资金安全问题,应用并不广泛,只在阿里系的相关网站中使用。这些地方登录都遵循着一个协议:OAuth 2.0协议。如果你想学习OAuth 2.0协议的相关技术,请访问我们的OAuth 2.0教程。具体的登录方式,各个平台大同小异,具体请参照官方的文档。这里列出这三个平台的申请地址:
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue 发布时间:2018/1/18 13:47:15