{"record":{"id":"cb753ad56fc9b0a4","repo":"dromara/Sa-Token","slug":"12003","errorCode":"12003","errorMessage":"无效Value：\" + value","messagePattern":"无效Value：\" \\+ value","errorType":"validation","errorClass":"SaTokenException","httpStatus":null,"severity":"error","filePath":"sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaCookie.java","lineNumber":325,"sourceCode":"\t */\n\tpublic void builder() {\n\t\tif(path == null) {\n\t\t\tpath = \"/\";\n\t\t}\n\t}\n\n\t/**\n\t * 转换为响应头 Set-Cookie 参数需要的值\n\t * @return /\n\t */\n\tpublic String toHeaderValue() {\n\t\tthis.builder();\n\n\t\tif(SaFoxUtil.isEmpty(name)) {\n\t\t\tthrow new SaTokenException(\"name不能为空\").setCode(SaErrorCode.CODE_12002);\n\t\t}\n\t\tif(value != null && value.contains(\";\")) {\n\t\t\tthrow new SaTokenException(\"无效Value：\" + value).setCode(SaErrorCode.CODE_12003);\n\t\t}\n\n\t\t// example：\n\t\t// Set-Cookie: name=value; Max-Age=100000; Expires=Tue, 05-Oct-2021 20:28:17 GMT; Domain=localhost; Path=/; Secure; HttpOnly; SameSite=Lax\n\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(name).append(\"=\").append(value);\n\n\t\tif(maxAge >= 0) {\n\t\t\t sb.append(\"; Max-Age=\").append(maxAge);\n\t\t\t String expires;\n\t\t\t if(maxAge == 0) {\n\t\t\t\t expires = Instant.EPOCH.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.RFC_1123_DATE_TIME);\n\t\t\t } else {\n\t\t\t\t expires = OffsetDateTime.now().plusSeconds(maxAge).format(DateTimeFormatter.RFC_1123_DATE_TIME);\n\t\t\t }\n\t\t\t sb.append(\"; Expires=\").append(expires);\n\t\t}","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/dromara/Sa-Token/blob/ac2c7f6e94a78573cf0bcb932dd8b04e68fad189/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaCookie.java#L307-L343","documentation":"SaCookie.toHeaderValue() rejects any cookie value containing ';' (code 12003). ';' is the delimiter inside the Set-Cookie header, so embedding it would split/forge the cookie attributes; sa-token fails fast instead of emitting a broken header.","triggerScenarios":"SaCookie.setValue(\"a;b\") followed by toHeaderValue()/addCookie; building an auth or ticket cookie from user-controlled input that may contain ';', e.g. value = username + \";\" + role.","commonSituations":"Composing multi-part values with ';' as separator instead of URL-encoding; passing an unparsed Authorization header or JWT fragment (which can contain base64 ';'-adjacent data) into a cookie; copying values from another cookie verbatim.","solutions":["URL-encode the value before setting: SaFoxUtil.encodeUrl(value) or java.net.URLEncoder.encode(value, StandardCharsets.UTF_8)","Use a safe separator (e.g. '&' or '|') and still encode the whole value","Prefer storing opaque tokens (UUID/JWT without ';') in cookies and keeping structured data server-side"],"exampleFix":"// before\nSaCookie cookie = new SaCookie().setName(\"auth\").setValue(userInput); // userInput = \"a;b\"\n\n// after\nSaCookie cookie = new SaCookie().setName(\"auth\").setValue(SaFoxUtil.encodeUrl(userInput));","handlingStrategy":"validation","validationCode":"if (value != null && value.contains(\";\")) {\n    value = SaFoxUtil.encodeUrl(value); // or reject\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["URL-encode any cookie value derived from user input","Store opaque tokens in cookies, keep structure server-side","Add a unit test for cookie serialization of edge-case values"],"tags":["sa-token","cookie","header-injection","encoding","validation"],"backgroundTag":null,"analyzedSha":"ac2c7f6e94a78573cf0bcb932dd8b04e68fad189","analyzedAt":"2026-08-14T14:36:10.271Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}