安全编码规范

典型安全漏洞

漏洞 备注 措施
脚本注入(SQL注入) 攻击者利用 SQL注入漏洞,可以获取数据库中的多种信息(如管理员后台密码),从而脱取数据库中内容(脱库)。在特别情况下还可以修改数据库内容或者插入内容到数据库,如果数据库权限分配存在问题,或者数据库本身存在缺陷,那么攻击者可以通过SQL注入漏洞直接获取 bshell 或者服务器系统权限。 杜绝静态SQL,使用PreparedStatement
XSS攻击 通过漏洞页面在浏览器上执行 js, 从而进行一系列的操作。常见的攻击方式主要是利用 XSS 盗取用户身份,进一步获取权限,甚至利用高级攻击技巧直接攻击企业内网 。 前端传入的参数输出前要进行处理
任意文件上传 文件上传攻击被利用于上传可执行文件、脚本到服务器上,进而进一步导致服务器沦陷。 不允许应用自己实现上传,统一使用系统组件,特殊需求需报备。
路径遍历 客户端指定上传文件名,攻击者利用相对路径将文件上传到上下文目录中。 1、不允许前端传递文件名;2、前端传递的参数如果带文件名,需校验File是否在指定的目录下。
任意文件下载 任意文件读取漏洞能够读取服务器上任意的脚本代码、服务及系统的配置文件等敏感信息,造成企业代码与数据的泄漏,威胁主机安全。 不允许前端指定要下载的文件位置
越权访问/敏感信息泄漏 获取其他用户的数据,修改其他用户的信息,乃至直接获取管理员权限从而可以对整个网站的数据进行操作。 角色控制
弱口令/暴力破解 获取员工权限,拿到网站管理或内部系统权限,对进一步渗透打下基础,甚至可能直接获取系统权限。 不使用弱口令

安全规范

  • 单点登录的ticket绝对不允许使用登录名,不允许使用永久有效的ticket,同一用户每次生成的ticket必须不一样
  • 不要给黑客留可以不登录就根据字典暴力破解密码的口。
  • 缺省不允许使用弱口令。
  • 不允许按前端传递的人员Id进行响应,必须根据当前登录用户筛选数据。
  • URL中不允许传递用户名或密码等敏感信息,必须放到Body里用POST方式提交。
  • 敏感信息不落地,密码仅用于验证环节,不允许在内存中缓存或为了方便在Cookie或Session中存储。不允许应用在数据库、文件中存储平台或系统其它应用的密码。
  • 关于敏感信息,存储一定要加密,传输尽可能保障安全,必须使用POST提交,敏感信息不允许出现在URL中,不要在日志中输出客户的信息。
  • 必须登录才允许上传文件。
  • 最终用户上传的文件不允许进入上下文目录,不允许上传jsp文件(上传的html文件要对script进行trim)。
  • 不要为了方便就开一个口可以下载任何一个文件。
  • 管理功能必须加角色控制注解,限定角色访问。
  • Servlet和不在WEB-INF中的jsp必须报备,无需登录即可访问的入口必须控制住不进行上传、反序列化等危险操作。

如何避免SQL注入

所有的SQL/HQL必须使用PreparedStatement,不允许拼静态SQL

比如下面这个


    public void updateHistoryDataMapNull(Long itemId) throws DatabaseException {
        super.getHibernateTemplate().bulkUpdate("update HistoryWorkitemBLOBDAO w set w.itemDataMap2=null where w.id="+itemId);
    }

需要修改为

 super.getHibernateTemplate().bulkUpdate("update HistoryWorkitemBLOBDAO w set w.itemDataMap2=null where w.id=?");

如何避免XSS

所有请求的parameter,必须处理后才能输出

比如下面就是典型的XSS

public ModelAndView xss(HttpServletRequest request, HttpServletResponse response){
    ModelAndView view = new ModelAndView("xss.jsp");
    view.addObject("param1", request.getParameter(param1));
}
//xss.jsp
<script>
    var param1 = ${param1};
</script>

<body>
    <input value="${param1}"/>
    <input value="<%=request.getParameter('param1')%>"/>
</body>

修改方式如下:html中的内容使用ctp:toHTML,javascript上下文使用ctp:escapeJavascript进行转义

//xss.jsp
<script>
    var param1 = ${ctp:escapeJavascript(param1)};
</script>

<body>
    <input value="${ctp:toHTML(param1)}"/>
</body>

CSRF控制

我们的CSRF框架基于OWASP CSRFGuard,但为了性能做了简化,裁剪了大部分特性。

对于开发人员来说,重点保证以下两点即可:

1、所有的Ajax请求都带着CSRFTOKEN的header

2、所有的链接或IFRAME的src都带着&CSRFTOKEN=xxx后缀

平台已经对基础组件进行了处理,受控应用只需按下面的方式对开启CSRF控制开关后404的页面进行处理

我们的filter只监控.do和.jsp,所以无需处理js和css的引用

  • frame的src

增加${ctp:csrfSuffix()}

<iframe  src="${path }/collaboration/collaboration.do?method=tabOffice${ctp:csrfSuffix()}" ></iframe>
  • js指定的url

调用 CsrfGuard.getUrlSurffix()

quoteDocumentFrame.location.href = url + CsrfGuard.getUrlSurffix(url);
// 如果你很确定你的url含有?字符,可以不传url,使用
+ CsrfGuard.getUrlSurffix()
  • Ajax请求

增加beforeSend: CsrfGuard.beforeAjaxSend,例如:

$.ajax({
    type: "POST",
    beforeSend: CsrfGuard.beforeAjaxSend, 
    url: encodeURI("/seeyon/organization/orgIndexController.do?method=saveRecentData4OrgIndex&rData=" + _value)
});

 $('#attchmentForm').ajaxSubmit({
    url : genericURL + "?method=updateAttachment&edocSummaryId="+summaryId+"&affairId="+affair_id,
    type : 'POST',
    beforeSend: CsrfGuard.beforeAjaxSend, 
    success : function(data) {
    }
});
  • 后台调用
// 返回"&CSRFTOKEN={currentTokenValue}"
com.seeyon.ctp.common.taglibs.functions.Functions.csrfSuffix()
  • form增加hidden域

不常见

    var jsonForm = $('<form method="post" action="'
        + action
        + '">'+'<input type="hidden" name="CSRFTOKEN" value="'+ CsrfGuard.getToken()+'"/>' +'<input type="hidden" id="_json_params" name="_json_params" value=""/></form>', targetWindow.document);
    <input type="hidden" name="CSRFTOKEN" value="${sessionScope['CSRFTOKEN']}" />

越权控制:角色控制注解

控制Controller的方法只允许固定角色访问,避免普通用户越权。

下面的例子使得MemberController的所有方法都只允许系统管理员、动物管理员、HR管理员、集团管理员、部门管理员访问。

import com.seeyon.ctp.organization.OrgConstants.Role_NAME;
@CheckRoleAccess(roleTypes={Role_NAME.SystemAdmin,Role_NAME.AccountAdministrator,Role_NAME.HrAdmin,Role_NAME.GroupAdmin,Role_NAME.DepAdmin})
public class MemberController extends BaseController {
    // ...
}

也可以只限制指定的方法

public class SpaceController extends BaseController {
    @CheckRoleAccess(roleTypes = { Role_NAME.AccountAdministrator,Role_NAME.GroupAdmin})
    public ModelAndView spaceMain(HttpServletRequest request, HttpServletResponse response) throws BusinessException {
    }
}

支持的角色参见下面的代码

    /**
     * 应用预置角色,<b>名称不允许有下划线</b>
     */
    public static enum Role_NAME {
        /** 这个状态值不能使用,仅仅是因为升级上来占0这个位置的*/
        NULL,
        /** 集团文档空间管理员,为文档发送到集团空间使用*/
        GroupManager,
        /** 单位文档空间管理员,为文档发送到单位空间使用,并且可以在知识管理中进行博客管理 */
        AccountAdmin,        
        /** 部门管理员 */
        DepAdmin,
        /** 部门主管 */
        DepManager,
        /** 部门分管领导 */
        DepLeader,
        /** 部門公文收發員 */
        Departmentexchange,    
        /** 外部人员*/
        ExternalStaff,
        /** 普通人员*/
        GeneralStaff,
        /** 协同模板管理员*/
        TtempletManager,
        /** 考勤管理员 */
        AttendanceAdmin,
        /** HR管理员 */
        HrAdmin,
        /** 工资管理员 */
        SalaryAdmin,
        /** 表单管理员 */
        FormAdmin,
        /** 车辆管理员 */
        CarsAdmin,
        /** 图书管理员 */
        BooksAdmin,
        /** 办公用品管理员 */
        StocksAdmin,
        /** 会议室管理员*/
        MeetingRoomAdmin,
        /** 公文管理员*/
        EdocManagement,
        /** 單位公文收發員 */
        Accountexchange,
        /** 发文拟文*/
        SendEdoc,
        /** 签报拟文*/
        SignEdoc,
        /** 收文登记*/
        RecEdoc,
        /** 绩效管理员*/
        PerformanceAdmin,
        /** 集团公告管理员*/
        GroupBulletinAdmin,
        /** 集团公告审核员*/
        GroupBulletinAuditor,
        /** 集团新闻管理员*/
        GroupNewsAdmin,
        /** 集团新闻审核员*/
        GroupNewsAuditor,
        /** 集团调查管理员*/
        GroupSurveyAdmin,
        /** 集团调查审核员*/
        GroupSurveyAuditor,
        /** 集团讨论管理员*/
        GroupDiscussAdmin,
        /** 单位公告管理员*/
        UnitBulletinAdmin,
        /** 单位公告审核员*/
        UnitBulletinAuditor,
        /** 单位新闻管理员*/
        UnitNewsAdmin,
        /** 单位新闻审核员*/
        UnitNewsAuditor,
        /** 单位调查管理员*/
        UnitSurveyAdmin,
        /** 单位调查审核员*/
        UnitSurveyAuditor,
        /** 单位讨论管理员*/
        UnitDiscussAdmin,
        /** 个人博客*/
        MemberBlog,
        /** 知识管理集团库管理员 */
        DocGroupAdmin,
        /** 知识管理单位库管理员 */
        DocUnitAdmin,
        /** 单位主管 */
        AccountManager,  
        /** 系统管理员 */
        SystemAdmin,
        /** 审计管理员 */
        AuditAdmin,
        /** 集团管理员 */
        GroupAdmin,
        /** 单位后台管理员 */
        AccountAdministrator,
        /** 超级管理员 */
        SuperAdmin,
        /** 部门空间角色 */
        DeptSpace,
        /** 归档公文修改 */
        EdocModfiy,
        /** 信息上报人 */
        InfoReporter,
        /** 期刊审核人 */
        MagazineAudit,
        /** 信息报送管理员 */
        InfoManager,
        /** G6 角色 收文登记 */
        RegisterEdoc,
        /** G6 角色 收文分发 */
        FenfaEdoc,
        /** 驾驶员 */
        CarsDriver,
        /** 办公设备管理员 */
        AssetsAdmin,
        /** 报表管理员 */
        ReportAdmin,
        /** REST 管理员 */
        RESTManager,
        /** 单位分管领导 */
        AccountLeader,
        /** 大秀管理员*/
        ShowAdmin,
        /**A82\81 流程绩效用户授权*/
        WfanalysisAuth,
        /**A82\81 行为绩效用户授权*/
        BehavioranalysisAuth,
        /** 集团用车管理员*/
        GroupSpecialTrainAdmin,
        /** 单位用车管理员*/
        AccountSpecialTrainAdmin,
        /** 部门用车管理员*/
        DepartmentSpecialTrainAdmin,
        /**员工福利管理员*/
        EmployeeBenefitAdmin,
        /** vjoin人员(v-join平台人员的默认角色)*/
        VjoinStaff,
        /** 机构负责人(v-join平台预制的角色)*/
        VjoinUnitManager,
        /** 单位负责人(v-join平台预制的角色)*/
        VjoinAccountManager,
    }

无需登录的请求(before V7.1SP1)

对于无需登录即可访问的url,比如登录,可以加@NeedlessCheckLogin注解

public class MainController{
    @NeedlessCheckLogin
    public ModelAndView login(HttpServletRequest request, HttpServletResponse response) throws Exception {

    }
}

注意,上面的注解仅在7.1SP1之前可用,7.1SP1以后将匿名请求控制收归平台统一管理,原则上不允许应用随意新增无需登录的请求。

XXE控制(XML实体攻击)

XML解析时,调用平台提供的XXEUtil的prevent处理,提供了对XMLReader、XMLInputFactory、SAXReader、SAXBuilder、SAXTransformerFactory、DocumentBuilderFactory的支持,如

            InputSource sheetSource = new InputSource(is);
            SAXParserFactory saxFactory = SAXParserFactory.newInstance();
            SAXParser saxParser = saxFactory.newSAXParser();
            XMLReader sheetParser = saxParser.getXMLReader();
            XXEUtil.prevent(sheetParser);

            XMLReader reader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
            XXEUtil.prevent(reader);

对于jdom的parseText方法,也提供了XXEUtil.safeParseText替代。

文件越权控制:加V

我们的很多功能对链接做了权限控制,同一个URL,不同的登录用户也不能下载。

以doc插件为例

在插件的配置文件(pluginProperties.xml)中增加security.digesturl

<?xml version="1.0" encoding="utf-8"?>
<ctpConfig>
    <doc>
        <security>
            <!-- 需要做URL digest的URL注册,空格分隔1为uri地址,2为method(没有可以省略),3为digest参数名 -->
            <digesturl>/doc.do rightNew resId,frType,docLibId,docLibType,isShareAndBorrowRoot,all,edit,add,readonly,browse,list,parentCommentEnabled,parentRecommendEnable,parentVersionEnabled,flag</digesturl>
            <digesturl>/doc.do docPropertyIframe resId,docResId,frType,docLibId,docLibType,isShareAndBorrowRoot,all,edit,add,create,readonly,browse,read,list,parentCommentEnabled,parentRecommendEnable</digesturl>
            <digesturl>/doc.do docProperty _resId,docResId,frType,docLibId,docLibType,isShareAndBorrowRoot,_all,_edit,_add,_create,_readonly,_browse,_read,_list,propEditValue,_parentCommentEnabled,isPerBorrow</digesturl>
            <digesturl>/doc.do knowledgeBrowse docResId,entranceType,openFrom,docVersionId</digesturl>
        </security>
    </doc>
</ctpConfig>

处理后的链接会多出一个v参数,只有当前登录用户才能够正常访问。

统一权限控制

7.1SP1以后对所有的Servlet、Spring controller、Ajax、jsp、Rest、SOAP请求做了统一的权限控制。

应用自行添加的JSP文件,不会被解释执行。在运行期内修改已存在的JSP文件,也会被判别为非法篡改,禁止执行。

results matching ""

    No results matching ""