英特尔nuc做网站服务器,做招商网站的前景怎么样,申请百度账号注册,wordpress默认分类链接即使只限定在“软件架构设计”这个语境下#xff0c;系统安全仍然是一 个很大的话题。我们谈论的计算机系统安全#xff0c;不仅仅是指“防御系统 被黑客攻击”这样狭隘的安全#xff0c;还至少应包括#xff08;不限于#xff09;以下这些问 题的具体解决方案。
认证系统安全仍然是一 个很大的话题。我们谈论的计算机系统安全不仅仅是指“防御系统 被黑客攻击”这样狭隘的安全还至少应包括不限于以下这些问 题的具体解决方案。
认证Authentication系统如何正确分辨出操作用户的真实 身份授权Authorization系统如何控制一个用户该看到哪些数 据操作哪些功能凭证Credential系统如何保证它与用户之间的承诺是双方 当时真实意图的体现是准确、完整且不可抵赖的保密Confidentiality系统如何保证敏感数据无法被包括系 统管理员在内的内外部人员所窃取、滥用传输Transport Security系统如何保证通过网络传输的信息 无法被第三方窃听、篡改和冒充验证Verification系统如何确保提交到每项服务中的数据 是合乎规则的不会对系统稳定性、数据一致性、正确性产生风险 与安全相关的问题一般不会直接创造价值解决起来又烦琐复 杂费时费力很容易被开发人员忽略但庆幸的是这些问题基本上 也都是与具体系统、具体业务无关的通用性问题这意味着它们往往 会存在业界通行的、已被验证过是行之有效的解决方案甚至已经形 成行业标准不需要开发者自己从头去构思如何解决。
一、认证 认证、授权和凭证可以说是一个系统中最基础的安全设计哪怕 再简陋的信息系统大概也不可能忽略“用户登录”功能。信息系统 为用户提供服务之前总是希望先弄清楚“你是谁”认证、“你 能干什么”授权以及“你如何证明”凭证这三个基本问题。 然而这三个基本问题又不像部分开发者认为的那样只是一个“系 统登录”功能仅仅是校验一下用户名、密码是否正确这么简单。账 户和权限作为一种必须最大限度保障安全和隐私同时又要兼顾各个 系统模块甚至系统间共享访问的基础主数据它的存储、管理与使用 都面临一系列复杂的问题。对于某些大规模的信息系统账户和权限 的管理往往要由专门的基础设施来负责譬如微软的活动目录 Active DirectoryAD或者轻量目录访问协议Lightweight Directory Access ProtocolLDAP跨系统的共享使用甚至会用到 区块链技术。
1、认证的标准 世纪之交Java迎来了Web的辉煌时代互联网的迅速兴起促使 Java进入快速发展时期。这时候基于HTML和JavaScript的超文本Web 应用迅速超过了“Java 2时代”之前的Java Applets应用B/S系统对 最终用户认证的需求使得“安全认证”的重点逐渐从“代码级安全” 转为“用户级安全”即你是否信任正在操作的用户。在1999年随 J2EE 1.2[1]发布的Servlet 2.2中添加了一系列用于认证的API主要 包括下列两部分内容
标准方面添加了四种内置的、不可扩展的认证方案即ClientCert、Basic、Digest和Form实现方面添加了一套与认证和授权相关的程序接口譬如 HttpServletRequest::is-UserInRole()、HttpServletRequest::getUserPrincipal() 等方法。 一项发布超过20年的老旧技术原本并没有什么专门提起的必要 性笔者之所以引用这件事是希望从它包含的两部分内容中引出一 个架构安全性的经验原则以标准规范为指导、以标准接口去实现。 安全涉及的问题很麻烦但解决方案已相当成熟对于99%的系统来 说在安全上不去做轮子不去想发明创造严格遵循标准就是最 恰当的安全设计。 引用J2EE 1.2对安全的改进还有另一个原因它内置的ClientCert、Basic、Digest和Form这四种认证方案都很有代表性刚好分别 覆盖了通信信道、协议和内容层面的认证。而这三种层面的认证恰好 涵盖了主流的三种认证方式具体含义和应用场景列举如下。
通信信道上的认证你和我建立通信连接之前要先证明你是 谁。在网络传输Network场景中的典型应用是基于SSL/TLS传输安 全层的认证。通信协议上的认证你请求获取我的资源之前要先证明你是 谁。在互联网Internet场景中的典型应用是基于HTTP协议的认证通信内容上的认证你使用我提供的服务之前要先证明你是 谁。在万维网World Wide Web场景中的典型应用是基于Web内容的 认证。
二、授权 授权这个概念通常伴随着认证、审计、账号一同出现并称为 AAAAAuthentication、Authorization、Audit、Account也有一些 领域把Account解释为计费的意思。授权行为在程序中的应用非常广 泛给某个类或某个方法设置范围控制符public、protected、 private、在本质上也是一种授权访问控制行为。而 在安全领域中所说的授权就更具体一些通常涉及以下两个相对独立 的问题。
确保授权的过程可靠对于单一系统来说授权的过程是比较 容易控制的以前很多语境上提到授权实质上讲的都是访问控制 但理论上两者是应该分开的。确保授权的结果可控授权的结果用于对程序功能或者资源的 访问控制Access Control成理论体系的权限控制模型有很多譬 如自主访问控制Discretionary Access ControlDAC、强制访问控制 Mandatory Access ControlMAC、基于属性的访问控制AttributeBased Access ControlABAC还有最为常用的基于角色的访问控制 Role-Based Access ControlRBAC。
RBAC
所有的访问控制模型实质上都是在解决同一个问题“谁 User拥有什么权限Authority去操作Operation哪些资源 Resource。” 这个问题初看起来并不难一种直观的解决方案就是在用户对象 上设定一些权限当用户使用资源时检查是否有对应的操作权限即 可。很多著名的安全框架譬如Spring Security的访问控制本质上就 是这么做的。不过这种把权限直接关联在用户身上的简单设计在 复杂系统上确实会导致一些比较烦琐的问题。试想一下如果某个系 统涉及成百上千的资源又有成千上万的用户若要为每个用户访问 每个资源都分配合适的权限必定导致巨大的操作量和极高的出错概 率这也正是RBAC所关注的问题之一。 RBAC模型在业界中有多种说法其中以美国George Mason大学信 息安全技术实验室提出的RBAC96模型最具系统性得到了普遍的认 可。为了避免对每一个用户设定权限RBAC将权限从用户身上剥离 改为绑定到“角色”Role上将权限控制变为对“角色拥有操作 哪些资源的许可”这个逻辑表达式的值是否为真的求解过程。RBAC的 主要元素的关系可以图5-5来表示。 许可是抽象权限的具象化 体现权限在RBAC系统中的含义是“允许何种操作作用于哪些资源之 上”这句话的具体实例即为“许可”。提出许可这个概念的目的其 实与提出角色的目的是完全一致的只是更为抽象。角色为的是解耦 用户与权限之间的多对多关系而许可为的是解耦操作与资源之间的 多对多关系譬如不同的数据都能够有增、删、改等操作如果将数 据与操作搅和在一起也会面临配置膨胀问题。 从实现角度来看Spring Security中的角色和权限的差异很小 它们完全共享同一套存储结构唯一的差别仅是角色会在存储时自动 带上“ROLE_”前缀罢了。但从使用角度来看角色和权限的差异可以 很大用户可以自行决定系统中许可是只能对应到角色身上还是可 以让用户也拥有某些角色中没有的权限。这一点不符合RBAC的思想 但笔者个人认同这是一种创新而非破坏在Spring Security的文档上 说得很清楚这取决于你自己如何使用。
OAuth 2
OAuth 2是在RFC 6749中定义的国际标准在RFC 6749正文的第一句就阐明了OAuth 2是面向解决第三方应用ThirdParty Application的认证授权协议。
如何获得 我的授权呢一种最简单的方案是把我的用户账号和密码都告诉 Travis-CI但这显然导致了以下这些问题。
密码泄漏如果Travis-CI被黑客攻破将导致我的GitHub的密码 同时被泄漏。访问范围Travis-CI将有能力读取、修改、删除、更新我放在 GitHub上的所有代码仓库而我并不希望它修改、删除文件。授权回收只有修改密码才能回收我授予给Travis-CI的权力可 是我在GitHub的密码只有一个授权的应用除了Travis-CI之外却还有许 多修改密码意味着所有别的第三方应用程序会全部失效。
OAuth 2给出了多种解决办法这些办法的共同特征是以令牌 Token代替用户密码作为授权的凭证。有了令牌之后哪怕令牌被 泄漏也不会导致密码的泄漏令牌上可以设定访问资源的范围以及 时效性每个应用都持有独立的令牌任何一个失效都不会波及其 他。这样上面提出的三个问题就都解决了。加令牌后的整个授权流程。 这个流程图里面涉及了OAuth 2中的几个关键术语我们通过前面 那个具体的上下文语境来解释其含义这对理解后续几种认证流程十 分重要。
第三方应用Third-Party Application需要得到授权访问我的 资源的那个应用即此场景中的“Travis-CI”。授权服务器Authorization Server能够根据我的意愿提供授 权授权之前肯定已经进行了必要的认证过程但它与授权可以没有 直接关系的服务器即此场景中的“GitHub”。资源服务器Resource Server能够提供第三方应用所需资源 的服务器它与认证服务可以是相同的服务器也可以是不同的服务 器即此场景中的“我的代码仓库”。资源所有者Resource Owner拥有授权权限的人即此场景 中的“我”。操作代理User Agent指用户用来访问服务器的工具对于 人类用户来说这个通常是指浏览器但在微服务中一个服务经常会 作为另一个服务的用户此时指的可能就是HttpClient、RPCClient或者 其他访问途径。 “用令牌代替密码”确实是解决问题的好方法但这充其量只能 算个思路距离可实施的步骤还是不够具体的流程图中的“要求/同 意授权”、“要求/同意发放令牌”、“要求/同意开放资源”的服务 请求、响应该如何设计这就是执行步骤的关键了。对此OAuth 2一 共提出了四种不同的授权方式这也是OAuth 2复杂烦琐的主要原 因分别为
授权码模式Authorization Code隐式授权模式Implicit密码模式Resource Owner Password Credentials客户端模式Client Credentials。
三、凭证 在前面介绍OAuth 2的内容中每一种授权模式的最终目标都是拿 到访问令牌但从未涉及过拿回来的令牌应该长什么样子。反而还挖 了一些坑没有填为何说OAuth 2的一个主要缺陷是令牌难以主动失 效。这节讨论的主角是令牌同时还会讨论如果不使用OAuth 2 如何以最传统的方式完成认证、授权。 对“如何承载认证授权信息”这个问题的不同看法代表了软件 架构对待共享状态信息的两种不同思路状态应该维护在服务端还 是在客户端之中在分布式系统崛起以前这个问题原本已有了较为 统一的结论即以HTTP协议的Cookie-Session机制为代表的服务端状 态存储在分布式崛起前的三十年中都是主流的解决方案。不过到了 最近十年由于分布式系统中共享数据必然会受到CAP不兼容原理的打 击限制迫使人们重新去审视之前已基本放弃掉的客户端状态存储 这就让原本只在多方系统中采用的JWT令牌方案在分布式系统中也有 了另一块用武之地。本节将围绕Cookie-Session和JWT之间的相同与不 同而展开。
1、Cookie-Session 大家知道HTTP协议是一种无状态的传输协议无状态是指协议对 事务处理没有上下文的记忆能力每一个请求都是完全独立的但是 我们中肯定有许多人并没有意识到HTTP协议无状态的重要性。假如你 做了一个简单的网页其中包含1个HTML、2个Script脚本、3个CSS 还有10张图片若要这个网页成功展示在用户屏幕前需要完成16次 与服务端的交互来获取上述资源由于网络传输等各种因素的影响 服务器发送的顺序与客户端请求的先后并没有必然的联系按照可能 出现的响应顺序理论上最多会有P(16,16)20922789888000种可能 性。试想一下如果HTTP协议不是设计成无状态的这16次请求每一 次都有依赖关联先调用哪一个、先返回哪一个都会对结果产生影 响的话那协调工作会多么复杂。 可是HTTP协议的无状态特性又有悖于我们最常见的网络应用场 景典型就是认证授权系统总得要获知用户身份才能提供合适的服 务因此我们也希望HTTP能有一种手段让服务器至少能够区分出 发送请求的用户是谁。为了实现这个目的RFC 6265规范定义了HTTP 的状态管理机制在HTTP协议中增加了Set-Cookie指令该指令的含 义是以键值对的方式向客户端发送一组信息此信息将在此后一段时 间内的每次HTTP请求中以名为Cookie的Header附带着重新发给服务 端以便服务端区分来自不同客户端的请求。 一般来说系统会把状态信息保存在服务端在 Cookie里只传输一个无字面意义的、不重复的字符串习惯上以 sessionid或者jsessionid为名然后服务端会把这个字符串作为 Key以Key/Entity的结构存储每一个在线用户的上下文状态再辅以 一些超时自动清理之类的管理措施。这种服务端的状态管理机制就是 今天大家非常熟悉的SessionCookie-Session也即最传统但今天依然 广泛应用于大量系统中的由服务端与客户端联动来完成的状态管理 机制。 Session-Cookie在单节点的单体服务环境中是最合适的方案但 当需要水平扩展服务能力要部署集群时就比较麻烦了由于Session 存储在服务器的内存中当服务器水平拓展成多节点时设计者必须 在以下三种方案中选择其一。
牺牲集群的一致性让负载均衡器采用亲和式的负载均衡算 法譬如根据用户IP或者Session来分配节点每一个特定用户发出的 所有请求都一直被分配到其中某一个节点来提供服务每个节点都不 重复地保存着一部分用户的状态如果这个节点崩溃了里面的用户 状态便完全丢失。牺牲集群的可用性让各个节点之间采用复制式的Session每 一个节点中的Session变动都会发送到组播地址的其他服务器上这样 即使某个节点崩溃了也不会中断某个用户的服务但Session之间组 播复制的同步代价高昂节点越多时同步成本越高牺牲集群的分区容忍性让普通的服务节点中不再保留状态 将上下文集中放在一个所有服务节点都能访问到的数据节点中进行存 储。此时的矛盾是数据节点成为单点一旦数据节点损坏或出现网络 分区整个集群将都不能再提供服务。 通过前面章节的内容我们已经知道只要在分布式系统中共享信 息CAP就不可兼得所以分布式环境中的状态管理一定会受到CAP的 限制无论怎样都不可能完美。但如果只是解决分布式下的认证授权 问题并顺带解决少量状态的问题就不一定只能依靠共享信息去实 现。这句话的言外之意是提醒读者接下来的JWT令牌与CookieSession并不是完全对等的解决方案JWT令牌只用来处理认证授权问 题充其量只能携带少量非敏感的信息是Cookie-Session在认证授 权问题上的替代品而不能说JWT要比Cookie-Session更先进更不可 能说JWT可以全面取代Cookie-Session机制。
JWT JWTJSON Web Token定义于RFC 7519标准之中是目前广泛使 用的一种令牌格式尤其经常与OAuth 2配合应用于分布式的、涉及多 方的应用系统中。介绍JWT的具体构成之前我们先来直观地看一下它 是什么样子的如图5-13所示。 以上截图来自JWT官网https://jwt.io数据则是笔者随意编 的。右边的JSON结构是JWT令牌中携带的信息左边的字符串呈现了 JWT令牌的本体。它最常见的使用方式是附在名为Authorization的 Header发送给服务端前缀在RFC 6750中被规定为Bearer。如果你没 有忘记“认证方案”与“OAuth 2”的内容那看到Authorization这 个Header与Bearer这个前缀时便应意识到它是HTTP认证框架中的 OAuth 2认证方案。如下代码展示了一次采用JWT令牌的HTTP实际请 求 右边的状态信息是对令牌使用Base64URL转码后得到 的明文请特别注意是明文JWT只解决篡改的问题并不解决泄漏的 问题因此令牌默认是不加密的。尽管你自己要加密也不难做到接 收时自行解密即可但这样做其实没有太大意义。从明文中可以看到JWT令牌是以JSON结构毕竟名字就叫JSON Web Token存储的该结构总体上可划分为三个部分每个部分间用点号 “.”分隔开。
第一部分是令牌头Header
第二部分是负载Payload这是令牌真正需要向服务端 传递的信息
第三部分是签名Signature签名的意思是使用在对 象头中公开的特定签名算法通过特定的密钥由服务器进行保密 不能公开对前面两部分内容进行加密计算以例子里使用的JWT默认 的HMAC SHA256算法为例将通过以下公式产生签名值。 尽管大型系统中只使用JWT来维护上下文状态服务端完全 不持有状态是不太现实的不过将热点的服务单独抽离出来做成无状 态仍是一种有效提升系统吞吐能力的架构技巧。但是JWT也并非没 有缺点的完美方案它存在以下几个经常被提及的缺点。
令牌难以主动失效JWT令牌一旦签发理论上就和认证服务器 再没有什么瓜葛了在到期之前就会始终有效除非服务器部署额外 的逻辑去处理失效问题这对某些管理功能的实现是很不利的。相对更容易遭受重放攻击首先说明Cookie-Session也是有重放 攻击问题的只是因为Session中的数据控制在服务端手上在应对重放 攻击时会相对主动一些。要在JWT层面解决重放攻击问题需要付出比较 大的代价无论是加入全局序列号HTTPS协议的思路、Nonce字符 串HTTP Digest验证的思路挑战应答码当下网银动态令牌的思 路还是缩短令牌有效期强制频繁刷新令牌在真正应用时都很麻 烦。真要处理重放攻击建议的解决方案是在信道层次譬如启用 HTTPS上解决而不在服务层次譬如在令牌或接口其他参数上增 加额外逻辑上解决。只能携带相当有限的数据HTTP协议并没有强制约束Header的 最大长度但是各种服务器、浏览器都会有自己的约束譬如 Tomcat就要求Header最大不超过8KB而在Nginx中则默认为4KB因 此在令牌中存储过多的数据不仅耗费传输带宽还有额外的出错风 险。必须考虑令牌在客户端如何存储严谨地说这个并不是JWT的 问题而是系统设计的问题。如果授权之后操作完关掉浏览器就结束 了那把令牌放到内存里面压根不考虑持久化那是最理想的方案。 但并不是谁都能忍受一个网站关闭之后下次就一定强制要重新登录 的。这样的话想想客户端该把令牌存放到哪里Cookie localStorageIndexed DB它们都有泄漏的可能而令牌一旦泄漏别 人就可以冒充用户的身份做任何事情。无状态也不总是好的这个其实也不是JWT的问题。如果不能想 象无状态会有什么不好的话笔者可以提个需求请基于无状态JWT的 方案做一个在线用户实时统计功能。
四、保密 保密是加密和解密的统称是指以某种特殊的算法改变原有的信 息数据使得未授权的用户即使获得了已加密的信息但因不知解密 的方法或者知晓解密的算法但缺少解密所需的必要信息仍然无法 了解数据的真实内容。
1、保密的强度
保密是有成本的追求越高的安全等级就要付出越多的工作量 与算力消耗。
1以摘要代替明文如果密码本身比较复杂那一次简单的哈希 摘要至少可以保证即使传输过程中有信息泄漏也不会被逆推出原信 息即使密码在一个系统中泄漏了也不至于威胁到其他系统的使 用。但这种处理不能防止弱密码被彩虹表攻击所破解。
2先加盐值再做哈希是应对弱密码的常用方法盐值可以为弱密 码建立一道防御屏障一定程度上防御已有的彩虹表攻击但不能阻 止加密结果被监听、窃取后攻击者直接发送加密结果给服务端进行 冒认。
3将盐值变为动态值能有效防止冒认如果每次密码向服务端传 输时都掺入了动态的盐值让每次加密的结果都不同那即使传输给 服务端的加密结果被窃取了也不能冒用来进行另一次调用。尽管在 双方通信均可能泄漏的前提下协商出只有通信双方才知道的保密信息 是完全可行的后续5.5.3节会提到但这样协商出盐值的过程将变 得极为复杂而且每次协商只保护一次操作也难以阻止对其他服务 的重放攻击。
4给服务加入动态令牌在网关或其他流量公共位置建立校验逻 辑这样服务端在愿意付出集群中分发令牌信息等代价的前提下可 以做到防止重放攻击但是依然不能解决传输过程中被嗅探而泄漏信 息的问题。
5启用HTTPS可以防御链路上的恶意嗅探也以在通信层面解决 了重放攻击的问题。但是依然有因客户端被攻破产生伪造根证书的风 险、因服务端被攻破产生的证书泄漏而被中间人冒认的风险、因CRL更 新不及时或者OCSP Soft-fail产生吊销证书被冒用的风险以及因TLS 的版本过低或密码学套件选用不当产生加密强度不足的风险。
6为了抵御上述风险保密强度还要进一步提升譬如银行会使 用独立于客户端的存储证书的物理设备俗称的U盾来避免根证书被 客户端中的恶意程序窃取伪造大型网站涉及账号、金钱等操作时 会使用双重验证开辟一条独立于网络的信息通道如手机验证码、电 子邮件来显著提高冒认的难度甚至一些关键企业如国家电网 或机构如军事机构会专门建设遍布全国各地的与公网物理隔离的 专用内部网络来保障通信安全。 2、客户端加密 关于客户端在用户登录、注册类场景里是否需要对密码进行加密 的问题一直存有争议。笔者的观点很明确为了保证信息不被黑客窃 取而做客户端加密没有太大意义对绝大多数的信息系统来说启用 HTTPS可以说是唯一的实际可行的方案。但是为了保证密码不在服务 端被滥用在客户端就开始加密还是很有意义的。密码明文被写入数据库、被输出到日志中之类的事情也 屡见不鲜做系统设计时就应该把明文密码这种东西当成是最烫手的 山芋来看待越早消灭掉越好将一个潜在的炸弹从客户端运到服务 端对绝大多数系统来说都没有必要。 五、传输
1、摘要、加密与签名 我们从JWT令牌的一小段“题外话”来引出现代密码学算法的三种 主要用途摘要、加密与签名。JWT令牌携带信息的可信度源自于它是 被签过名的信息是令牌签发者真实意图的体现因此是不可篡改 的。然而你是否了解签名具体做了什么为什么有签名就能够让负 载中的信息变得不可篡改和不可抵赖呢要解释数字签名Digital Signature必须先从密码学算法的另外两种基础应用“摘要”和 “加密”说起。 摘要也称为数字摘要Digital Digest或数字指纹Digital Fingerprint。JWT令牌中默认的签名信息是对令牌头、负载和密钥 三者通过令牌头中指定的哈希算法HMAC SHA256计算出来的摘要 值。 理想的哈希算法都具备两个特性。一是易变性这是指算法的输 入端发生了任何一点细微变动都会引发雪崩效应Avalanche Effect使得输出端的结果产生极大的变化。二是不可逆性摘要的运算过程是单向的不可能 从摘要的结果中逆向还原出输入值来。世间的信息有无穷多种而摘 要的结果无论其位数是32、128、512位甚至更多位都是一个有限 的数字因此输入数据与输出的摘要结果必然不是一一对应的关系。 由这两个特性可见摘要的意义是在源信息不泄漏的前提下辨别 其真伪。易变性保证了可以从公开的特征上甄别出信息是否来自于源 信息不可逆性保证了不会从公开的特征暴露出源信息这与今天用 作身份甄别的指纹、面容和虹膜的生物特征是具有高度可比性的。 20世纪70年代中后期出现的非对称加密算法从根本上解决了密钥 分发的难题它将密钥分成公钥和私钥。公钥可以完全公开无须安 全传输的保证。私钥由用户自行保管不参与任何通信传输。根据这 两个密钥加解密方式的不同使得算法可以提供两种不同的功能。
公钥加密私钥解密这种就是加密用于向私钥所有者发送 信息这个信息可能被他人篡改但是无法被他人得知。私钥加密公钥解密这种就是签名用于让所有公钥所有者 验证私钥所有者的身份并且防止私钥所有者发布的内容被篡改。但 是它不用于保证内容不被他人获得。
2、数字证书 当我们无法以“签名”的手段来达成信任时就只能求助于其他 途径。不妨先想一想真实的世界中我们是如何达成信任的其实不 外乎以下两种。
基于共同私密信息的信任。譬如某个陌生号码找你说是你的 老同学生病了要找你借钱。你能够信任他的方式是向对方询问一些 你们两个应该知道且只有你们两个知道的私密信息如果对方能够 回答出来他有可能真的是你的老同学否则他十有八九就是个骗 子。基于权威公证人的信任。如果有个陌生人找你说他是警察 让你把存款转到他们的安全账号上。你能够信任他的方式是去一趟公 安局如果公安局担保他确实是个警察那他有可能真的是警察否 则他十有八九就是个骗子。 回到网络世界中我们并不能假设授权服务器和资源服务器是互 相认识的所以通常不太会采用第一种方式而第二种就是目前保证 公钥可信分发的标准即公开密钥基础设施Public Key InfrastructurePKI。咱们不必纠缠于PKI概念上的内容只要知道里面定义的“数字证 书认证中心”相当于前面例子中“权威公证人”的角色是负责发放 和管理数字证书的权威机构即可。 到这里出现了本节的主角之一证书Certificate。证书是权 威CA中心对特定公钥信息的一种公证载体也可以理解为权威CA对特 定公钥未被篡改的签名背书。由于客户的机器上已经预置了这些权威 CA中心本身的证书称为CA证书或者根证书所以我们能够在不依 靠网络的前提下使用根证书里面的公钥信息对其所签发的证书中的 签名进行确认。到此终于打破了鸡生蛋、蛋生鸡的循环使得整套 数字签名体系有了坚实的逻辑基础
六、 验证
数据验证与程序的编码密切相关很多开发者往往不把它归入安全范畴。然而关注“你是谁”认证和“你能做什么”授权等问题是合理的安全实践关注“你做得对不对”验证同样合理。数据验证不严谨导致的安全问题远多于其他安全攻击而这些问题的风险有高有低。高风险的数据问题可能导致的损失不一定小于黑客攻击带来的损失。
验证的必要性
数据验证是贯穿于开发中的每一个层次的常规工作。缺失的校验会影响数据质量过度的校验不会使系统更加健壮反而会制造垃圾代码甚至带来副作用。下列是一个实际的例子说明了没有统一校验策略所带来的问题
前端提交用户数据 - 控制器层发现邮箱为空 - 安全层发现手机号格式错误 - 服务层发现邮箱重复 - 持久层发现签名字段超长无法插入数据库。这种情况不仅用户体验差还会导致各层的异常处理混乱。
服务端验证的层次
验证应在哪一层进行存在争议
控制器层控制器层做格式校验服务层不做防止重复校验。服务层服务层做业务校验控制器层不做因为业务校验放在控制器层不合适。两层均做控制器层做格式校验服务层做业务校验看似合理但会导致冗余代码和复杂性。
Java中的验证标准做法
在Java中推荐的做法是使用Bean Validation将校验行为从分层中剥离出来在Bean上做验证。这种方法的好处包括
预置格式验证可以统一处理无业务含义的格式验证。业务验证重用针对Bean进行验证更具价值因为一个Bean可以被多个方法用作参数或返回值。集中管理利于统一认证的异常体系、国际化和客户端返回格式。避免污染业务代码防止防御性代码侵入业务逻辑。统一执行多个校验器避免用户在不同层次重复提交数据的试错体验。
Bean Validation的实践
Bean Validation是Java的标准规范执行频率高且可重复利用。对无业务逻辑的注解如NotEmpty、Pattern等重复执行不会产生成本。对于带业务逻辑的校验如需要外部资源参与的校验应让使用者判断是否触发。对于不同操作如新增、修改、删除需要不同校验规则时可以使用分组校验来处理。
以下是一个使用Bean Validation的示例
public class Account extends BaseEntity { NotEmpty(message 用户不允许为空) private String username; NotEmpty(message 用户姓名不允许为空) private String name; private String avatar; Pattern(regexp 1\\d{10}, message 手机号格式不正确) private String telephone; Email(message 邮箱格式不正确) private String email;
}每次执行校验时注解都会被运行。复杂的业务校验可以通过自定义校验注解实现如
POST
public Response createUser(Valid UniqueAccount Account user) { return CommonResponse.op(() - service.createAccount(user));
}PUT
CacheEvict(key #user.username)
public Response updateUser(Valid AuthenticatedAccount NotConflictAccount Account user) { return CommonResponse.op(() - service.updateAccount(user));
}这些自定义校验注解的含义如下
UniqueAccount确保用户对象唯一。AuthenticatedAccount确保用户对象与当前登录用户一致。
通过这种方式验证的灵活性和管理的集中性得到了保证系统的健壮性和用户体验也得到了提升。