精华内容
下载资源
问答
  • OWASP-安全编码规范

    千次阅读 2021-05-31 23:27:48
    OWASP-安全编码规范 原文 pdfhttp//www.owasp.org.cn/owasp-project/download/OWASP_SCP_Quick_Reference_GuideChinese.pdf 1. 序言 本项目与技术无关的文档以清单列表的形式,定义了一套可以集成到软件开发生命周期...

    OWASP-安全编码规范

    原文 pdfhttp//www.owasp.org.cn/owasp-project/download/OWASP_SCP_Quick_Reference_GuideChinese.pdf

    1. 序言

    本项目与技术无关的文档以清单列表的形式,定义了一套可以集成到软件开发生命周期中的通 用软件安全编码规范。采用这些规范将减少最为常见的软件漏洞。 一般来说,开发安全的软件要比在软件包完成以后再纠正安全问题的成本低很多,且还没涉 及到因为安全问题而造成的损失。 保护关键软件资源的安全性,比以往任何时候都更为重要,因为攻击者的重点已逐步转向了 应用层。2009 年 SANS 的一项研究 1 表明,针对 Web 应用程序的攻击已占据了在互联网上 观察到攻击总数的 60%以上。 在使用本指南时,开发团队应该从评估他们的安全软件开发生命周期成熟度和开发人员知识 水平着手。由于本指南不涉及如何实现每个编码规范的具体细节,因此,开发人员需要了解 相关知识,或者有足够可用资源来提供必要的指导。通过本指南,开发人员可在无需深入了 解安全漏洞和攻击的情况下,将编码规范转换成编码要求。当然,开发团队的其他成员应该 有责任,通过提供适当的培训. 工具和资源,以验证整个系统的设计和开发是安全的。 本文档中使用的重要术语,包括部分标题和文字,都以斜体字标注,并列举在附录 B 的术语列表 中。 关于安全软件开发框架的指南不属于本文讨论的范围。但是,我们推荐以下额外的常用规范和资 源  明确定义角色和职责。  为开发团队提供足够的软件安全培训。  采用一个安全软件开发生命周期。 o OWASP CLASP 项目  建立安全编码标准。 o OWASP 开发指南项目  建立一个可重用的对象库文件。 o OWASP Enterprise Security API (ESAPI) 项目  验证安全控制的有效性。 o OWASP Application Security Verification Standard (ASVS) 项目  建立外包开发安全规范,包括在建议书(RFP)和合同中定义安全需求和验证方法。

    2. 软件安全与风险原则概览

    开发安全的软件需要对安全原则有基本的了解。虽然对于安全原则的全面评估超出了本指南的范 围,但是我们还是提供了一个快速的概览。 软件安全的目标是要维护信息资源的保密性, 完整性,和可用性,以确保业务的成功运作。该目标 通过实施安全控制来实现。本指南重点介绍具体的技术控制,以缓解常见软件漏洞的发生。虽然主 要的关注点是 Web 应用程序及其配套的基础设施,但是本指南的大部分内容可应用于任意软件部署 平台。 为了保护业务免受来自与软件相关的不能接受的风险,了解风险的意义是很有帮助的。风险是一组 威胁业务成功因素的集合。它可以被定义为一个威胁代理与一个可能含有漏洞的系统交互,该漏 洞可被利用并造成影响。虽然这可能看起来象是一个抽象的概念,但可以这样想象它一个汽车盗 窃犯(威胁代理)来到一个停车场(系统)寻找没有锁车门(漏洞)的车,当找到一个时,他们打 开门(利用)并拿走里面任何的东西(影响)。所有这些因素在安全软件开发时都扮演了一个角 色。 开发团队采用的方法和攻击者攻击应用程序所采用的方法之间有一个根本区别。开发团队通常采用 的方法是基于应用程序的目的行为。换句话说,开发团队根据功能需求文档和用例设计一个应用程 序以执行特定的任务。而另一方面,攻击者,基于“没有具体说明应拒绝的行为,则被认为是可行 的”原则,对于应用程序可以做什么更感兴趣。为了解决这个问题,一些额外的元素需要被集成到软 件生命周期的早期阶段。这些新元素是安全需求和滥用实例。本指南旨在帮助明确高等级的安全需 求,并解决许多常见的滥用情况。 Web 开发团队应当明白,基于客户端的输入验证. 隐藏字段和界面控件(例如,下拉键和单选按 钮)的客户端控制,所带来的安全性收益是有限的,这一点非常重要。攻击者可以使用工具,比 如客户端的 Web 代理(例如,OWASP WebScarab,Burp)或网络数据包捕获工具(例如, Wireshark),进行应用程序流量分析,提交定制的请求,并绕过所有的接口。另外,Flash,Java Applet 和其它客户端对象可以被反编译并进行漏洞分析。 软件的安全漏洞可以在软件开发生命周期的任何阶段被引入,包括  最初没有明确的安全需求;  创建有逻辑错误的概念设计;  使用糟糕的编码规范,从而带来了技术漏洞;  软件部署不当;  在维护或者更新过程中引入缺陷。 此外,还有重要的一点需要明白,软件漏洞可以超出软件本身的范围。根据不同的软件. 漏洞和配 套基础设施的性质,一次成功的攻击会影响下面任何或者所有的方面  软件和其相关的信息;  相关服务器的操作系统;  后端数据库;  在共享环境中的其它应用程序;  用户的系统;  与用户交互的其它软件。

    3. 安全编码规范检查列表

    输入验证

    1. 在可信系统(比如服务器)上执行所有的数据验证。

    2. 识别所有的数据源,并将其分为可信的和不可信的。验证所有来自不可信数据源(比如数 据库,文件流,等)的数据。

    3. 应当为应用程序应提供一个集中的输入验证规则。

    4. 为所有输入明确恰当的字符集,比如 UTF-8。

    5. 在输入验证前,将数据按照常用字符进行编码(规范化)。

    6. 丢弃任何没有通过输入验证的数据。

    7. 确定系统是否支持 UTF-8 扩展字符集,如果支持,在 UTF-8 解码完成以后进行输入验证。

    8. 在处理以前,验证所有来自客户端的数据,包括所有参数. URL. HTTP 头信息(比如 cookie 名字和数据值)。确定包括了来自 JavaScript. Flash 或其他嵌入代码的 post back 信息。

      • 验证在请求和响应的报头信息中只含有 ASCII 字符。
      • 核实来自重定向输入的数据(一个攻击者可能向重定向的目标直接提交恶意代码,从而避开 应用程序逻辑以及在重定向前执行的任何验证)。
    9. 验证正确的数据类型。

    10. 验证数据范围。

    11. 验证数据长度。

    12. 尽可能采用“白名单”形式,验证所有的输入。

    13. 如果任何潜在的危险字符必须被作为输入,请确保您执行了额外的控制,比如输出编码. 特定的安全 API. 以及在应用程序中使用的原因。部分常见的危险字符包括 < > " ’ % ( ) & + \ ’ " 。

      • 如果您使用的标准验证规则无法验证下面的输入,那么它们需要被单独验证 o 验证空字节 (%00); o 验证换行符 (%0d, %0a, \r, \n); o 验证路径替代字符“点-点-斜杠”(…/或 …\)。如果支持 UTF-8 扩展字符集编码,验证替 代字符 %c0%ae%c0%ae/ (使用规范化 验证双编码或其他类型的编码攻击)。

    输出编码

    1. 在可信系统(比如服务器)上执行所有的编码。

    2. 为每一种输出编码方法采用一个标准的. 已通过测试的规则。

    3. 通过语义输出编码方式,对所有返回到客户端的来自于应用程序信任边界之外的数据进行编 码。HTML 实体编码是一个例子,但不是在所有的情况下都可用。

      • 除非对目标编译器是安全的,否则请对所有字符进行编码。
      • 针对 SQL. XML 和 LDAP 查询,语义净化所有不可信数据的输出。
    4. 对于操作系统命令,净化所有不可信数据输出。.

    身份验证和密码管理

    1. 除了那些特定设为“公开”的内容以外,对所有的网页和资源要求身份验证。

    2. 所有的身份验证过程必须在可信系统(比如服务器)上执行。

    3. 在任何可能的情况下,建立并使用标准的. 已通过测试的身份验证服务。

    4. 为所有身份验证控制使用一个集中实现的方法,其中包括利用库文件请求外部身份验证服 务。

      • 将身份验证逻辑从被请求的资源中隔离开,并使用重定向到或来自集中的身份验证控制。
      • 所有的身份验证控制应当安全的处理未成功的身份验证。
      • 所有的管理和账户管理功能至少应当具有和主要身份验证机制一样的安全性。
    5. 如果您的应用程序管理着凭证的存储,那么应当保证只保存了通过使用强加密单向 salted 哈希 算法得到的密码,并且只有应用程序具有对保存密码和密钥的表/文件的写权限(如果可以避 免的话,不要使用 MD5 算法)。

    6. 密码哈希必须在可信系统(比如服务器)上执行。

    7. 只有当所有的数据输入以后,才进行身份验证数据的验证,特别是对连续身份验证机制。

    8. 身份验证的失败提示信息应当避免过于明确。比如可以使用“用户名和/或密码错误”,而不 要使用“用户名错误”或者“密码错误”。错误提示信息在显示和源代码中应保持一致。

    9. 为涉及敏感信息或功能的外部系统连接使用身份验证。

    10. 用于访问应用程序以外服务的身份验证凭据信息应当加密,并存储在一个可信系统(比如 服务器)中受到保护的地方。源代码不是一个安全的地方。

    11. 只使用 HTTP Post 请求传输身份验证的凭据信息。

    12. 非临时密码只在加密连接中发送或作为加密的数据(比如,一封加密的邮件)。通过邮件重 设临时密码可以是一个例外。

    13. 通过政策或规则加强密码复杂度的要求(比如要求使用字母. 数字和/或特殊符号)。身份 验证的凭据信息应当足够复杂以对抗在其所部署环境中的各种威胁攻击。

    14. 通过政策和规则加强密码长度要求。常用的是 8 个字符长度,但是 16 个字符长度更好,或者 考虑使用多单词密码短语。

    15. 输入的密码应当在用户的屏幕上模糊显示(比如在 Web 表单中使用“password”输入类 型)。

    16. 当连续多次登录失败后(比如通常情况下是 5 次),应强制锁定账户。账户锁定的时间必须 足够长,以阻止暴力攻击猜测登录信息,但是不能长到允许执行一次拒绝服务攻击。

    17. 密码重设和更改操作需要类似于账户创建和身份验证的同样控制等级。

    18. 密码重设问题应当支持尽可能随机的提问(比如“最喜爱的书”是一个坏的问题,因为《圣 经》是最常见的答案)。

    19. 如果使用基于邮件的重设,只将临时链接或密码发送到预先注册的邮件地址。

    20. 临时密码和链接应当有一个短暂的有效期。

    21. 当再次使用临时密码时,强制修改临时密码。

    22. 当密码重新设置时,通知用户。

    23. 阻止密码重复使用。

    24. 密码在被更改前应当至少使用了一天,以阻止密码重用攻击。

    25. 根据政策或规则的要求,强制定期更改密码。关键系统可能会要求更频繁的更改。更改时间 周期必须进行明确。

    26. 为密码填写框禁用“记住密码”功能。 2012 年 8 月 中文版 Version 1.0 7

      • 用户账号的上一次使用信息(成功或失败)应当在下一次成功登录时向用户报告。
      • 执行监控以确定针对使用相同密码的多用户帐户攻击。当用户 ID 可以被得到或被猜到时,该 攻击模式用来绕开标准的锁死功能。
    27. 更改所有厂商提供的默认用户 ID 和密码,或者禁用相关帐号。

    28. 在执行关键操作以前,对用户再次进行身份验证。

    29. 为高度敏感或重要的交易账户使用多因子身份验证机制。

    30. 如果使用了第三方身份验证的代码,仔细检查代码以保证其不会受到任何恶意代码的影响。

    会话管理

    1. 使用服务器或者框架的会话管理控制。应用程序应当只识别有效的会话标识符。

    2. 会话标识符必须总是在一个可信系统(比如服务器)上创建。

    3. 会话管理控制应当使用通过审查的算法以保证足够的随机会话标识符。

    4. 为包含已验证的会话标识符的 cookie 设置域和路径,以为站点设置一个恰当的限制值。

    5. 注销功能应当完全终止相关的会话或连接。

      • 注销功能应当可用于所有受身份验证保护的网页。
    6. 在平衡的风险和业务功能需求的基础上,设置一个尽量短的会话超时时间。通常情况下,应 当不超过几个小时。

    7. 禁止连续的登录并强制执行周期性的会话终止,即使是活动的会话。特别是对于支持富网络 连接或连接到关键系统的应用程序。终止时机应当可以根据业务需求调整,并且用户应当收 到足够的通知已减少带来的负面影响。

      • 如果一个会话在登录以前就建立,在成功登录以后,关闭该会话并创建一个新的会话。
    8. 在任何重新身份验证过程中建立一个新的会话标识符。

    9. 不允许同一用户 ID 的并发登录。

    10. 不要在 URL. 错误信息或日志中暴露会话标识符。会话标识符应当只出现在 HTTP cookie 头 信息中。比如,不要将会话标识符以 GET 参数进行传递。

    11. 通过在服务器上使用恰当的访问控制,保护服务器端会话数据免受来自服务器其他用户的未 授权访问。

    12. 生成一个新的会话标识符并周期性地使旧会话标识符失效(这可以缓解那些原标识符被获得 的特定会话劫持情况)。

    13. 在身份验证的时候,如果连接从 HTTP 变为 HTTPS,则生成一个新的会话标识符。在应用程 序中,推荐持续使用 HTTPS,而非在 HTTP 和 HTTPS 之间转换。

      • 为服务器端的操作执行标准的会话管理,比如,通过在每个会话中使用强随机令牌或参数来 管理账户。该方法可以用来防止跨站点请求伪造攻击。
    14. 通过在每个请求或每个会话中使用强随机令牌或参数,为高度敏感或关键的操作提供标准的 会话管理。

    15. 为在 TLS 连接上传输的 cookie 设置“安全”属性。

    16. 将 cookie 设置为 HttpOnly 属性,除非在应用程序中明确要求了客户端脚本程序读取或者设置 cookie 的值。

    访问控制

    1. 只使用可信系统对象(比如服务器端会话对象)以做出访问授权的决定。

    2. 使用一个单独的全站点部件以检查访问授权。这包括调用外部授权服务的库文件。

    3. 安全的处理访问控制失败的操作。 2012 年 8 月 中文版 Version 1.0 8

    4. 如果应用程序无法访问其安全配置信息,则拒绝所有的访问。

    5. 在每个请求中加强授权控制,包括服务器端脚本产生的请求,“includes”和来自象 AJAX 和 FLASH 那样的富客户端技术的请求。

      • 将有特权的逻辑从其他应用程序代码中隔离开。
      • 限制只有授权的用户才能访问文件或其他资源,包括那些应用程序外部的直接控制。
    6. 限制只有授权的用户才能访问受保护的 URL。

    7. 限制只有授权的用户才能访问受保护的功能。

    8. 限制只有授权的用户才能访问直接对象引用。

    9. 限制只有授权的用户才能访问服务。

    10. 限制只有授权的用户才能访问应用程序数据。

    11. 限制通过使用访问控制来访问用户. 数据属性和策略信息。

    12. 限制只有授权的用户才能访问与安全相关的配置信息。

    13. 服务器端执行的访问控制规则和表示层实施的访问控制规则必须匹配。

    14. 如果状态数据必须存储在客户端,使用加密算法,并在服务器端检查完整性以捕获状态的改 变。

    15. 强制应用程序逻辑流程遵照业务规则。

    16. 限制单一用户或设备在一段时间内可以执行的事务数量。事务数量/时间应当高于实际的业务 需求,但也应该足够低以判定自动化攻击。

    17. 仅使用“referer”头作为补偿性质的检查,它永远不能被单独用来进行身份验证检查,因为它可 以被伪造。

    18. 如果长的身份验证会话被允许,周期性的重新验证用户的身份,以确保他们的权限没有改 变。如果发生改变,注销该用户,并强制他们重新执行身份验证。

    19. 执行帐户审计并将没有使用的帐号强制失效(比如在用户密码过期后的 30 天以内)。

    20. 应用程序必须支持帐户失效,并在帐户停止使用时终止会话(比如角色. 职务状况. 业务 处理的改变,等等)。

    21. 服务帐户,或连接到或来自外部系统的帐号,应当只有尽可能小的权限。

    22. 建立一个“访问控制政策”以明确一个应用程序的业务规则. 数据类型和身份验证的标准或处理 流程,确保访问可以被恰当的提供和控制。这包括了为数据和系统资源确定访问需求。

    加密规范

    1. 所有用于保护来自应用程序用户秘密信息的加密功能都必须在一个可信系统(比如服务 器)上执行。

    2. 保护主要秘密信息免受未授权的访问。

    3. 全的处理加密模块失败的操作。

    4. 为防范对随机数据的猜测攻击,应当使用加密模块中已验证的随机数生成器生成所有的随机 数. 随机文件名. 随机 GUID 和随机字符串。

    5. 应用程序使用的加密模块应当遵从 FIPS 140-2 或其他等同的标准(请见 http//csrc.nist.gov/groups/STM/cmvp/validation.html)。

    6. 建立并使用相关的政策和流程以实现加. 解密的密钥管理。

    错误处理和日志

    1. 不要在错误响应中泄露敏感信息,包括系统的详细信息. 会话标识符或者帐号信息。

    2. 使用错误处理以避免显示调试或堆栈跟踪信息。

    3. 使用通用的错误消息并使用定制的错误页面。

    4. 应用程序应当处理应用程序错误,并且不依赖服务器配置。

    5. 当错误条件发生时,适当的清空分配的内存。

    6. 在默认情况下,应当拒绝访问与安全控制相关联的错误处理逻辑。

    7. 所有的日志记录控制应当在可信系统(比如服务器)上执行。

    8. 日志记录控制应当支持记录特定安全事件的成功或者失败操作。

    9. 确保日志记录包含了重要的日志事件数据。

    10. 确保日志记录中包含的不可信数据,不会在查看界面或者软件时以代码的形式被执行。

    11. 限制只有授权的个人才能访问日志。

    12. 为所有的日志记录采用一个主要的常规操作。

    13. 不要在日志中保存敏感信息,包括不必要的系统详细信息. 会话标识符或密码。

    14. 确保一个执行日志查询分析机制的存在。

    15. 记录所有失败的输入验证。

    16. 记录所有的身份验证尝试,特别是失败的验证。

    17. 记录所有失败的访问控制。

    18. 记录明显的修改事件,包括对于状态数据非期待的修改。

    19. 记录连接无效或者已过期的会话令牌尝试。

    20. 记录所有的系统例外。

    21. 记录所有的管理功能行为,包括对于安全配置设置的更改。

    22. 记录所有失败的后端 TLS 链接。

    23. 记录加密模块的错误。

    24. 使用加密哈希功能以验证日志记录的完整性。

    数据保护

    1. 授予最低权限,以限制用户只能访问为完成任务所需要的功能. 数据和系统信息。

    2. 保护所有存放在服务器上缓存的或临时拷贝的敏感数据,以避免非授权的访问,并在临时工 作文件不再需要时被尽快清除。

    3. 即使在服务器端,任然要加密存储的高度机密信息,比如,身份验证的验证数据。总是使用 已经被很好验证过的算法,更多指导信息请参见“加密规范”部分。

    4. 保护服务器端的源代码不被用户下载。

    5. 不要在客户端上以明文形式或其他非加密安全模式保存密码. 连接字符串或其他敏感信息。 这包括嵌入在不安全的形式中 MS viewstate. Adobe flash 或者已编译的代码。

    6. 删除用户可访问产品中的注释,以防止泄露后台系统或者其他敏感信息。

    7. 删除不需要的应用程序和系统文档,因为这些也可能向攻击者泄露有用的信息。

    8. 不要在 HTTP GET 请求参数中包含敏感信息。

    9. 禁止表单中的自动填充功能,因为表单中可能包含敏感信息,包括身份验证信息。

    10. 禁止客户端缓存网页,因为可能包含敏感信息。“Cache-Control no-store”,可以和 HTTP 报头 控制“Pragma no-cache”一起使用,该控制不是非常有效,但是与 HTTP/1.0 向后兼容。

    11. 应用程序应当支持,当数据不再需要的时候,删除敏感信息(比如个人信息或者特定财务 数据)。

    12. 为存储在服务器中的敏感信息提供恰当的访问控制。这包括缓存的数据. 临时文件以及只允 许特定系统用户访问的数据

    通讯安全

    1. 为所有敏感信息采用加密传输。其中应该包括使用 TLS 对连接的保护,以及支持对敏感文件 或非基于 HTTP 连接的不连续加密。

    2. TLS 证书应当是有效的,有正确且未过期的域名,并且在需要时,可以和中间证书一起安装。

    3. 没有成功的 TLS 连接不应当后退成为一个不安全的连接。

    4. 为所有要求身份验证的访问内容和所有其他的敏感信息提供 TLS 连接。

    5. 为包含敏感信息或功能. 且连接到外部系统的连接使用 TLS。

    6. 使用配置合理的单一标准 TLS 连接。

    7. 为所有的连接明确字符编码。

    8. 当链接到外部站点时,过滤来自 HTTP referer 中包含敏感信息的参数。

    系统配置

    1. 确保服务器. 框架和系统部件采用了认可的最新版本。

    2. 确保服务器. 框架和系统部件安装了当前使用版本的所有补丁。

    3. 关闭目录列表功能。

    4. 将 Web 服务器. 进程和服务的账户限制为尽可能低的权限。

    5. 当例外发生时,安全的进行错误处理。

    6. 移除所有不需要的功能和文件。

    7. 在部署前,移除测试代码和产品不需要的功能。

    8. 通过将不进行对外检索的路径目录放在一个隔离的父目录里,以防止目录结构在 robots.txt 文 档中暴露。然后,在 robots.txt 文档中“禁止”整个父目录,而不是对每个单独目录的“禁止”。

    9. 明确应用程序采用哪种 HTTP 方法 GET 或 POST,以及是否需要在应用程序不同网页中以不 同的方式进行处理。

    10. 禁用不需要的 HTTP 方法,比如 WebDAV 扩展。如果需要使用一个扩展的 HTTP 方法以支持 文件处理,则使用一个好的经过验证的身份验证机制。

    11. 如果 Web 服务器支持 HTTP1.0 和 1.1,确保以相似的方式对它们进行配置,或者确保您理解 了它们之间可能存在差异(比如处理扩展的 HTTP 方法)。

    12. 移除在 HTTP 相应报头中有关 OS. Web 服务版本和应用程序框架的无关信息。

    13. 应用程序存储的安全配置信息应当可以以可读的形式输出,以支持审计。

    14. 使用一个资产管理系统,并将系统部件和软件注册在其中。

    15. 将开发环境从生成网络隔离开,并只提供给授权的开发和测试团队访问。开发环境往往没有 实际生成环境那么安全,攻击者可以使用这些差别发现共有的弱点或者是可被利用的漏洞。

    16. 使用一个软件变更管理系统以管理和记录在开发和产品中代码的变更。

    数据库安全

    1. 使用强类型的参数化查询方法。

    2. 使用输入验证和输出编码,并确保处理了元字符。如果失败,则不执行数据库命令。

    3. 确保变量是强类型的。

    4. 当应用程序访问数据库时,应使用尽可能最低的权限。

    5. 为数据库访问使用安全凭证。

    6. 连接字符串不应当在应用程序中硬编码。连接字符串应当存储在一个可信服务器的独立配置 文件中,并且应当被加密。

    7. 使用存储过程以实现抽象访问数据,并允许对数据库中表的删除权限。

    8. 尽可能地快速关闭数据库连接。

    9. 删除或者修改所有默认的数据库管理员密码。使用强密码. 强短语,或者使用多因子身份验 证。

    10. 关闭所有不必要的数据库功能(比如不必要的存储过程或服务. 应用程序包. 仅最小化安 装需要的功能和选项(表面范围缩减))。

    11. 删除厂商提供的不必要的默认信息(比如数据库模式示例)。

    12. 禁用任何不支持业务需求的默认帐户。

    13. 应用程序应当以不同的凭证为每个信任的角色(比如用户. 只读用户. 访问用户. 管理 员)连接数据库.

    文件管理

    1. 不要把用户提交的数据直接传送给任何动态调用功能。

    2. 在允许上传一个文档以前进行身份验证。

    3. 只允许上传满足业务需要的相关文档类型。

    4. 通过检查文件报头信息,验证上传文档是否是所期待的类型。只验证文件类型扩展是不够 的。

    5. 不要把文件保存在与应用程序相同的 Web 环境中。文件应当保存在内容服务器或者数据库 中。

    6. 防止或限制上传任意可能被 Web 服务器解析的文件。

    7. 关闭在文件上传目录的运行权限。

    8. 通过装上目标文件路径作为使用了相关路径或者已变更根目录环境的逻辑盘,在 UNIX 中实现 安全的文件上传服务。

    9. 当引用已有文件时,使用一个白名单记录允许的文件名和类型。验证传递的参数值,如果与 预期的值不匹配,则拒绝使用,或者使用默认的硬编码文件值代替。

    10. 不要将用户提交的数据传递到动态重定向中。如果必须允许使用,那么重定向应当只接受通 过验证的相对路径 URL。

    11. 不要传递目录或文件路径,使用预先设置路径列表中的匹配索引值。

    12. 绝对不要将绝对文件路径传递给客户。

    13. 确保应用程序文件和资源是只读的。

    14. 对用户上传的文件扫描进行病毒和恶意软件。

    内存管理

    1. 对不可信数据进行输入和输出控制。

    2. 重复确认缓存空间的大小是否和指定的大小一样。

    3. 当使用允许多字节拷贝的函数时,比如 strncpy(),如果目的缓存容量和源缓存容量相等时,需 要留意字符串没有 NULL 终止。

    4. 如果在循环中调用函数时,检查缓存大小,以确保不会出现超出分配空间大小的危险。

    5. 在将输入字符串传递给拷贝和连接函数前,将所有输入的字符串缩短到合理的长度。

    6. 关闭资源时要特别注意,不要依赖垃圾回收机制(比如连接对象. 文档处理,等)。

    7. 在可能的情况下,使用不可执行的堆栈。

    8. 避免使用已知有漏洞的函数(比如 printf,strcat,strcpy,等)。

    9. 当方法结束时和在所有的退出节点时,正确地清空所分配的内存。

    通用编码规范

    1. 为常用的任务使用已测试且已认可的托管代码,而不创建新的非托管代码。

    2. 使用特定任务的内置 API 以执行操作系统的任务。不允许应用程序直接将代码发送给操作系 统,特别是通过使用应用程序初始的命令 shell。

      • 使用校验和或哈希值验证编译后的代码. 库文件. 可执行文件和配置文件的完整性。   使用死锁来防止多个同时发送的请求,或使用一个同步机制防止竞态条件。
    3. 在同时发生不恰当的访问时,保护共享的变量和资源。

    4. 在声明时或在第一次使用前,明确初始化您的所有变量和其他数据存储。

    5. 当应用程序运行发生必须提升权限的情况时,尽量晚点提升权限,并且尽快放弃所提升的权 限。

    6. 通过了解您使用的编程语言的底层表达式以及它们是如何进行数学计算,从而避免计算错 误。密切注意字节大小依赖. 精确度. 有无符合. 截尾操作. 转换. 字节之间的组合. “not-a-number”计算. 以及对于编程语言底层表达式如何处理非常大或者非常小的数。

    7. 不要将用户提供的数据传递给任何动态运行的功能。

    8. 限制用户生成新代码或更改现有代码。

    9. 审核所有从属的应用程序. 第三方代码和库文件,以确定业务的需要,并验证功能的安全 性,因为它们可能产生新的漏洞。

    10. 执行安全更新。如果应用程序采用自动更新,则为您的代码使用加密签名,以确保的您的下 载客户端验证这些签名。使用加密的信道传输来自主机服务器的代码

    展开全文
  • 原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-3-java%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8...

    申明:本文非笔者原创,原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-3-java%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83.md

    1输入验证和数据合法性校验

    程序接受数据可能来源于未经验证的用户,网络连接和其他不受信任的来源,如果未对程序接受数据进行校验,则可能会引发安全问题。

    1.1避免SQL注入

    使用PreparedStatement预编译SQL,解决SQL注入问题,传递给PreparedStatement对象的参数可以被强制进行类型转换,确保在插入或查询数据时与底层的数据库格式匹配。

    String sqlString = "select * from db_user where username=? and password=?";

    PreparedStatement stmt = connection.prepareStatement(sqlString);

    stmt.setString(1, username);

    stmt.setString(2, pwd);

    ResultSet rs = stmt.executeQuery();

    1.2避免XML注入

    通过StringBulider 或 StringBuffer 拼接XML文件时,需对输入数据进行合法性校验。 对数量quantity 进行合法性校验,控制只能传入0-9的数字:

    if (!Pattern.matches("[0-9]+", quantity)) {

    // Format violation

    }

    String xmlString = "\nWidget\n" +

    "500\n" +

    "" + quantity + "";

    outStream.write(xmlString.getBytes());

    outStream.flush();

    1.3避免跨站点脚本(XSS)

    对产生跨站的参数进行严格过滤,禁止传入

    //定义需过滤的字段串

    String s = "\uFE64" + "script" + "\uFE65";

    // 过滤字符串标准化

    s = Normalizer.normalize(s, Form.NFKC);

    // 使用正则表达式匹配inputStr是否存在

    Pattern pattern = Pattern.compile(inputStr);

    Matcher matcher = pattern.matcher(s);

    if (matcher.find()) {

    // Found black listed tag

    throw new IllegalStateException();

    } else {

    // ...

    }

    2声明和初始化

    2.1避免类初始化的相互依赖

    例:

    错误的写法:

    public class Cycle {

    private final int balance;

    private static final Cycle c = new Cycle();

    private static final int deposit = (int) (Math.random() * 100); // Random deposit

    public Cycle() {

    balance = deposit - 10; // Subtract processing fee

    }

    public static void main(String[] args) {

    System.out.println("The account balance is: " + c.balance);

    }

    }

    类加载时初始化指向Cycle类的静态变量c,而类Cycle的无参构造方法又依赖静态变量deposit,导致无法预期的结果。 正确的写法:

    public class Cycle {

    private final int balance;

    private static final int deposit = (int) (Math.random() * 100); // Random deposit

    private static final Cycle c = new Cycle(); // Inserted after initialization of required fields

    public Cycle() {

    balance = deposit - 10; // Subtract processing fee

    }

    public static void main(String[] args) {

    System.out.println("The account balance is: " + c.balance);

    }

    }

    3表达式

    3.1不可忽略方法的返回值

    忽略方法的放回值可能会导致无法预料的结果。

    错误的写法:

    public void deleteFile(){

    File someFile = new File("someFileName.txt");

    someFile.delete();

    }

    正确的写法:

    public void deleteFile(){

    File someFile = new File("someFileName.txt");

    if (!someFile.delete()) {

    // handle failure to delete the file

    }

    }

    3.2不要引用空指针

    当一个变量指向一个NULL值,使用这个变量的时候又没有检查,这时会导致。NullPointerException。

    在使用变量前一定要做是否为NULL值的校验。

    3.3使用Arrays.equals()来比较数组的内容

    数组没有覆盖的Object. equals()方法,调用Object. equals()方法实际上是比较数组的引用,而不是他们的内容。程序必须使用两个参数Arrays.equals()方法来比较两个数组的内容

    public void arrayEqualsExample() {

    int[] arr1 = new int[20]; // initialized to 0

    int[] arr2 = new int[20]; // initialized to 0

    Arrays.equals(arr1, arr2); // true

    }

    4数字类型和操作

    4.1防止整数溢出

    使用java.lang.Number. BigInteger类进行整数运算,防止整数溢出。

    public class BigIntegerUtil {

    private static final BigInteger bigMaxInt = BigInteger.valueOf(Integer.MAX_VALUE);

    private static final BigInteger bigMinInt = BigInteger.valueOf(Integer.MIN_VALUE);

    public static BigInteger intRangeCheck(BigInteger val) throws ArithmeticException {

    if (val.compareTo(bigMaxInt) == 1 || val.compareTo(bigMinInt) == -1) {

    throw new ArithmeticException("Integer overflow");

    }

    return val;

    }

    public static int addInt(int v1, int v2) throws ArithmeticException {

    BigInteger b1 = BigInteger.valueOf(v1);

    BigInteger b2 = BigInteger.valueOf(v2);

    BigInteger res = intRangeCheck(b1.add(b2));

    return res.intValue();

    }

    public static int subInt(int v1, int v2) throws ArithmeticException {

    BigInteger b1 = BigInteger.valueOf(v1);

    BigInteger b2 = BigInteger.valueOf(v2);

    BigInteger res = intRangeCheck(b1.subtract(b2));

    return res.intValue();

    }

    public static int multiplyInt(int v1, int v2) throws ArithmeticException {

    BigInteger b1 = BigInteger.valueOf(v1);

    BigInteger b2 = BigInteger.valueOf(v2);

    BigInteger res = intRangeCheck(b1.multiply(b2));

    return res.intValue();

    }

    public static int divideInt(int v1, int v2) throws ArithmeticException {

    BigInteger b1 = BigInteger.valueOf(v1);

    BigInteger b2 = BigInteger.valueOf(v2);

    BigInteger res = intRangeCheck(b1.divide(b2));

    return res.intValue();

    }

    }

    4.2避免除法和取模运算分母为零

    要避免因为分母为零而导致除法和取模运算出现异常。

    if (num2 == 0) {

    // handle error

    } else {

    result1= num1 /num2;

    result2= num1 % num2;

    }

    5类和方法操作

    5.1数据成员声明为私有,提供可访问的包装方法

    攻击者可以用意想不到的方式操纵public或protected的数据成员,所以需要将数据成员为private,对外提供可控的包装方法访问数据成员。

    5.2敏感类不允许复制

    包含私人的,机密或其他敏感数据的类是不允许被复制的,解决的方法有两种:

    1、类声明为final

    final class SensitiveClass {

    // ...

    }

    2、Clone 方法抛出CloneNotSupportedException异常

    class SensitiveClass {

    // ...

    public final SensitiveClass clone() throws CloneNotSupportedException {

    throw new CloneNotSupportedException();

    }

    }

    5.3比较类的正确做法

    如果由同一个类装载器装载,它们具有相同的完全限定名称,则它们是两个相同的类。 不正确写法:

    // Determine whether object auth has required/expected class object

    if (auth.getClass().getName().equals(

    "com.application.auth.DefaultAuthenticationHandler")) {

    // ...

    }

    正确写法:

    // Determine whether object auth has required/expected class name

    if (auth.getClass() == com.application.auth.DefaultAuthenticationHandler.class) {

    // ...

    }

    5.4不要硬编码敏感信息

    硬编码的敏感信息,如密码,服务器IP地址和加密密钥,可能会泄露给攻击者。

    敏感信息均必须存在在配置文件或数据库中。

    5.5验证方法参数

    验证方法的参数,可确保操作方法的参数产生有效的结果。不验证方法的参数可能会导致不正确的计算,运行时异常,违反类的不变量,对象的状态不一致。 对于跨信任边界接收参数的方法,必须进行参数合法性校验

    private Object myState = null;

    //对于修改myState 方法的入参,进行非空和合法性校验

    void setState(Object state) {

    if (state == null) {

    // Handle null state

    }

    if (isInvalidState(state)) {

    // Handle invalid state

    }

    myState = state;

    }

    5.6不要使用过时、陈旧或低效的方法

    在程序代码中使用过时的、陈旧的或低效的类或方法可能会导致错误的行为。

    5.7数组引用问题

    某个方法返回一个对敏感对象的内部数组的引用,假定该方法的调用程序不改变这些对象。即使数组对象本身是不可改变的,也可以在数组对象以外操作数组的内容,这种操作将反映在返回该数组的对象中。如果该方法返回可改变的对象,外部实体可以改变在那个类中声明的 public 变量,这种改变将反映在实际对象中。

    不正确的写法:

    public class XXX {

    private String[] xxxx;

    public String[] getXXX() {

    return xxxx;

    }

    }

    正确的写法:

    public class XXX {

    private String[] xxxx;

    public String[] getXXX() {

    String temp[] = Arrays.copyof(…); // 或其他数组复制方法

    return temp;

    }

    }

    5.8不要产生内存泄露

    垃圾收集器只收集不可达的对象,因此,存在未使用的可到达的对象,仍然表示内存管理不善。过度的内存泄漏可能会导致内存耗尽,拒绝服务(DoS)。

    6异常处理

    6.1不要忽略捕获的异常

    对于捕获的异常要进行相应的处理,不能忽略已捕获的异常

    不正确写法:

    class Foo implements Runnable {

    public void run() {

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    // 此处InterruptedException被忽略

    }

    }

    }

    正确写法:

    class Foo implements Runnable {

    public void run() {

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    Thread.currentThread().interrupt(); // Reset interrupted status

    }

    }

    }

    6.2不允许暴露异常的敏感信息

    没有过滤敏感信息的异常堆栈往往会导致信息泄漏,

    不正确的写法:

    try {

    FileInputStream fis =

    new FileInputStream(System.getenv("APPDATA") + args[0]);

    } catch (FileNotFoundException e) {

    // Log the exception

    throw new IOException("Unable to retrieve file", e);

    }

    正确的写法:

    class ExceptionExample {

    public static void main(String[] args) {

    File file = null;

    try {

    file = new File(System.getenv("APPDATA") +

    args[0]).getCanonicalFile();

    if (!file.getPath().startsWith("c:\\homepath")) {

    log.error("Invalid file");

    return;

    }

    } catch (IOException x) {

    log.error("Invalid file");

    return;

    }

    try {

    FileInputStream fis = new FileInputStream(file);

    } catch (FileNotFoundException x) {

    log.error("Invalid file");

    return;

    }

    }

    }

    6.3不允许抛出RuntimeException, Exception,Throwable

    不正确的写法:

    boolean isCapitalized(String s) {

    if (s == null) {

    throw new RuntimeException("Null String");

    }

    }

    private void doSomething() throws Exception {

    //...

    }

    正确写法:

    boolean isCapitalized(String s) {

    if (s == null) {

    throw new NullPointerException();

    }

    }

    private void doSomething() throws IOException {

    //...

    }

    6.4不要捕获NullPointerException或其他父类异常

    不正确的写法:

    boolean isName(String s) {

    try {

    String names[] = s.split(" ");

    if (names.length != 2) {

    return false;

    }

    return (isCapitalized(names[0]) && isCapitalized(names[1]));

    } catch (NullPointerException e) {

    return false;

    }

    }

    正确的写法:

    boolean isName(String s) /* throws NullPointerException */ {

    String names[] = s.split(" ");

    if (names.length != 2) {

    return false;

    }

    return (isCapitalized(names[0]) && isCapitalized(names[1]));

    }

    7多线程编程

    7.1确保共享变量的可见性

    对于共享变量,要确保一个线程对它的改动对其他线程是可见的。 线程可能会看到一个陈旧的共享变量的值。为了共享变量是最新的,可以将变量声明为volatile或同步读取和写入操作。 将共享变量声明为volatile:

    final class ControlledStop implements Runnable {

    private volatile boolean done = false;

    @Override public void run() {

    while (!done) {

    try {

    // ...

    Thread.currentThread().sleep(1000); // Do something

    } catch(InterruptedException ie) {

    Thread.currentThread().interrupt(); // Reset interrupted status

    }

    }

    }

    public void shutdown() {

    done = true;

    }

    }

    同步读取和写入操作:

    final class ControlledStop implements Runnable {

    private boolean done = false;

    @Override public void run() {

    while (!isDone()) {

    try {

    // ...

    Thread.currentThread().sleep(1000); // Do something

    } catch(InterruptedException ie) {

    Thread.currentThread().interrupt(); // Reset interrupted status

    }

    }

    }

    public synchronized boolean isDone() {

    return done;

    }

    public synchronized void shutdown() {

    done = true;

    }

    }

    7.2确保共享变量的操作是原子的

    除了要确保共享变量的更新对其他线程可见的,还需要确保对共享变量的操作是原子的,这时将共享变量声明为volatile往往是不够的。需要使用同步机制或Lock 同步读取和写入操作:

    final class Flag {

    private volatile boolean flag = true;

    public synchronized void toggle() {

    flag ^= true; // Same as flag = !flag;

    }

    public boolean getFlag() {

    return flag;

    }

    }

    //使用读取锁确保读取和写入操作的原子性

    final class Flag {

    private boolean flag = true;

    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    private final Lock readLock = lock.readLock();

    private final Lock writeLock = lock.writeLock();

    public void toggle() {

    writeLock.lock();

    try {

    flag ^= true; // Same as flag = !flag;

    } finally {

    writeLock.unlock();

    }

    }

    public boolean getFlag() {

    readLock.lock();

    try {

    return flag;

    } finally {

    readLock.unlock();

    }

    }

    }

    7.3不要调用Thread.run(),不要使用Thread.stop()以终止线程

    7.4确保执行阻塞操作的线程可以终止

    public final class SocketReader implements Runnable {

    private final SocketChannel sc;

    private final Object lock = new Object();

    public SocketReader(String host, int port) throws IOException {

    sc = SocketChannel.open(new InetSocketAddress(host, port));

    }

    @Override public void run() {

    ByteBuffer buf = ByteBuffer.allocate(1024);

    try {

    synchronized (lock) {

    while (!Thread.interrupted()) {

    sc.read(buf);

    // ...

    }

    }

    } catch (IOException ie) {

    // Forward to handler

    }

    }

    public static void main(String[] args)

    throws IOException, InterruptedException {

    SocketReader reader = new SocketReader("somehost", 25);

    Thread thread = new Thread(reader);

    thread.start();

    Thread.sleep(1000);

    thread.interrupt();

    }

    }

    7.5相互依存的任务不要在一个有限的线程池执行

    有限线程池指定可以同时执行在线程池中的线程数量的上限。程序不得使用有限线程池线程执行相互依赖的任务。可能会导致线程饥饿死锁,所有的线程池执行的任务正在等待一个可用的线程中执行一个内部队列阻塞

    8输入输出

    8.1程序终止前删除临时文件

    8.2检测和处理文件相关的错误

    Java的文件操作方法往往有一个返回值,而不是抛出一个异常,表示失败。因此,忽略返回值文件操作的程序,往往无法检测到这些操作是否失败。Java程序必须检查执行文件I / O方法的返回值。

    不正确的写法:

    File file = new File(args[0]);

    file.delete();

    正确的写法:

    File file = new File("file");

    if (!file.delete()) {

    log.error("Deletion failed");

    }

    8.3及时释放资源

    垃圾收集器无法释放非内存资源,如打开的文件描述符与数据库的连接。因此,不释放资源,可能导致资源耗尽攻击。

    try {

    final FileInputStream stream = new FileInputStream(fileName);

    try {

    final BufferedReader bufRead =

    new BufferedReader(new InputStreamReader(stream));

    String line;

    while ((line = bufRead.readLine()) != null) {

    sendLine(line);

    }

    } finally {

    if (stream != null) {

    try {

    stream.close();

    } catch (IOException e) {

    // forward to handler

    }

    }

    }

    } catch (IOException e) {

    // forward to handler

    }

    9序列化

    9.1不要序列化未加密的敏感数据

    序列化允许一个对象的状态被保存为一个字节序列,然后重新在稍后的时间恢复,它没有提供任何机制来保护序列化的数据。敏感的数据不应该被序列化的例子包括加密密钥,数字证书。 解决方法:

    对于数据成员可以使用transient ,声明该数据成员是瞬态的。

    重写序列化相关方法writeObject、readObject、readObjectNoData,防止被子类恶意重写

    class SensitiveClass extends Number {

    // ...

    protected final Object writeObject(java.io.ObjectOutputStream out) throws NotSerializableException {

    throw new NotSerializableException();

    }

    protected final Object readObject(java.io.ObjectInputStream in) throws NotSerializableException {

    throw new NotSerializableException();

    }

    protected final Object readObjectNoData(java.io.ObjectInputStream in) throws NotSerializableException {

    throw new NotSerializableException();

    }

    }

    9.2在序列化过程中避免内存和资源泄漏

    不正确的写法:

    class SensorData implements Serializable {

    // 1 MB of data per instance!

    public static SensorData readSensorData() {...}

    public static boolean isAvailable() {...}

    }

    class SerializeSensorData {

    public static void main(String[] args) throws IOException {

    ObjectOutputStream out = null;

    try {

    out = new ObjectOutputStream(

    new BufferedOutputStream(new FileOutputStream("ser.dat")));

    while (SensorData.isAvailable()) {

    // note that each SensorData object is 1 MB in size

    SensorData sd = SensorData.readSensorData();

    out.writeObject(sd);

    }

    } finally {

    if (out != null) {

    out.close();

    }

    }

    }

    }

    正确写法:

    class SerializeSensorData {

    public static void main(String[] args) throws IOException {

    ObjectOutputStream out = null;

    try {

    out = new ObjectOutputStream(

    new BufferedOutputStream(new FileOutputStream("ser.dat")));

    while (SensorData.isAvailable()) {

    // note that each SensorData object is 1 MB in size

    SensorData sd = SensorData.readSensorData();

    out.writeObject(sd);

    out.reset(); // reset the stream

    }

    } finally {

    if (out != null) {

    out.close();

    }

    }

    }

    }

    9.3反序列化要在程序最小权限的安全环境中

    (完成)

    展开全文
  • java安全编码规范

    2021-01-01 15:04:31
    https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-3-java%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83.md ...

    申明:本文非笔者原创,原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-3-java%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83.md

    1输入验证和数据合法性校验

    程序接受数据可能来源于未经验证的用户,网络连接和其他不受信任的来源,如果未对程序接受数据进行校验,则可能会引发安全问题。
    1.1避免SQL注入

    使用PreparedStatement预编译SQL,解决SQL注入问题,传递给PreparedStatement对象的参数可以被强制进行类型转换,确保在插入或查询数据时与底层的数据库格式匹配。

    String sqlString = “select * from db_user where username=? and password=?”;
    PreparedStatement stmt = connection.prepareStatement(sqlString);
    stmt.setString(1, username);
    stmt.setString(2, pwd);
    ResultSet rs = stmt.executeQuery();

    1.2避免XML注入

    通过StringBulider 或 StringBuffer 拼接XML文件时,需对输入数据进行合法性校验。 对数量quantity 进行合法性校验,控制只能传入0-9的数字:

    if (!Pattern.matches("[0-9]+", quantity)) {
    // Format violation
    }
    String xmlString = “\nWidget\n” +
    “500\n” +
    “” + quantity + “”;
    outStream.write(xmlString.getBytes());
    outStream.flush();

    1.3避免跨站点脚本(XSS)

    对产生跨站的参数进行严格过滤,禁止传入

    //定义需过滤的字段串

    String s = “\uFE64” + “script” + “\uFE65”;

    // 过滤字符串标准化

    s = Normalizer.normalize(s, Form.NFKC);

    // 使用正则表达式匹配inputStr是否存在

    Pattern pattern = Pattern.compile(inputStr);
    Matcher matcher = pattern.matcher(s);
    if (matcher.find()) {
    // Found black listed tag
    throw new IllegalStateException();
    } else {
    // …
    }

    2声明和初始化
    2.1避免类初始化的相互依赖

    例:

    错误的写法:

    public class Cycle {
    private final int balance;
    private static final Cycle c = new Cycle();
    private static final int deposit = (int) (Math.random() * 100); // Random deposit
    public Cycle() {
    balance = deposit - 10; // Subtract processing fee
    }
    public static void main(String[] args) {
    System.out.println("The account balance is: " + c.balance);
    }
    }

    类加载时初始化指向Cycle类的静态变量c,而类Cycle的无参构造方法又依赖静态变量deposit,导致无法预期的结果。 正确的写法:

    public class Cycle {
    private final int balance;
    private static final int deposit = (int) (Math.random() * 100); // Random deposit
    private static final Cycle c = new Cycle(); // Inserted after initialization of required fields
    public Cycle() {
    balance = deposit - 10; // Subtract processing fee
    }

    public static void main(String[] args) {
    System.out.println("The account balance is: " + c.balance);
    }
    }

    3表达式
    3.1不可忽略方法的返回值

    忽略方法的放回值可能会导致无法预料的结果。

    错误的写法:

    public void deleteFile(){
    File someFile = new File(“someFileName.txt”);
    someFile.delete();
    }

    正确的写法:

    public void deleteFile(){
    File someFile = new File(“someFileName.txt”);
    if (!someFile.delete()) {
    // handle failure to delete the file
    }
    }

    3.2不要引用空指针

    当一个变量指向一个NULL值,使用这个变量的时候又没有检查,这时会导致。NullPointerException。

    在使用变量前一定要做是否为NULL值的校验。
    3.3使用Arrays.equals()来比较数组的内容

    数组没有覆盖的Object. equals()方法,调用Object. equals()方法实际上是比较数组的引用,而不是他们的内容。程序必须使用两个参数Arrays.equals()方法来比较两个数组的内容

    public void arrayEqualsExample() {
    int[] arr1 = new int[20]; // initialized to 0
    int[] arr2 = new int[20]; // initialized to 0
    Arrays.equals(arr1, arr2); // true
    }

    4数字类型和操作
    4.1防止整数溢出

    使用java.lang.Number. BigInteger类进行整数运算,防止整数溢出。

    public class BigIntegerUtil {

    private static final BigInteger bigMaxInt = BigInteger.valueOf(Integer.MAX_VALUE);
    private static final BigInteger bigMinInt = BigInteger.valueOf(Integer.MIN_VALUE);
    
    public static BigInteger intRangeCheck(BigInteger val) throws ArithmeticException {
        if (val.compareTo(bigMaxInt) == 1 || val.compareTo(bigMinInt) == -1) {
            throw new ArithmeticException("Integer overflow");
        }
        return val;
    }
    
    public static int addInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.add(b2));
        return res.intValue(); 
    }
    
    public static int subInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.subtract(b2));
        return res.intValue(); 
    }
    
    public static int multiplyInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.multiply(b2));
        return res.intValue(); 
    }
    
    public static int divideInt(int v1, int v2) throws ArithmeticException {
        BigInteger b1 = BigInteger.valueOf(v1);
        BigInteger b2 = BigInteger.valueOf(v2);
        BigInteger res = intRangeCheck(b1.divide(b2));
        return res.intValue(); 
    }
    

    }

    4.2避免除法和取模运算分母为零

    要避免因为分母为零而导致除法和取模运算出现异常。

    if (num2 == 0) {
    // handle error
    } else {
    result1= num1 /num2;
    result2= num1 % num2;
    }

    5类和方法操作
    5.1数据成员声明为私有,提供可访问的包装方法

    攻击者可以用意想不到的方式操纵public或protected的数据成员,所以需要将数据成员为private,对外提供可控的包装方法访问数据成员。
    5.2敏感类不允许复制

    包含私人的,机密或其他敏感数据的类是不允许被复制的,解决的方法有两种:

    1、类声明为final

    final class SensitiveClass {
    // …
    }

    2、Clone 方法抛出CloneNotSupportedException异常

    class SensitiveClass {
    // …
    public final SensitiveClass clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
    }
    }

    5.3比较类的正确做法

    如果由同一个类装载器装载,它们具有相同的完全限定名称,则它们是两个相同的类。 不正确写法:

    // Determine whether object auth has required/expected class object
    if (auth.getClass().getName().equals(
    “com.application.auth.DefaultAuthenticationHandler”)) {
    // …
    }
    正确写法:
    // Determine whether object auth has required/expected class name
    if (auth.getClass() == com.application.auth.DefaultAuthenticationHandler.class) {
    // …
    }

    5.4不要硬编码敏感信息

    硬编码的敏感信息,如密码,服务器IP地址和加密密钥,可能会泄露给攻击者。

    敏感信息均必须存在在配置文件或数据库中。
    5.5验证方法参数

    验证方法的参数,可确保操作方法的参数产生有效的结果。不验证方法的参数可能会导致不正确的计算,运行时异常,违反类的不变量,对象的状态不一致。 对于跨信任边界接收参数的方法,必须进行参数合法性校验

    private Object myState = null;
    //对于修改myState 方法的入参,进行非空和合法性校验
    void setState(Object state) {
    if (state == null) {
    // Handle null state
    }
    if (isInvalidState(state)) {
    // Handle invalid state
    }
    myState = state;
    }

    5.6不要使用过时、陈旧或低效的方法

    在程序代码中使用过时的、陈旧的或低效的类或方法可能会导致错误的行为。
    5.7数组引用问题

    某个方法返回一个对敏感对象的内部数组的引用,假定该方法的调用程序不改变这些对象。即使数组对象本身是不可改变的,也可以在数组对象以外操作数组的内容,这种操作将反映在返回该数组的对象中。如果该方法返回可改变的对象,外部实体可以改变在那个类中声明的 public 变量,这种改变将反映在实际对象中。

    不正确的写法:

    public class XXX {
    private String[] xxxx;
    public String[] getXXX() {
    return xxxx;
    }
    }

    正确的写法:

    public class XXX {
    private String[] xxxx;
    public String[] getXXX() {
    String temp[] = Arrays.copyof(…); // 或其他数组复制方法
    return temp;
    }
    }

    5.8不要产生内存泄露

    垃圾收集器只收集不可达的对象,因此,存在未使用的可到达的对象,仍然表示内存管理不善。过度的内存泄漏可能会导致内存耗尽,拒绝服务(DoS)。
    6异常处理
    6.1不要忽略捕获的异常

    对于捕获的异常要进行相应的处理,不能忽略已捕获的异常

    不正确写法:

    class Foo implements Runnable {
    public void run() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    // 此处InterruptedException被忽略
    }
    }
    }

    正确写法:

    class Foo implements Runnable {
    public void run() {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // Reset interrupted status
    }
    }
    }

    6.2不允许暴露异常的敏感信息

    没有过滤敏感信息的异常堆栈往往会导致信息泄漏,

    不正确的写法:

    try {
    FileInputStream fis =
    new FileInputStream(System.getenv(“APPDATA”) + args[0]);
    } catch (FileNotFoundException e) {
    // Log the exception
    throw new IOException(“Unable to retrieve file”, e);
    }

    正确的写法:

    class ExceptionExample {
    public static void main(String[] args) {
    File file = null;
    try {
    file = new File(System.getenv(“APPDATA”) +
    args[0]).getCanonicalFile();
    if (!file.getPath().startsWith(“c:\homepath”)) {
    log.error(“Invalid file”);
    return;
    }
    } catch (IOException x) {
    log.error(“Invalid file”);
    return;
    }
    try {
    FileInputStream fis = new FileInputStream(file);
    } catch (FileNotFoundException x) {
    log.error(“Invalid file”);
    return;
    }
    }
    }

    6.3不允许抛出RuntimeException, Exception,Throwable

    不正确的写法:

    boolean isCapitalized(String s) {
    if (s == null) {
    throw new RuntimeException(“Null String”);
    }
    }

    private void doSomething() throws Exception {
    //…
    }

    正确写法:

    boolean isCapitalized(String s) {
    if (s == null) {
    throw new NullPointerException();
    }
    }

    private void doSomething() throws IOException {
    //…
    }

    6.4不要捕获NullPointerException或其他父类异常

    不正确的写法:

    boolean isName(String s) {
    try {
    String names[] = s.split(" ");
    if (names.length != 2) {
    return false;
    }
    return (isCapitalized(names[0]) && isCapitalized(names[1]));
    } catch (NullPointerException e) {
    return false;
    }
    }

    正确的写法:

    boolean isName(String s) /* throws NullPointerException */ {
    String names[] = s.split(" ");
    if (names.length != 2) {
    return false;
    }
    return (isCapitalized(names[0]) && isCapitalized(names[1]));
    }

    7多线程编程
    7.1确保共享变量的可见性

    对于共享变量,要确保一个线程对它的改动对其他线程是可见的。 线程可能会看到一个陈旧的共享变量的值。为了共享变量是最新的,可以将变量声明为volatile或同步读取和写入操作。 将共享变量声明为volatile:

    final class ControlledStop implements Runnable {
    private volatile boolean done = false;
    @Override public void run() {
    while (!done) {
    try {
    // …
    Thread.currentThread().sleep(1000); // Do something
    } catch(InterruptedException ie) {
    Thread.currentThread().interrupt(); // Reset interrupted status
    }
    }
    }
    public void shutdown() {
    done = true;
    }
    }

    同步读取和写入操作:

    final class ControlledStop implements Runnable {
    private boolean done = false;
    @Override public void run() {
    while (!isDone()) {
    try {
    // …
    Thread.currentThread().sleep(1000); // Do something
    } catch(InterruptedException ie) {
    Thread.currentThread().interrupt(); // Reset interrupted status
    }
    }
    }
    public synchronized boolean isDone() {
    return done;
    }
    public synchronized void shutdown() {
    done = true;
    }
    }

    7.2确保共享变量的操作是原子的

    除了要确保共享变量的更新对其他线程可见的,还需要确保对共享变量的操作是原子的,这时将共享变量声明为volatile往往是不够的。需要使用同步机制或Lock 同步读取和写入操作:

    final class Flag {
    private volatile boolean flag = true;
    public synchronized void toggle() {
    flag ^= true; // Same as flag = !flag;
    }
    public boolean getFlag() {
    return flag;
    }
    }

    //使用读取锁确保读取和写入操作的原子性

    final class Flag {
    private boolean flag = true;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();
    public void toggle() {
    writeLock.lock();
    try {
    flag ^= true; // Same as flag = !flag;
    } finally {
    writeLock.unlock();
    }
    }
    public boolean getFlag() {
    readLock.lock();
    try {
    return flag;
    } finally {
    readLock.unlock();
    }
    }
    }

    7.3不要调用Thread.run(),不要使用Thread.stop()以终止线程
    7.4确保执行阻塞操作的线程可以终止

    public final class SocketReader implements Runnable {
    private final SocketChannel sc;
    private final Object lock = new Object();
    public SocketReader(String host, int port) throws IOException {
    sc = SocketChannel.open(new InetSocketAddress(host, port));
    }
    @Override public void run() {
    ByteBuffer buf = ByteBuffer.allocate(1024);
    try {
    synchronized (lock) {
    while (!Thread.interrupted()) {
    sc.read(buf);
    // …
    }
    }
    } catch (IOException ie) {
    // Forward to handler
    }
    }
    public static void main(String[] args)
    throws IOException, InterruptedException {
    SocketReader reader = new SocketReader(“somehost”, 25);
    Thread thread = new Thread(reader);
    thread.start();
    Thread.sleep(1000);
    thread.interrupt();
    }
    }

    7.5相互依存的任务不要在一个有限的线程池执行

    有限线程池指定可以同时执行在线程池中的线程数量的上限。程序不得使用有限线程池线程执行相互依赖的任务。可能会导致线程饥饿死锁,所有的线程池执行的任务正在等待一个可用的线程中执行一个内部队列阻塞
    8输入输出
    8.1程序终止前删除临时文件
    8.2检测和处理文件相关的错误

    Java的文件操作方法往往有一个返回值,而不是抛出一个异常,表示失败。因此,忽略返回值文件操作的程序,往往无法检测到这些操作是否失败。Java程序必须检查执行文件I / O方法的返回值。

    不正确的写法:

    File file = new File(args[0]);
    file.delete();
    正确的写法:
    File file = new File(“file”);
    if (!file.delete()) {
    log.error(“Deletion failed”);
    }

    8.3及时释放资源

    垃圾收集器无法释放非内存资源,如打开的文件描述符与数据库的连接。因此,不释放资源,可能导致资源耗尽攻击。

    try {
    final FileInputStream stream = new FileInputStream(fileName);
    try {
    final BufferedReader bufRead =
    new BufferedReader(new InputStreamReader(stream));

    String line;
    while ((line = bufRead.readLine()) != null) {
      sendLine(line);
    }
    

    } finally {
    if (stream != null) {
    try {
    stream.close();
    } catch (IOException e) {
    // forward to handler
    }
    }
    }
    } catch (IOException e) {
    // forward to handler
    }

    9序列化
    9.1不要序列化未加密的敏感数据

    序列化允许一个对象的状态被保存为一个字节序列,然后重新在稍后的时间恢复,它没有提供任何机制来保护序列化的数据。敏感的数据不应该被序列化的例子包括加密密钥,数字证书。 解决方法:

    对于数据成员可以使用transient ,声明该数据成员是瞬态的。
    重写序列化相关方法writeObject、readObject、readObjectNoData,防止被子类恶意重写
    

    class SensitiveClass extends Number {
    // …
    protected final Object writeObject(java.io.ObjectOutputStream out) throws NotSerializableException {
    throw new NotSerializableException();
    }
    protected final Object readObject(java.io.ObjectInputStream in) throws NotSerializableException {
    throw new NotSerializableException();
    }
    protected final Object readObjectNoData(java.io.ObjectInputStream in) throws NotSerializableException {
    throw new NotSerializableException();
    }
    }

    9.2在序列化过程中避免内存和资源泄漏

    不正确的写法:

    class SensorData implements Serializable {
    // 1 MB of data per instance!
    public static SensorData readSensorData() {…}
    public static boolean isAvailable() {…}
    }
    class SerializeSensorData {
    public static void main(String[] args) throws IOException {
    ObjectOutputStream out = null;
    try {
    out = new ObjectOutputStream(
    new BufferedOutputStream(new FileOutputStream(“ser.dat”)));
    while (SensorData.isAvailable()) {
    // note that each SensorData object is 1 MB in size
    SensorData sd = SensorData.readSensorData();
    out.writeObject(sd);
    }
    } finally {
    if (out != null) {
    out.close();
    }
    }
    }
    }

    正确写法:

    class SerializeSensorData {
    public static void main(String[] args) throws IOException {
    ObjectOutputStream out = null;
    try {
    out = new ObjectOutputStream(
    new BufferedOutputStream(new FileOutputStream(“ser.dat”)));
    while (SensorData.isAvailable()) {
    // note that each SensorData object is 1 MB in size
    SensorData sd = SensorData.readSensorData();
    out.writeObject(sd);
    out.reset(); // reset the stream
    }
    } finally {
    if (out != null) {
    out.close();
    }
    }
    }
    }

    9.3反序列化要在程序最小权限的安全环境中

    展开全文
  • 原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-4-python%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8...

    申明:本文非笔者原创,原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-4-python%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83.md

    python语言安全

    本身要注意的有,一些危险函数,危险模块的调用,主要是系统调用。这个如果调用一定要对输入输出做好过滤,以下是代码中各种导致进行系统调用的方式。尽量避免。

    避免各种情况导致系统调用

    谨慎使用Eval

    数据序列化

    Web编程

    对应Web编程中安全概念在python web框架中的实现。url跳转,目录遍历,任意文件读取也需要考虑在内。针对不同的框架也需要。

    Flask 安全

    使用Flask-Security

    直接生成 HTML 而不通过使用Jinja2

    不要在用户提交的数据上调用Markup

    使用 Content-Disposition: attachment 标头去避免上传html文件

    防止CSRF,flask本身没有实现该功能

    Django 安全

    可参考phithon的博客,有较多相关资料。

    关闭DEBUG模式

    关闭swagger调试

    妥善保存SECRET_KEY

    使用SecurityMiddleware

    设置SECURE_HSTS_SECONDS开启HSTS头,强制HTTPS访问

    设置SECURE_CONTENT_TYPE_NOSNIFF输出nosniff头,防止类型混淆类漏洞

    设置SECURE_BROWSER_XSS_FILTER输出x-xss-protection头,让浏览器强制开启XSS过滤

    设置SECURE_SSL_REDIRECT让HTTP的请求强制跳转到HTTPS

    设置SESSION_COOKIE_SECURE使Cookie为Secure,不允许在HTTP中传输

    设置CSRF_COOKIE_SECURE使CSRF Token Cookie设置为Secure,不允许在HTTP中传输

    设置CSRF_COOKIE_HTTPONLY为HTTP ONLY

    设置X_FRAME_OPTIONS返回X-FRAME-OPTIONS: DENY头,以防止被其他页面作为框架加载导致ClickJacking

    部署前运行安全性检测 django-admin.py checksecure --settings=production_settings

    审计工具

    安装使用方式较为简单,所以不做介绍。

    引用

    展开全文
  • 前端安全编码规范

    2021-05-08 14:18:07
    前端安全编码规范1.跨站脚本攻击(Cross Sites Script)1.1 反射型XSS1.2 存储型XSS1.3 DOM based XSS1.4 如何去检测是否存在XSS1.5 XSS的攻击方式1.5.1 Cookie劫持1.5.2 构造GET和POST请求1.5.3 XSS钓鱼1.5.4 获取...
  • 出处:https://segmentfault.com/a/1190000037657222前言随着互联网高速的发展,信息安全已经成为企业重点关注焦点之一,而前端又是引发安全问题的高危据...
  • Golang编码规范

    2020-12-23 12:55:10
    语言规范2.1 true/false 求值2.2 Receiver2.3 类型申明2.4 Error Handler2.5 自定义类型的String循环问题2.6 Typical Data Races-数据竞争2.7 引用第三包需要验证2.8 字符串使用注意事项2.9 embedding 的使用2.10 ...
  • Android安全编码规范

    2021-04-30 13:33:44
    为使系统开发人员能够编写符合安全要求的代码,以降低代码安全漏洞,减少代码被利用的可能性,从而提升各系统安全水平,符合国家安全合规性要求,保障产品安全稳定运营以及信息安全,特制定本规范。 2.引用规范 ...
  • 方式如下: Web.xml XSSFilter *.XSSFilte XSSFilter /* 拦截所有请求 在XSSFilter.java类中: paramValue= paramValue.replaceAll(“>”, “”) paramValue= paramValue.replaceAll(“%”,“”) 2、输出编码编码...
  • 原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-2-php%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C...
  • [转]陌陌Java安全编码规范

    千次阅读 2021-02-25 10:09:41
    陌陌Java安全编码规范 https://github.com/momosecurity/rhizobia_J JAVA安全编码规范 目录 1、安全编码基本原则 1.1 所有输入数据都是有害的 1.2 不依赖运行环境的安全配置 1.3 安全控制措施落实在最后执行阶段 ...
  • 1、页面编码 01 在网页设置网页编码:在<head></head>中加入设置特定html标签 <meta charset="utf-8" /> 这样页面的编码就会变成utf-8 ,如果没有设置编码就会使用默认的编码,而浏览器默认编码与...
  • 网络安全-WEB中的常见编码

    万次阅读 2021-01-04 20:37:27
    编码(encode)和解码(decode)是相当广泛的话题,设计计算机对信息处理的方式,常见于加解密中,当然学习WEB也要了解一些常见的编码,可在攻击中使用编码绕过。 ASCII编码 ASCII (American Standard Code for ...
  • 前言随着互联网高速的发展,信息安全已经成为企业重点关注焦点之一,而前端又是引发安全问题的高危据点,所以,作为一个前端开发人员,需要了解前端的安全问题,以及如何去预防、修复安全漏洞。下面就...
  • 常见21种漏洞编码安全规范 1.sql注入 1.1风险描述 SQL注入主要发生在应用程序数据库层面上。程序员在设计程序的时候,没有对用户的输入进行校验,含有特殊字符语句会被数据库误认为是正常的SQL指令而运行,从而使...
  • JAVA 代码安全规范

    2021-02-13 01:18:47
    原文转载自:https://github.com/SecurityPaper/SecurityPaper-web/blob/master/_posts/2.SDL%E8%A7%84%E8%8C%83%E6%96%87%E6%A1%A3/2018-08-17-SDL-3-java%E5%AE%89%E5%85%A8%E7%BC%96%E7%A0%81%E8%A7%84%E8%8...
  • JAVA编码规范

    2021-09-21 15:13:12
    包名统一小写,点分隔符有且有一个自然语义单词 避免在父子类和不同代码块中采用相同变量名 避免不规范的缩写命名 在对元素命名时用完整单词组合表达其意 常量和变量命名时,表示类型放在词尾,如:idList、...
  • 这些年来多从事Linux下PHP和C相关的开发,带过很多项目和团队,下面是根据经验整理的PHP编码规范,可以用作给大家的范例和参考,根据需要进行取舍和修改!(可能最新的一些php5的规范不够完整,今后有机会保持更新!)...
  • 《java编码规范考试题答案》.doc

    千次阅读 2021-03-11 10:59:06
    安全编程规范中,下面说法错误的是D A. 对外部输入进行校验 B. 禁止不受信任的代码直接终止JVM C. 创建文件时指定合理的访问权限 D. 记录日志时可以抛异常 10. 若程序中需要获取操作系统登录用户名,应采取下列哪种...
  • 源自:https://segmentfault.com/a/1190000037657222声明:文章著作权归作者所有,如有侵权,请联系小编删除。前言随着互联网高速的发展,信息安全已经成...
  • 一、为什么要编码转义通常如果一样东西需要编码,说明这样东西并不适合传输。原因多种多样,如Size过大,包含隐私数据,对于Url来说,之所以要进行编码,是因为Url中有些字符会引起歧义。例如Url参数字符串中使用key...
  • HTML编码规范1 前言2 代码风格2.1 缩进与换行[强制] 使用 `4` 个空格做为一个缩进层级,不允许使用 `2` 个空格 或 `tab` 字符。[建议] 每行不得超过 `120` 个字符。2.2 命名[强制] `class` 必须单词全字母小写,单词...
  • php编码规范

    2021-05-03 08:11:59
    [TOC]# 前言如果您已经决定向加入`山西创泰`并贡献代码,请详细...# PHP编码规范与原则文件编码及编辑器格式请在开始编辑山西创泰代码之前调整你的编辑器设置。本条规范同样适用于 `PHP`、`HTML`、`CSS`、`JavaScri...
  • 本次写的是一篇如何通过针对性系统学习Web安全或者说如何能成为一名渗透测试人员(有疑问或者错误的地方还望大家多多指正)。 相信大家每学习一样东西的时候都是存在一种很迷茫的心态(包括我自己),上网找视频找...
  • 本篇文章对于学习Web安全的朋友来说应该是目前最全面的面试题合集了,后续也会陆续更新其他大厂的面试题目和知识点。另外我还整理了许多关于Web安全的学习资料+工具包等等,需要的点击Web安全学习 一、渗透测试...
  • 大厂技术高级前端Node进阶点击上方程序员成长指北,关注公众号回复1,加入高级Node交流群大家好,今天给大家介绍一下,Web安全领域常见的一些安全问题。1、SQL 注入SQL注...
  • 今天给大家介绍一下,Web安全领域常见的一些安全问题。1、SQL 注入SQL注入攻击的核心在于让Web服务器执行攻击者期望的SQL语句,以便得到数据库中的感兴趣的数据或对数据库进行读取、修...
  • web安全详解(渗透测试基础)

    千次阅读 2021-11-20 20:28:13
    文章目录一、Web基础知识1.http协议2....14. Web客户端的作用15.Web服务端作用16.集群环境的作用17.什么是Cookie,Cookie的作用。18.Cookie 的类型19.session的作用和原理Session的原理Session的两

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 82,804
精华内容 33,121
关键字:

安全编码规范web