前置知识: 网络安全

OAuth2/OIDC 配置命令

3 min中级

OAuth 2.0/OIDC 实战命令:发现文档与端点探测、授权码+PKCE 流程、Token 校验与自省、安全检测与 Keycloak 管理。

概念速览(阅读命令前先看)

  • OAuth 2.0 管「授权」:让客户端应用拿到 access_token 去访问资源,而不是拿到用户密码。
  • OIDC 管「认证」:在 OAuth 2.0 上加 scope=openid,多发一张 ID Token(JWT) 证明用户身份;取用户详情走 UserInfo 端点,不要拿 access_token 当身份凭证。
  • OAuth 2.1 的口径(当前实现的推荐基线):授权码流程必须配合 PKCE; 隐式流程(response_type=token)与密码模式(grant_type=password)已废弃, 下文相关命令仅用于检测目标是否仍在暴露这些不安全流程。
  • 四个角色:资源所有者(用户)、客户端(应用)、授权服务器(发 Token)、资源服务器(验 Token)。
  • 协议原理与 SAML/OIDC 对比见 032-IdentityAccessManagement。

OAuth2 端点探测

基本写法:获取授权服务器元数据 curl -s <URL>/.well-known/oauth-authorization-server

# 获取 OAuth2 授权服务器配置信息
curl -s https://example.com/.well-known/oauth-authorization-server

基本写法:获取 OIDC 发现文档 curl -s <URL>/.well-known/openid-configuration

# 获取 OIDC 配置发现文档
curl -s https://example.com/.well-known/openid-configuration

基本写法:获取 JWKS 公钥集 curl -s <URL>/.well-known/jwks.json

# 获取签名 Token 的公钥集合
curl -s https://example.com/.well-known/jwks.json

基本写法:测试授权端点 curl -s -I "<URL>/authorize?response_type=code&client_id=<ID>&redirect_uri=<回调>"

# 测试授权端点是否可用
curl -s -I "https://example.com/oauth/authorize?response_type=code&client_id=client123&redirect_uri=https://app.com/callback"

基本写法:测试 Token 端点 curl -s -X POST <URL>/token -d "grant_type=client_credentials"

# 测试 Token 端点响应
curl -s -X POST https://example.com/oauth/token -d "grant_type=client_credentials&client_id=app&client_secret=secret"

授权码流程测试

基本写法:构造授权请求 <URL>/authorize?response_type=code&client_id=<ID>&redirect_uri=<回调>&scope=<范围>&state=<状态>

# 构造标准授权码请求
echo "https://example.com/oauth/authorize?response_type=code&client_id=app123&redirect_uri=https://app.com/callback&scope=openid+profile&state=$(openssl rand -hex 8)"

基本写法:使用 PKCE 构造请求 <URL>/authorize?response_type=code&client_id=<ID>&code_challenge=<挑战值>&code_challenge_method=S256

# 构造带 PKCE 的授权请求
VERIFIER=$(openssl rand -base64 32 | tr -d '+/=' | head -c 43)
CHALLENGE=$(echo -n "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 | tr -d '+/=' | head -c 43)
echo "https://example.com/oauth/authorize?response_type=code&client_id=app123&code_challenge=$CHALLENGE&code_challenge_method=S256"

基本写法:用授权码换 Token curl -X POST <URL>/token -d "grant_type=authorization_code&code=<授权码>&redirect_uri=<回调>&client_id=<ID>"

# 使用授权码交换访问 Token
curl -X POST https://example.com/oauth/token -d "grant_type=authorization_code&code=abc123&redirect_uri=https://app.com/callback&client_id=app123&client_secret=secret"

基本写法:PKCE 换 Token curl -X POST <URL>/token -d "grant_type=authorization_code&code=<授权码>&code_verifier=<校验值>"

# PKCE 流程交换 Token
curl -X POST https://example.com/oauth/token -d "grant_type=authorization_code&code=abc123&code_verifier=verifier_value&client_id=app123"

客户端凭据流程

基本写法:请求客户端凭据 Token curl -X POST <URL>/token -d "grant_type=client_credentials&scope=<范围>"

# 服务间调用获取 Token
curl -X POST https://example.com/oauth/token -u "client_id:client_secret" -d "grant_type=client_credentials&scope=read"

基本写法:使用 Basic 认证 curl -X POST <URL>/token -u "<ID>:<密钥>" -d "grant_type=client_credentials"

# 使用 Basic Auth 方式传递客户端凭据
curl -X POST https://example.com/oauth/token -u "app123:secret" -d "grant_type=client_credentials"

基本写法:刷新 Token curl -X POST <URL>/token -d "grant_type=refresh_token&refresh_token=<刷新令牌>"

# 使用刷新令牌获取新的访问 Token
curl -X POST https://example.com/oauth/token -d "grant_type=refresh_token&refresh_token=refresh_value&client_id=app123&client_secret=secret"

基本写法:密码凭据流程(已废弃,仅用于检测存量暴露) curl -X POST <URL>/token -d "grant_type=password&username=<用户>&password=<密码>"

# 资源所有者密码流程:OAuth 2.1 已移除,正规系统应拒绝该 grant_type
# 若目标仍接受,说明存在「应用接触明文密码」的过时设计,应迁移授权码+PKCE
curl -X POST https://example.com/oauth/token -d "grant_type=password&username=admin&password=pass&client_id=app123"

Token 校验与自省

基本写法:调用自省端点 curl -X POST <URL>/introspect -d "token=<Token>"

# 使用 Token 自省端点验证 Token 状态
curl -X POST https://example.com/oauth/introspect -u "app123:secret" -d "token=access_token_value"

基本写法:本地校验 JWT python3 -c "import jwt; print(jwt.decode('<Token>', '<密钥>', algorithms=['HS256'], audience='<受众>'))"

# 本地校验 JWT 签名与声明
python3 -c "import jwt; print(jwt.decode('eyJ...', 'secret', algorithms=['RS256'], audience='api.example.com', options={'verify_aud': True}))"

基本写法:使用 JWKS 校验 python3 -c "import jwt, requests; jwks=requests.get('<JWKS_URL>').json(); print(jwks)"

# 获取 JWKS 并校验 RS256 Token
python3 -c "import jwt, requests; jwks=requests.get('https://example.com/.well-known/jwks.json').json(); print(jwt.decode('eyJ...', key=jwks, algorithms=['RS256']))"

基本写法:UserInfo 端点调用 curl -H "Authorization: Bearer <Token>" <URL>/userinfo

# 调用 OIDC UserInfo 端点获取用户信息
curl -H "Authorization: Bearer access_token_value" https://example.com/userinfo

OAuth2 安全检测

基本写法:检测 redirect_uri 校验 curl -I "<URL>/authorize?client_id=<ID>&redirect_uri=https://evil.com&response_type=code"

# 测试是否校验 redirect_uri 防止开放重定向
curl -I "https://example.com/oauth/authorize?client_id=app123&redirect_uri=https://evil.com&response_type=code"

基本写法:检测 state 参数缺失 curl -I "<URL>/authorize?client_id=<ID>&response_type=code&redirect_uri=<回调>"

# 测试是否强制要求 state 参数防 CSRF
curl -I "https://example.com/oauth/authorize?client_id=app123&response_type=code&redirect_uri=https://app.com/callback"

基本写法:测试 scope 越权 curl -X POST <URL>/token -d "grant_type=client_credentials&scope=admin superuser"

# 测试能否请求超出授权范围的 scope
curl -X POST https://example.com/oauth/token -u "app123:secret" -d "grant_type=client_credentials&scope=admin superuser"

基本写法:检测隐式流程是否启用 curl -I "<URL>/authorize?response_type=token&client_id=<ID>"

# 检测是否支持不安全的隐式流程
curl -I "https://example.com/oauth/authorize?response_type=token&client_id=app123"

Keycloak 命令行操作

基本写法:获取管理员 Token curl -X POST <URL>/realms/master/protocol/openid-connect/token -d "grant_type=password&username=admin&password=<密码>"

# 获取 Keycloak 管理员 Token
curl -X POST https://kc.example.com/realms/master/protocol/openid-connect/token -d "grant_type=password&username=admin&password=admin&client_id=admin-cli"

基本写法:列出所有 Realm curl -H "Authorization: Bearer <Token>" <URL>/admin/realms

# 列出 Keycloak 中所有 Realm
curl -H "Authorization: Bearer admin_token" https://kc.example.com/admin/realms

基本写法:创建 Realm curl -X POST -H "Authorization: Bearer <Token>" -H "Content-Type: application/json" <URL>/admin/realms -d '<JSON>'

# 创建新的 Realm
curl -X POST -H "Authorization: Bearer admin_token" -H "Content-Type: application/json" https://kc.example.com/admin/realms -d '{"realm":"myrealm","enabled":true}'

基本写法:创建客户端 curl -X POST -H "Authorization: Bearer <Token>" <URL>/admin/realms/<Realm>/clients -d '<JSON>'

# 在指定 Realm 中创建客户端
curl -X POST -H "Authorization: Bearer admin_token" -H "Content-Type: application/json" https://kc.example.com/admin/realms/myrealm/clients -d '{"clientId":"app123","enabled":true}'

OAuth2 服务端配置

基本写法:nginx 模板反向代理 OAuth2 proxy_pass <后端URL>;

# 反向代理 OAuth2 后端服务
location /oauth {
    proxy_pass http://127.0.0.1:8080/oauth;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
}

基本写法:Apache oauth2-proxy ProxyPass /oauth2/ http://127.0.0.1:4180/oauth2/

# Apache 反向代理 oauth2-proxy
ProxyPass /oauth2/ http://127.0.0.1:4180/oauth2/
ProxyPassReverse /oauth2/ http://127.0.0.1:4180/oauth2/

基本写法:oauth2-proxy 启动 oauth2-proxy --http-address="0.0.0.0:4180" --upstream="<后端>" --client-id="<ID>" --client-secret="<密钥>" --email-domain="<域名>"

# 启动 oauth2-proxy 服务
oauth2-proxy --http-address="0.0.0.0:4180" --upstream="http://127.0.0.1:8080/" --client-id="app123" --client-secret="secret" --cookie-secret=$(openssl rand -base64 32) --email-domain="example.com"

基本写法:配置 cookie 安全属性 --cookie-secure --cookie-httponly --cookie-samesite=lax

# oauth2-proxy 配置安全 Cookie
oauth2-proxy --cookie-secure --cookie-httponly --cookie-samesite=lax --cookie-name="_oauth2_proxy"

OAuth2 日志审计

基本写法:检索 OAuth 请求日志 grep -iE "/oauth/|/authorize|/token" <日志>

# 检索所有 OAuth2 相关请求
grep -iE "/oauth/|/authorize|/token|/introspect" /var/log/nginx/access.log

基本写法:统计 Token 端点调用 grep "/token" <日志> | awk '{print $1}' | sort | uniq -c | sort -rn

# 统计 Token 端点调用来源 IP
grep "/oauth/token" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head

基本写法:检测 redirect_uri 攻击 grep -i "redirect_uri" <日志> | grep -i "evil\|attacker"

# 检测可疑的 redirect_uri 重定向
grep -i "redirect_uri" /var/log/nginx/access.log | grep -iE "evil|attacker|hack"

基本写法:监控异常 scope 请求 grep "scope" <日志> | grep -iE "admin|root|superuser"

# 监控异常权限提升请求
grep "scope" /var/log/nginx/access.log | grep -iE "admin|root|superuser"

OAuth2 安全自检

基本写法:检查 PKCE 是否强制 curl -I "<URL>/authorize?response_type=code&client_id=<ID>"

# 不带 PKCE 的请求测试是否被拒绝
curl -I "https://example.com/oauth/authorize?response_type=code&client_id=app123&redirect_uri=https://app.com/callback"

基本写法:检查 HTTPS 强制 curl -I http://<URL>/.well-known/openid-configuration

# 测试 HTTP 是否被重定向到 HTTPS
curl -I http://example.com/.well-known/openid-configuration

基本写法:验证 Token 过期时间 python3 -c "import jwt; print(jwt.decode('<Token>', options={'verify_signature': False})['exp'])"

# 检查 Token 过期时间是否合理
python3 -c "import jwt; print(jwt.decode('eyJ...', options={'verify_signature': False}))"

基本写法:批量检查客户端配置 curl -H "Authorization: Bearer <Token>" <URL>/admin/realms/<Realm>/clients

# 列出所有客户端配置检查安全性
curl -H "Authorization: Bearer admin_token" https://kc.example.com/admin/realms/myrealm/clients | python3 -m json.tool