url安全的base64编码

Jun 5, 2017 • kael


前言

在OpenID Connect协议中,如果登录的时候指定了response_type=id_token的话, 在重定向回到第三方应用时,会将id_token的值作为queryString参数放在url中,这里 就涉及一个问题:

如何避免id_token和url的特殊字符冲突?

URL编码规范

关于URL编码的详细规范,可以查看RFC 3986, 这里只对我们会涉及的信息做简要说明。

RFC 3986中有如下两个重要规定:

  • Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、- _ . ~4个特殊字符以及所有保留字符。
  • 保留字符包括:! * ' ( ) ; : @ & = + $ , / ? # [ ]

另外,还有很多字符,当它们直接放在Url中的时候,可能会引起解析程序的歧义。

这些字符被视为不安全字符,原因有很多。

  • 空格:Url在传输的过程,或者用户在排版的过程,或者文本处理程序在处理Url的过程,都有可能引入无关紧要的空格,或者将那些有意义的空格给去掉;
  • 引号以及<>:引号和尖括号通常用于在普通文本中起到分隔Url的作用;
  • #:通常用于表示书签或者锚点;
  • %:百分号本身用作对不安全字符进行编码时使用的特殊字符,因此本身需要编码;
  • { } | \ ^ [ ] ` ~:某一些网关或者传输代理会篡改这些字符。

url安全的base64编码

在实际的应用中,id_token是一个jwt字符串,字符串的载体是一个base64编码的json字符串,这就可能出现不符合url编码规范的结果, 因为base64编码会出现+/=这些被url转码的特殊字符,一旦这个结果被转码了,第三方应用获取到的base64字符串和服务端 签名生成的就不一致了,因此对id_token的编码必须是url安全的base64编码。

url安全的base64编码规范一般采用如下方式:

  • 将base64中的+号换成-
  • 将base64中的/号换成_
  • 将base64中的=号换成空字符串,即删除=

对应的,url安全的base64解码过程要反过来:

  • 将base64中的-号换成+
  • 将base64中的_号换成/
  • 在base64中末位补齐=号,使得base64的字符数量可以被4整除

java编码示例代码如下:

public static String safeUrlBase64Encode(byte[] data){
		String encodeBase64 = new BASE64Encoder().encode(data);
		String safeBase64Str = encodeBase64.replace('+', '-');
		safeBase64Str = safeBase64Str.replace('/', '_');
		safeBase64Str = safeBase64Str.replaceAll("=", "");
		return safeBase64Str;
}

java解码示例代码如下:

public static byte[] safeUrlBase64Decode(final String safeBase64Str){
		String base64Str = safeBase64Str.replace('-', '+');
		base64Str = base64Str.replace('_', '/');
		int mod4 = base64Str.length()%4;
		if(mod4 > 0){
			base64Str = base64Str + "====".substring(mod4);
		}
		return new BASE64Decoder().decodeBuffer(base64Str);
}