{"record":{"id":"648e722b746fff6b","repo":"pagehelper-org/Mybatis-PageHelper","slug":"count-countcolumn-has-a-risk-of-sql-injection","errorCode":null,"errorMessage":"count(${countColumn}) has a risk of SQL injection","messagePattern":"count\\((.+?)\\) has a risk of SQL injection","errorType":"exception","errorClass":"PageException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/github/pagehelper/Page.java","lineNumber":591,"sourceCode":"    public <E> PageSerializable<E> doSelectPageSerializable(ISelect select) {\n        select.doSelect();\n        return (PageSerializable<E>) this.toPageSerializable();\n    }\n\n    public long doCount(ISelect select) {\n        this.pageSizeZero = true;\n        this.pageSize = 0;\n        select.doSelect();\n        return this.total;\n    }\n\n    public String getCountColumn() {\n        return countColumn;\n    }\n\n    public void setCountColumn(String countColumn) {\n        if (!\"0\".equals(countColumn) && !\"*\".equals(countColumn) && SqlSafeUtil.check(countColumn)) {\n            throw new PageException(\"count(\" + countColumn + \") has a risk of SQL injection\");\n        }\n        this.countColumn = countColumn;\n    }\n\n    public BoundSqlInterceptor getBoundSqlInterceptor() {\n        return boundSqlInterceptor;\n    }\n\n    public void setBoundSqlInterceptor(BoundSqlInterceptor boundSqlInterceptor) {\n        this.boundSqlInterceptor = boundSqlInterceptor;\n    }\n\n    BoundSqlInterceptor.Chain getChain() {\n        return chain;\n    }\n\n    void setChain(BoundSqlInterceptor.Chain chain) {\n        this.chain = chain;","sourceCodeStart":573,"sourceCodeEnd":609,"githubUrl":"https://github.com/pagehelper-org/Mybatis-PageHelper/blob/c692616c5bc95b41aa779b502f991825c8e5acbc/src/main/java/com/github/pagehelper/Page.java#L573-L609","documentation":"Page.setCountColumn validates the column used in the generated count query. Only \"0\", \"*\", or a value passing SqlSafeUtil's injection check are allowed; anything else throws a PageException. This protects the count(...) SQL, which cannot use bound parameters for the column list.","triggerScenarios":"Calling Page.setCountColumn(String) (or the countColumn(...) helper / PageHelper.skip-style API that sets it) with a value like 'id) FROM users--' or any string containing quotes/comments that SqlSafeUtil rejects.","commonSituations":"Passing a user-supplied count column from a request parameter, or configuring countColumn in properties with a typo or expression such as 'distinct id' that the safe check flags.","solutions":["Use \"*\" (default) or \"0\" or a plain verified column name like \"id\".","Sanitize/whitelist the column name before calling setCountColumn.","If a distinct/expression count is required, write a custom count method in the mapper (count method id via countSuffix) instead of injecting an expression.","Update config/property value to a simple column identifier."],"exampleFix":"// before\npage.setCountColumn(request.getParameter(\"col\"));\n// after\nString col = request.getParameter(\"col\");\nif (col == null || !col.matches(\"[a-zA-Z0-9_]+\")) {\n    page.setCountColumn(\"*\");\n} else {\n    page.setCountColumn(col);\n}","handlingStrategy":"validation","validationCode":"static void assertSafeCountColumn(String col) {\n    if (!(\"*\".equals(col) || \"0\".equals(col) || col.matches(\"[a-zA-Z0-9_]+\"))) {\n        throw new IllegalArgumentException(\"unsafe countColumn: \" + col);\n    }\n}","typeGuard":"static boolean isSafeCountColumn(String s) {\n    return \"*\".equals(s) || \"0\".equals(s) || (s != null && s.matches(\"[a-zA-Z0-9_]+\"));\n}","tryCatchPattern":"try {\n    page.setCountColumn(column);\n} catch (PageException e) {\n    log.warn(\"Rejected countColumn: {}\", column, e);\n    page.setCountColumn(\"*\");\n}","preventionTips":["Default to \"*\" unless profiling demands a specific count column.","Whitelist column names instead of accepting user input verbatim.","For DISTINCT/expression counts, use a dedicated mapper count method instead of countColumn.","Keep countColumn values in server-side config, not client payloads."],"tags":["sql-injection","security-validation","count-column","java"],"backgroundTag":"sql-injection-risk","analyzedSha":"c692616c5bc95b41aa779b502f991825c8e5acbc","analyzedAt":"2026-09-08T03:50:49.192Z","contentChangedAt":"2026-09-08T03:50:49.192Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}