Grok API 密钥与环境变量
官方 Quickstart 的 Python 示例里,api_key 那一行原本写着 <YOUR_XAI_API_KEY_HERE>。它是占位符,语义很明确:这里该换成你的值。而几乎每个人都要在同一行做同一个动作——替换。替换完忘了还原,一次提交,密钥就进了仓库历史。
密钥泄露多数不是被攻破,而是自己交出去的。这一节只做一件事:让密钥在「开发 → 提交 → 运行 → 排障」四个环节里都不落到代码和日志里。
一、密钥离开机器的三条路
| 路径 | 典型现场 | 后果 |
|---|---|---|
| 源码 | 直接把字符串写进 api_key= |
进 git 历史,删掉也留痕 |
.env 入库 |
.env 没进忽略清单 |
多一份明文,还带全部环境值 |
| 日志 | 打印了完整认证头 | 进日志系统、工单与截图 |
三条里最常见的是第一条,最难收拾的也是第一条:git 历史里的字符串不会因为一次删除而消失,它还在每一个克隆过仓库的人手里。
所以规则要简单到不可能记错:仓库里只允许出现 YOUR_XAI_API_KEY 这一个占位符,真值只存在于运行环境。
二、提交前扫一遍
下面这段扫描器只读文本,不联网,命中后只输出遮罩片段——扫描器自己不能变成第二个泄漏点。
"""提交前密钥扫描:只读文本,不联网,命中后只输出遮罩片段。"""
import re
PATTERNS = [
("源码里疑似硬编码密钥", re.compile(r"""api_key\s*=\s*["'][^"']{12,}["']""")),
("疑似密钥字面量", re.compile(r"\bxai-[A-Za-z0-9]{12,}\b")),
("日志里打印认证头", re.compile(r"print\([^)]*(?:Authorization|api_key)[^)]*\)")),
]
## 演练样本:故意混入正确写法,用来看扫描器会不会误报
SAMPLES = [
("app.py", 'client = OpenAI(api_key="YOUR_XAI_API_KEY")'),
("app.py", 'client = OpenAI(api_key=os.environ["XAI_API_KEY"])'),
("debug.py", 'print("Authorization:", headers)'),
("notes.md", "xai-demo0000abcd"),
]
def mask(text: str) -> str:
"""只显示首个片段与长度,避免命中内容二次外流。"""
head = text[:8]
return head + "…(共 " + str(len(text)) + " 字符)"
def scan(name: str, text: str) -> list:
hits = []
for label, pattern in PATTERNS:
for match in pattern.finditer(text):
token = match.group(0)
# 占位符是唯一允许进仓库的形态;不放行它,第一次跑就全是噪音
if "YOUR_XAI_API_KEY" in token:
continue
hits.append(name + ":" + label + " → " + mask(token))
return hits
if __name__ == "__main__":
for name, text in SAMPLES:
for line in scan(name, text):
print(line)
YOUR_XAI_API_KEY 的放行逻辑不是偷懒,是必需:如果占位符也被判为违规,第一次运行就会给出四条噪音,团队会在第二周把这个脚本从流水线里摘掉。
三、密钥三种合法位置与禁区
密钥的三种合法位置,各有各的禁区:
| 存放位置 | 适合场景 | 不能做的事 |
|---|---|---|
| 进程环境变量 | 本地运行、CI | 写进镜像的构建层或启动脚本明文 |
.env 文件 |
本地开发 | 提交进仓库、打进镜像 |
| 密钥管理服务 | 多环境与生产 | 在代码里留一份明文兜底 |
三点需要说清:
.env与.env.example必须成对存在。前者进忽略清单、只在自己机器上;后者入库、只放占位符。缺了后者,新同事只能来问你,而「来问你」的下场往往是有人把真值发到了群里。- 轮换的前提是能轮换。密钥至少要能一环境一钥,否则换一次等于全环境停机,于是没人敢换。这条直接决定了 10.3 里那份交接清单能不能落地。
- 密钥不是唯一敏感物。提示里可能带客户文档,日志里可能带完整请求体。官方安全 FAQ 说明了 API 输入输出的存储与训练口径,数据侧怎么处置是 10.3 的内容,本节只守住「密钥不进代码」这一层。
四、预期输出与异常分支
运行 __main__,应该只命中两条——两条正确写法必须静默通过:
debug.py:日志里打印认证头 → print("A…(共 32 字符)
notes.md:疑似密钥字面量 → xai-demo…(共 16 字符)
常见失败分支往往不在扫描器,而在「扫描器没被跑起来」:
| 现象 | 原因 | 处理 |
|---|---|---|
| 启动即 401 | 变量名拼错,或 .env 没被加载 |
打印变量名与长度,不打印值 |
| 轮换后旧环境仍可用 | 多环境共用一个密钥 | 一环境一钥,逐个轮换 |
| 扫描器一路全绿 | 只扫了源码,没扫文档与脚本 | 扫描范围覆盖整个工作区 |
| 扫描器第二周就没人跑 | 误报太多 | 先放行占位符,再谈覆盖率 |
五、判据卡
| 判断项 | 该这么做 | 不该这么做 |
|---|---|---|
| 代码里的密钥 | 只留 YOUR_XAI_API_KEY 占位符 |
写真实值再「稍后删」 |
.env 管理 |
进忽略清单,配 .env.example |
提交进仓库或打进镜像 |
| 排障打印 | 打变量名与长度 | 打完整认证头 |
| 轮换设计 | 一环境一钥,可独立轮换 | 全环境共用一个密钥 |
| 扫描时机 | 提交前与 CI 双跑 | 靠记性定期手动执行 |
这张卡约束的是「密钥的物理位置」,不是某个框架的配置方式。换成 Node、换成容器、换成 Serverless,四条都还成立;唯一要重新设计的是「环境变量从哪注入」这一段。
六、来源与核验
- 官方 Quickstart:
https://docs.x.ai/developers/quickstart(核验日期 2026-11-04)。XAI_API_KEY这个变量名、export与.env两种写法,以及示例里的占位符形态,均来自该页 Step 2。 - 官方安全 FAQ:
https://docs.x.ai/developers/faq/security(核验日期 2026-11-04,页面本次实测可达)。其中说明默认不会在未经明确许可的情况下用 API 输入输出训练模型,请求与响应默认加密存储一段时间用于滥用审计;数据侧的具体处置请以该页当前内容为准,本教程不替官方下结论。 - 密钥轮换与日志脱敏的完整清单见 10.3;本节不涉及区域、网络与额度问题,这些以官方页与开发者控制台当前显示为准。
七、动手试
- 把
SAMPLES换成你项目里真实的两三个文件内容(脱敏后再贴),看扫描器会不会误报。误报率高于三成,说明规则要改,而不是团队要更小心。 - 给扫描器加一条规则:检查项目根目录是否存在
.env.example,且其中不包含任何真实值。想一想,这条规则该报「错误」还是「提醒」? - 如果团队现在只有一个密钥、五个环境共用,你会怎么排轮换的迁移顺序?写三步,要求第一步不导致停机。