> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Base64 与编码
- URL: https://blog.vercanti.com/base64-yu-bian-ma/
- Published: 2026-08-28T14:35:03.000Z
- Updated: 2026-08-28T14:57:55.000Z
- Description: Base64 是编码方案而非加密，任何人都可以解码。但在逆向中，Base64 几乎出现在每一个加密场景中（作为加密结果的传输格式），因此必须熟练掌握其各种变体。 使用字符集：A-Z a-z 0-9 + /，末尾用 = 填充至 4 的倍数。 遇到不可打印字符先判断编码：抓包响应体乱码时，优先尝试 Base64 解码；若结果仍乱码，再尝试 Hex 解码、zlib/gzip 解压，排除组合编码（如先 AES 再 Base64）。 URL-safe Base64 是最常见变体：搜索 + 被替换为 -、/ 被替换为 _ 的 Base64 字符串；Python 解码
- Author: yellowdog
- Tags: js逆向, 加密算法

> 官方文档：<https://www.rfc-editor.org/rfc/rfc4648>  
> 适用场景：JS 逆向中识别和处理 Base64 及各种编码变体

Base64 是编码方案而非加密，任何人都可以解码。但在逆向中，Base64 几乎出现在每一个加密场景中（作为加密结果的传输格式），因此必须熟练掌握其各种变体。

---

## 标准 Base64

使用字符集：`A-Z a-z 0-9 + /`，末尾用 `=` 填充至 4 的倍数。

---

## Python 实现

```python
import base64

# ── 标准 Base64 ────────────────────────────────────────────────

# 编码
def b64_encode(data: str) -> str:
    return base64.b64encode(data.encode('utf-8')).decode()

# 解码
def b64_decode(data: str) -> str:
    return base64.b64decode(data).decode('utf-8')

# 对字节数据编解码
def b64_encode_bytes(data: bytes) -> str:
    return base64.b64encode(data).decode()

def b64_decode_to_bytes(data: str) -> bytes:
    return base64.b64decode(data)

# ── URL 安全 Base64（+ → -，/ → _，去掉 =）──────────────────

def b64url_encode(data: str) -> str:
    return base64.urlsafe_b64encode(data.encode()).decode().rstrip('=')

def b64url_decode(data: str) -> str:
    # 补齐 = 号
    pad = 4 - len(data) % 4
    if pad != 4:
        data += '=' * pad
    return base64.urlsafe_b64decode(data).decode('utf-8')

# ── 自定义字母表 Base64 ──────────────────────────────────────

def custom_b64_encode(data: str, alphabet: str) -> str:
    """使用自定义字母表编码"""
    standard = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    encoded  = base64.b64encode(data.encode()).decode()
    table    = str.maketrans(standard, alphabet)
    return encoded.translate(table)

def custom_b64_decode(data: str, alphabet: str) -> str:
    standard = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    table    = str.maketrans(alphabet, standard)
    return base64.b64decode(data.translate(table)).decode()

# 示例：字母表被打乱的 Base64
my_alphabet = 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba9876543210+/'
enc = custom_b64_encode('hello world', my_alphabet)
dec = custom_b64_decode(enc, my_alphabet)

# ── 容错解码（处理非标准 Base64 字符串）────────────────────────

def safe_b64_decode(data: str) -> bytes:
    """处理缺少填充的 Base64 字符串"""
    data = data.replace('-', '+').replace('_', '/')
    pad  = 4 - len(data) % 4
    if pad != 4:
        data += '=' * pad
    return base64.b64decode(data)

```

---

## JavaScript 实现

### 浏览器原生

```javascript
// 编码
btoa('hello world')          // 'aGVsbG8gd29ybGQ='

// 解码
atob('aGVsbG8gd29ybGQ=')    // 'hello world'

// 处理中文（btoa 不支持 Unicode，需先转码）
function b64Encode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
        (_, p1) => String.fromCharCode(parseInt(p1, 16))
    ));
}

function b64Decode(str) {
    return decodeURIComponent(Array.from(atob(str),
        c => '%' + c.charCodeAt(0).toString(16).padStart(2, '0')
    ).join(''));
}

// URL 安全 Base64
function b64UrlEncode(str) {
    return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}

function b64UrlDecode(str) {
    str = str.replace(/-/g, '+').replace(/_/g, '/');
    while (str.length % 4) str += '=';
    return atob(str);
}

```

### Node.js

```javascript
// 编码
Buffer.from('hello world').toString('base64')         // 'aGVsbG8gd29ybGQ='

// 解码
Buffer.from('aGVsbG8gd29ybGQ=', 'base64').toString()  // 'hello world'

// URL 安全 Base64
Buffer.from('hello').toString('base64url')             // 无 = 号的 URL 安全版

// 字节数组转 Base64
Buffer.from([0x48, 0x65, 0x6c]).toString('base64')

// 自定义字母表（需手动替换）
function customB64Encode(data, alphabet) {
    const standard = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    let result = Buffer.from(data).toString('base64');
    return result.split('').map(c => {
        const idx = standard.indexOf(c);
        return idx >= 0 ? alphabet[idx] : c;
    }).join('');
}

```

### CryptoJS 中的 Base64

```javascript
const CryptoJS = require('crypto-js');

// WordArray 转 Base64
const wordArray = CryptoJS.enc.Utf8.parse('hello');
const b64 = wordArray.toString(CryptoJS.enc.Base64);

// Base64 转 WordArray
const wa = CryptoJS.enc.Base64.parse('aGVsbG8=');

// Hex 转 Base64
const hex    = '48656c6c6f';
const wa2    = CryptoJS.enc.Hex.parse(hex);
const b64str = wa2.toString(CryptoJS.enc.Base64);

```

---

## URL 编码

```python
from urllib.parse import quote, unquote, urlencode, parse_qs

# 编码（默认不编码 /）
quote('hello world & foo=bar')      # 'hello%20world%20%26%20foo%3Dbar'

# 编码所有特殊字符（包括 /）
quote('https://example.com/path', safe='')

# 解码
unquote('hello%20world')            # 'hello world'

# 将字典编码为 query string
urlencode({'a': 1, 'b': 'hello world'})  # 'a=1&b=hello+world'

# 解析 query string
parse_qs('a=1&b=hello+world')       # {'a': ['1'], 'b': ['hello world']}

```

```javascript
// 编码
encodeURIComponent('hello world & a=b')  // 'hello%20world%20%26%20a%3Db'
encodeURI('https://example.com/path?a=1')  // 只编码特殊字符，不编码 URL 结构字符

// 解码
decodeURIComponent('hello%20world')  // 'hello world'

// 构造 query string
new URLSearchParams({a: 1, b: 'hello world'}).toString()  // 'a=1&b=hello+world'

// 解析 query string
Object.fromEntries(new URLSearchParams('a=1&b=hello'))    // {a: '1', b: 'hello'}

```

---

## Hex 编解码

```python
# 字符串转 Hex
'hello'.encode().hex()         # '68656c6c6f'
bytes.fromhex('68656c6c6f').decode()  # 'hello'

# 整数转 Hex
hex(255)        # '0xff'
format(255, '02x')  # 'ff'
format(255, '08x')  # '000000ff'（补零到8位）

```

```javascript
// 字符串转 Hex
function strToHex(str) {
    return Array.from(Buffer.from(str, 'utf8'))
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');
}

// Hex 转字符串
function hexToStr(hex) {
    return Buffer.from(hex, 'hex').toString('utf8');
}

// CryptoJS
CryptoJS.enc.Utf8.parse('hello').toString(CryptoJS.enc.Hex);  // '68656c6c6f'
CryptoJS.enc.Hex.parse('68656c6c6f').toString(CryptoJS.enc.Utf8);  // 'hello'

```

---

## 各编码格式对照

| 原始数据        | Hex                    | Base64           | URL 编码        |
| ----------- | ---------------------- | ---------------- | ------------- |
| hello       | 68656c6c6f             | aGVsbG8=         | hello         |
| hello world | 68656c6c6f20776f726c64 | aGVsbG8gd29ybGQ= | hello%20world |
| a&b=c       | —                      | —                | a%26b%3Dc     |

---

## 最佳实践

**遇到不可打印字符先判断编码**：抓包响应体乱码时，优先尝试 Base64 解码；若结果仍乱码，再尝试 Hex 解码、zlib/gzip 解压，排除组合编码（如先 AES 再 Base64）。

**URL-safe Base64 是最常见变体**：搜索 `+` 被替换为 `-`、`/` 被替换为 `_` 的 Base64 字符串；Python 解码时用 `base64.urlsafe_b64decode(s + '==')` 处理（补齐 padding）。

**自定义 Base64 字母表是加密的一种**：若标准 Base64 解码结果不对，检查代码中是否有自定义字母表（长度 64 的字符串数组），用标准字母表逐字符对应翻译后再解码。

**Hex 编码搜索 `toString(16)` 或 `join('')`**：JS 常用 `Array.from(bytes).map(b => b.toString(16).padStart(2,'0')).join('')` 将字节转 Hex，搜索这段模式可定位编码逻辑。

**URL 编码注意二次编码**：参数经过两次 `encodeURIComponent` 时，`%` 本身也被编码为 `%25`，导致 `%2F` 变成 `%252F`；解码时需调用两次 `decodeURIComponent`。

---

## 常见陷阱

### 陷阱：标准 Base64 解码缺 padding 报错

**现象：** Python `base64.b64decode(s)` 抛出 `binascii.Error: Incorrect padding`。  
**原因：** JS 的 `btoa()` 和部分 Base64 库会省略末尾 `=` 填充字符。  
**解决：** `base64.b64decode(s + '==')` — 多余的 `=` 不影响解码，缺少的 `=` 才会报错。

### 陷阱：URL 编码大小写不一致

**现象：** Python `urllib.parse.quote()` 输出 `%2F`，而服务端期望 `%2f`（小写）。  
**原因：** RFC 3986 规定 Hex 字符不区分大小写，但部分服务端做了严格匹配；JS `encodeURIComponent` 输出大写，Python `quote` 也输出大写，但某些老接口用小写。  
**解决：** 根据抓包结果选择格式；Python 可用 `.lower()` 将 `%XX` 转小写：`re.sub(r'%[0-9A-F]{2}', lambda m: m.group().lower(), encoded)`。

### 陷阱：字符串 Base64 vs 字节 Base64 输入不同

**现象：** 同一段数据，Base64 后结果不同。  
**原因：** 将字符串直接 Base64（`btoa('hello')`）等价于对 Latin-1 字节编码；若字符串含 Unicode，`btoa` 报错或结果异常，正确做法是先 `encodeURIComponent` 或 `TextEncoder` 转字节再 Base64。  
**解决：** 确认 JS 侧的 Base64 输入是字符串还是 `Uint8Array`；Python 侧对应 `s.encode('utf-8')` 或 `s.encode('latin-1')`。

---

## 参见

[MD5](https://blog.vercanti.com/md5/)  
[SHA](https://blog.vercanti.com/sha-xi-lie/)  
[代码片段大全](https://blog.vercanti.com/js-ni-xiang-hook-yu-dai-ma-pian-duan-da-quan/)  
[加密算法识别](https://blog.vercanti.com/jia-mi-suan-fa-shi-bie/)